//! Refresh tokens aren't meant to expire, so they can be used as a persistent //! authentication method without the need for user's interaction for oauth //! requests. You still need to authenticate the usual way at least once to //! obtain the refresh token, and you may need to obtain a new one if you change //! the required scopes. //! //! The cache generated by `get_token` uses the refresh token under the hood to //! automatically authenticate the user. This example shows how it's done //! because sometimes it's not possible to use this cache file (a web server for //! example). //! //! *Note*: refresh tokens can actually expire, [as the OAuth2 spec //! indicates](https://tools.ietf.org/html/rfc6749#section-6), but this [hasn't //! actually happened in months with some //! tokens](https://github.com/felix-hilden/tekore/issues/86), so in the case of //! Spotify it doesn't seem to revoke them at all. use rspotify::{ model::ArtistId, prelude::*, scopes, AuthCodePkceSpotify, AuthCodeSpotify, Credentials, OAuth, }; // Sample request that will follow some artists, print the user's // followed artists, and then unfollow the artists. async fn do_things(spotify: &AuthCodeSpotify) { let artists = [ ArtistId::from_id("3RGLhK1IP9jnYFH4BRFJBS").unwrap(), // The Clash ArtistId::from_id("0yNLKJebCb8Aueb54LYya3").unwrap(), // New Order ArtistId::from_id("2jzc5TC5TVFLXQlBNiIUzE").unwrap(), // a-ha ]; let num_artists = artists.len(); spotify .user_follow_artists(artists.iter().map(|a| a.as_ref())) .await .expect("couldn't follow artists"); println!("Followed {num_artists} artists successfully."); // Printing the followed artists let followed = spotify .current_user_followed_artists(None, None) .await .expect("couldn't get user followed artists"); println!( "User currently follows at least {} artists.", followed.items.len() ); spotify .user_unfollow_artists(artists) .await .expect("couldn't unfollow artists"); println!("Unfollowed {num_artists} artists successfully."); } // Sample request that will follow some artists, print the user's // followed artists, and then unfollow the artists. async fn pkce_do_things(spotify: &AuthCodePkceSpotify) { let artists = [ ArtistId::from_id("3RGLhK1IP9jnYFH4BRFJBS").unwrap(), // The Clash ArtistId::from_id("0yNLKJebCb8Aueb54LYya3").unwrap(), // New Order ArtistId::from_id("2jzc5TC5TVFLXQlBNiIUzE").unwrap(), // a-ha ]; let num_artists = artists.len(); spotify .user_follow_artists(artists.iter().map(|a| a.as_ref())) .await .expect("AuthCodePkceSpotify couldn't follow artists"); println!("AuthCodePkceSpotify Followed {num_artists} artists successfully."); // Printing the followed artists let followed = spotify .current_user_followed_artists(None, None) .await .expect("couldn't get user followed artists"); println!( "AuthCodePkceSpotify User currently follows at least {} artists.", followed.items.len() ); spotify .user_unfollow_artists(artists) .await .expect("couldn't unfollow artists"); println!("AuthCodePkceSpotify Unfollowed {num_artists} artists successfully."); } async fn refresh_pkce_code(creds: Credentials, oauth: OAuth) { let mut spotify = AuthCodePkceSpotify::new(creds.clone(), oauth.clone()); // Obtaining the access token let url = spotify.get_authorize_url(None).unwrap(); // This function requires the `cli` feature enabled. spotify.prompt_for_token(&url).await.unwrap(); // Token refreshing works as well, but should with the one generated in the // previous request pkce_do_things(&spotify).await; // At a different time, the refresh token can be used to refresh an access // token directly and run requests: println!(">>> Pkce Session two, running some requests:"); // No `prompt_for_user_token` needed. spotify .refresh_token() .await .expect("couldn't refresh user token"); pkce_do_things(&spotify).await; // This process can now be repeated multiple times by using only the // refresh token that was obtained at the beginning. println!(">>> Pkce Session three, running some requests:"); spotify .refresh_token() .await .expect("couldn't refresh user token"); pkce_do_things(&spotify).await; } async fn refresh_auth_code(creds: Credentials, oauth: OAuth) { let spotify = AuthCodeSpotify::new(creds.clone(), oauth.clone()); // In the first session of the application we authenticate and obtain the // refresh token. We can also do some requests here. println!(">>> Session one, obtaining refresh token and running some requests:"); let url = spotify.get_authorize_url(false).unwrap(); // This function requires the `cli` feature enabled. spotify .prompt_for_token(&url) .await .expect("couldn't authenticate successfully"); // Token refreshing works as well, but should with the one generated in the // previous request do_things(&spotify).await; // At a different time, the refresh token can be used to refresh an access // token directly and run requests: println!(">>> Session two, running some requests:"); // No `prompt_for_user_token` needed. spotify .refresh_token() .await .expect("couldn't refresh user token"); do_things(&spotify).await; // This process can now be repeated multiple times by using only the // refresh token that was obtained at the beginning. println!(">>> Session three, running some requests:"); spotify .refresh_token() .await .expect("couldn't refresh user token"); do_things(&spotify).await; } #[tokio::main] async fn main() { // You can use any logger for debugging. env_logger::init(); // May require the `env-file` feature enabled if the environment variables // aren't configured manually. let creds = Credentials::from_env().unwrap(); let oauth = OAuth::from_env(scopes!("user-follow-read user-follow-modify")).unwrap(); refresh_auth_code(creds.clone(), oauth.clone()).await; refresh_pkce_code(creds, oauth).await; }