From 0d5cbedfd37e7ca8291326b4a00acfa44b7710d7 Mon Sep 17 00:00:00 2001 From: Nick Steel Date: Tue, 13 Aug 2024 13:47:37 +0100 Subject: [PATCH] oauth: username not always available in token. Use BasicClient --- oauth/src/lib.rs | 60 ++++++++++-------------------------------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/oauth/src/lib.rs b/oauth/src/lib.rs index aefcedf29..a8e294128 100644 --- a/oauth/src/lib.rs +++ b/oauth/src/lib.rs @@ -1,14 +1,9 @@ use log::{debug, error, info, trace}; -use oauth2::basic::{ - BasicErrorResponse, BasicRevocationErrorResponse, BasicTokenIntrospectionResponse, - BasicTokenType, -}; use oauth2::reqwest::http_client; use oauth2::{ - AuthUrl, AuthorizationCode, Client, ClientId, CsrfToken, ExtraTokenFields, PkceCodeChallenge, - RedirectUrl, Scope, StandardRevocableToken, StandardTokenResponse, TokenResponse, TokenUrl, + basic::BasicClient, AuthUrl, AuthorizationCode, ClientId, CsrfToken, PkceCodeChallenge, + RedirectUrl, Scope, TokenResponse, TokenUrl, }; -use serde::{Deserialize, Serialize}; use std::io; use std::{ io::{BufRead, BufReader, Write}, @@ -18,33 +13,6 @@ use std::{ }; use url::Url; -// Define extra fields to get the username too. -// TODO: Maybe don't bother and use simpler BasicClient instead? - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct SpotifyFields { - #[serde(rename = "username")] - #[serde(skip_serializing_if = "Option::is_none")] - pub username: Option, -} -impl SpotifyFields { - pub fn username(&self) -> Option<&String> { - self.username.as_ref() - } -} -impl ExtraTokenFields for SpotifyFields {} - -type SpotifyTokenResponse = StandardTokenResponse; - -type SpotifyClient = Client< - BasicErrorResponse, - SpotifyTokenResponse, - BasicTokenType, - BasicTokenIntrospectionResponse, - StandardRevocableToken, - BasicRevocationErrorResponse, ->; - fn get_authcode_stdin() -> AuthorizationCode { println!("Provide code"); let mut buffer = String::new(); @@ -90,29 +58,25 @@ fn get_authcode_listener(socket_address: SocketAddr) -> AuthorizationCode { code } -// TODO: Return a Result +// TODO: Return a Result? // TODO: Pass in redirect_address instead since the redirect host depends on client ID? -// TODO: Should also return username, for fun? pub fn get_access_token(client_id: &str, redirect_port: u16) -> String { // Must use host 127.0.0.1 with Spotify Desktop client ID. let redirect_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), redirect_port); let redirect_uri = format!("http://{redirect_address}/login"); - let client = SpotifyClient::new( + let client = BasicClient::new( ClientId::new(client_id.to_string()), None, - AuthUrl::new("https://accounts.spotify.com/authorize".to_string()) - .expect("Invalid authorization endpoint URL"), - Some( - TokenUrl::new("https://accounts.spotify.com/api/token".to_string()) - .expect("Invalid token endpoint URL"), - ), + AuthUrl::new("https://accounts.spotify.com/authorize".to_string()).unwrap(), + Some(TokenUrl::new("https://accounts.spotify.com/api/token".to_string()).unwrap()), ) .set_redirect_uri(RedirectUrl::new(redirect_uri).expect("Invalid redirect URL")); let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); // Generate the full authorization URL. + // Some of these scopes are unavailable for custom client IDs. Which? let scopes = vec![ "app-remote-control", "playlist-modify", @@ -169,15 +133,15 @@ pub fn get_access_token(client_id: &str, redirect_port: u16) -> String { }); let token_response = rx.recv().unwrap(); let token = match token_response { - Ok(tok) => tok, + Ok(tok) => { + trace!("Obtained new access token: {tok:?}"); + tok + } Err(e) => { error!("Failed to exchange code for access token: {e:?}"); exit(1); } }; - let username = token.extra_fields().username().unwrap().to_string(); - let access_token = token.access_token().secret().to_string(); - trace!("Obtained new access token for {username}: {token:?}"); - access_token + token.access_token().secret().to_string() }