Fix client-token and implement expiry logic

This commit is contained in:
Roderick van Domburg 2022-01-23 00:26:52 +01:00
parent 1528292583
commit 4ea1b77c7b
No known key found for this signature in database
GPG key ID: FE2585E713F9F30A
2 changed files with 57 additions and 23 deletions

View file

@ -1,4 +1,7 @@
use std::time::Duration; use std::{
convert::TryInto,
time::{Duration, Instant},
};
use bytes::Bytes; use bytes::Bytes;
use futures_util::future::IntoStream; use futures_util::future::IntoStream;
@ -22,6 +25,7 @@ use crate::{
connect::PutStateRequest, connect::PutStateRequest,
extended_metadata::BatchedEntityRequest, extended_metadata::BatchedEntityRequest,
}, },
token::Token,
version, Error, FileId, SpotifyId, version, Error, FileId, SpotifyId,
}; };
@ -29,7 +33,7 @@ component! {
SpClient : SpClientInner { SpClient : SpClientInner {
accesspoint: Option<SocketAddress> = None, accesspoint: Option<SocketAddress> = None,
strategy: RequestStrategy = RequestStrategy::default(), strategy: RequestStrategy = RequestStrategy::default(),
client_token: String = String::new(), client_token: Option<Token> = None,
} }
} }
@ -92,12 +96,21 @@ impl SpClient {
} }
pub async fn client_token(&self) -> Result<String, Error> { pub async fn client_token(&self) -> Result<String, Error> {
// TODO: implement expiry let client_token = self.lock(|inner| {
let client_token = self.lock(|inner| inner.client_token.clone()); if let Some(token) = &inner.client_token {
if !client_token.is_empty() { if token.is_expired() {
return Ok(client_token); inner.client_token = None;
}
}
inner.client_token.clone()
});
if let Some(client_token) = client_token {
return Ok(client_token.access_token);
} }
trace!("Client token unavailable or expired, requesting new token.");
let mut message = ClientTokenRequest::new(); let mut message = ClientTokenRequest::new();
message.set_request_type(ClientTokenRequestType::REQUEST_CLIENT_DATA_REQUEST); message.set_request_type(ClientTokenRequestType::REQUEST_CLIENT_DATA_REQUEST);
@ -118,7 +131,7 @@ impl SpClient {
windows_data.set_unknown_value_8(34404); windows_data.set_unknown_value_8(34404);
windows_data.set_unknown_value_10(true); windows_data.set_unknown_value_10(true);
let body = protobuf::text_format::print_to_string(&message); let body = message.write_to_bytes()?;
let request = Request::builder() let request = Request::builder()
.method(&Method::POST) .method(&Method::POST)
@ -128,10 +141,35 @@ impl SpClient {
.body(Body::from(body))?; .body(Body::from(body))?;
let response = self.session().http_client().request_body(request).await?; let response = self.session().http_client().request_body(request).await?;
let response = ClientTokenResponse::parse_from_bytes(&response)?; let message = ClientTokenResponse::parse_from_bytes(&response)?;
let client_token = response.get_granted_token().get_token().to_owned(); let client_token = self.lock(|inner| {
self.lock(|inner| inner.client_token = client_token.clone()); let access_token = message.get_granted_token().get_token().to_owned();
let client_token = Token {
access_token: access_token.clone(),
expires_in: Duration::from_secs(
message
.get_granted_token()
.get_refresh_after_seconds()
.try_into()
.unwrap_or(7200),
),
token_type: "client-token".to_string(),
scopes: message
.get_granted_token()
.get_domains()
.iter()
.map(|d| d.domain.clone())
.collect(),
timestamp: Instant::now(),
};
trace!("Got client token: {:?}", client_token);
inner.client_token = Some(client_token);
access_token
});
Ok(client_token) Ok(client_token)
} }
@ -180,9 +218,6 @@ impl SpClient {
let body = body.unwrap_or_else(String::new); let body = body.unwrap_or_else(String::new);
let client_token = self.client_token().await;
trace!("CLIENT TOKEN: {:?}", client_token);
loop { loop {
tries += 1; tries += 1;
@ -205,20 +240,19 @@ impl SpClient {
.body(Body::from(body.clone()))?; .body(Body::from(body.clone()))?;
// Reconnection logic: keep getting (cached) tokens because they might have expired. // Reconnection logic: keep getting (cached) tokens because they might have expired.
let token = self
.session()
.token_provider()
.get_token("playlist-read")
.await?;
let headers_mut = request.headers_mut(); let headers_mut = request.headers_mut();
if let Some(ref hdrs) = headers { if let Some(ref hdrs) = headers {
*headers_mut = hdrs.clone(); *headers_mut = hdrs.clone();
} }
headers_mut.insert( headers_mut.insert(
AUTHORIZATION, AUTHORIZATION,
HeaderValue::from_str(&format!( HeaderValue::from_str(&format!("{} {}", token.token_type, token.access_token,))?,
"Bearer {}",
self.session()
.token_provider()
.get_token("playlist-read")
.await?
.access_token
))?,
); );
last_response = self.session().http_client().request_body(request).await; last_response = self.session().http_client().request_body(request).await;

View file

@ -93,7 +93,7 @@ impl TokenProvider {
let request = self.session().mercury().get(query_uri)?; let request = self.session().mercury().get(query_uri)?;
let response = request.await?; let response = request.await?;
let data = response.payload.first().ok_or(TokenError::Empty)?.to_vec(); let data = response.payload.first().ok_or(TokenError::Empty)?.to_vec();
let token = Token::new(String::from_utf8(data)?)?; let token = Token::from_json(String::from_utf8(data)?)?;
trace!("Got token: {:#?}", token); trace!("Got token: {:#?}", token);
self.lock(|inner| inner.tokens.push(token.clone())); self.lock(|inner| inner.tokens.push(token.clone()));
Ok(token) Ok(token)
@ -103,7 +103,7 @@ impl TokenProvider {
impl Token { impl Token {
const EXPIRY_THRESHOLD: Duration = Duration::from_secs(10); const EXPIRY_THRESHOLD: Duration = Duration::from_secs(10);
pub fn new(body: String) -> Result<Self, Error> { pub fn from_json(body: String) -> Result<Self, Error> {
let data: TokenData = serde_json::from_slice(body.as_ref())?; let data: TokenData = serde_json::from_slice(body.as_ref())?;
Ok(Self { Ok(Self {
access_token: data.access_token, access_token: data.access_token,