mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Add client-token
header to spclient
requests
- Also fix an overflow panic when a token cannot be parsed. - Getting tokens always requires the keymaster client ID; passing the actual client ID yields a HashCash challenge.
This commit is contained in:
parent
ebfe8ca36c
commit
cdf84925ad
3 changed files with 12 additions and 6 deletions
|
@ -2,7 +2,7 @@ use std::{fmt, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
const KEYMASTER_CLIENT_ID: &str = "65b708073fc0480ea92a077233ca87bd";
|
pub const KEYMASTER_CLIENT_ID: &str = "65b708073fc0480ea92a077233ca87bd";
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SessionConfig {
|
pub struct SessionConfig {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use futures_util::future::IntoStream;
|
||||||
use http::header::HeaderValue;
|
use http::header::HeaderValue;
|
||||||
use hyper::{
|
use hyper::{
|
||||||
client::ResponseFuture,
|
client::ResponseFuture,
|
||||||
header::{ACCEPT, AUTHORIZATION, CONTENT_ENCODING, CONTENT_TYPE, RANGE},
|
header::{HeaderName, ACCEPT, AUTHORIZATION, CONTENT_ENCODING, CONTENT_TYPE, RANGE},
|
||||||
Body, HeaderMap, Method, Request,
|
Body, HeaderMap, Method, Request,
|
||||||
};
|
};
|
||||||
use protobuf::Message;
|
use protobuf::Message;
|
||||||
|
@ -19,6 +19,7 @@ use thiserror::Error;
|
||||||
use crate::{
|
use crate::{
|
||||||
apresolve::SocketAddress,
|
apresolve::SocketAddress,
|
||||||
cdn_url::CdnUrl,
|
cdn_url::CdnUrl,
|
||||||
|
config::KEYMASTER_CLIENT_ID,
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
protocol::{
|
protocol::{
|
||||||
canvaz::EntityCanvazRequest,
|
canvaz::EntityCanvazRequest,
|
||||||
|
@ -40,6 +41,9 @@ component! {
|
||||||
|
|
||||||
pub type SpClientResult = Result<Bytes, Error>;
|
pub type SpClientResult = Result<Bytes, Error>;
|
||||||
|
|
||||||
|
#[allow(clippy::declare_interior_mutable_const)]
|
||||||
|
const CLIENT_TOKEN: HeaderName = HeaderName::from_static("client-token");
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum SpClientError {
|
pub enum SpClientError {
|
||||||
#[error("missing attribute {0}")]
|
#[error("missing attribute {0}")]
|
||||||
|
@ -116,7 +120,7 @@ impl SpClient {
|
||||||
message.set_request_type(ClientTokenRequestType::REQUEST_CLIENT_DATA_REQUEST);
|
message.set_request_type(ClientTokenRequestType::REQUEST_CLIENT_DATA_REQUEST);
|
||||||
|
|
||||||
let client_data = message.mut_client_data();
|
let client_data = message.mut_client_data();
|
||||||
client_data.set_client_id(self.session().client_id());
|
client_data.set_client_id(KEYMASTER_CLIENT_ID.to_string());
|
||||||
client_data.set_client_version(version::SEMVER.to_string());
|
client_data.set_client_version(version::SEMVER.to_string());
|
||||||
|
|
||||||
let connectivity_data = client_data.mut_connectivity_sdk_data();
|
let connectivity_data = client_data.mut_connectivity_sdk_data();
|
||||||
|
@ -287,6 +291,7 @@ impl SpClient {
|
||||||
.token_provider()
|
.token_provider()
|
||||||
.get_token("playlist-read")
|
.get_token("playlist-read")
|
||||||
.await?;
|
.await?;
|
||||||
|
let client_token = self.client_token().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 {
|
||||||
|
@ -296,6 +301,7 @@ impl SpClient {
|
||||||
AUTHORIZATION,
|
AUTHORIZATION,
|
||||||
HeaderValue::from_str(&format!("{} {}", token.token_type, token.access_token,))?,
|
HeaderValue::from_str(&format!("{} {}", token.token_type, token.access_token,))?,
|
||||||
);
|
);
|
||||||
|
headers_mut.insert(CLIENT_TOKEN, HeaderValue::from_str(&client_token)?);
|
||||||
|
|
||||||
last_response = self.session().http_client().request_body(request).await;
|
last_response = self.session().http_client().request_body(request).await;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use std::time::{Duration, Instant};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::{config::KEYMASTER_CLIENT_ID, Error};
|
||||||
|
|
||||||
component! {
|
component! {
|
||||||
TokenProvider : TokenProviderInner {
|
TokenProvider : TokenProviderInner {
|
||||||
|
@ -65,7 +65,7 @@ impl TokenProvider {
|
||||||
|
|
||||||
// scopes must be comma-separated
|
// scopes must be comma-separated
|
||||||
pub async fn get_token(&self, scopes: &str) -> Result<Token, Error> {
|
pub async fn get_token(&self, scopes: &str) -> Result<Token, Error> {
|
||||||
let client_id = self.session().client_id();
|
let client_id = KEYMASTER_CLIENT_ID;
|
||||||
if client_id.is_empty() {
|
if client_id.is_empty() {
|
||||||
return Err(Error::invalid_argument("Client ID cannot be empty"));
|
return Err(Error::invalid_argument("Client ID cannot be empty"));
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ impl Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_expired(&self) -> bool {
|
pub fn is_expired(&self) -> bool {
|
||||||
self.timestamp + (self.expires_in - Self::EXPIRY_THRESHOLD) < Instant::now()
|
self.timestamp + (self.expires_in.saturating_sub(Self::EXPIRY_THRESHOLD)) < Instant::now()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn in_scope(&self, scope: &str) -> bool {
|
pub fn in_scope(&self, scope: &str) -> bool {
|
||||||
|
|
Loading…
Reference in a new issue