rinukkusu / spotify-dart

A dart library for interfacing with the Spotify API.
BSD 3-Clause "New" or "Revised" License
204 stars 92 forks source link

Support of Authorization Code Flow with Proof Key for Code Exchange (PKCE) #81

Open nkovshov opened 3 years ago

nkovshov commented 3 years ago

It's not documented, but passing null for clientSecret:

final credentials = SpotifyApiCredentials(clientId, null);

forces oauth2 lib to generate challenge and follow PKCE auth flow (https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce) and it worked out of the box for me.

It may be worth to document it.

Ruud14 commented 2 years ago

It seems like PKCE works fine when passing null for clientSecret when using the 'Authorization Code Flow': final credentials = SpotifyApiCredentials(clientId, null); spotify = SpotifyApi.fromAuthCodeGrant(grant, responseUri); But it doesn't work when later using the 'Saved Credentials Flow': spotify = SpotifyApi(spotifyCredentials);

It seems like PKCE works only with the SpotifyApi.fromAuthCodeGrant(grant, responseUri) constructor and not with the SpotifyApi(spotifyCredentials) default constructor.

Is there any way to use PKCE with the 'Saved Credentials Flow'?

Ruud14 commented 2 years ago

Okay I managed to find a sloppy workaround:

I manually created an oauth2 client based on the saved credentials ('creds' here):

import 'package:oauth2/src/client.dart';
import 'package:oauth2/src/credentials.dart';

Credentials credentials = Credentials(creds.accessToken!,
          refreshToken: creds.refreshToken,
          idToken: creds.clientId,
          tokenEndpoint: creds.tokenEndpoint,
          scopes: creds.scopes,
          expiration: creds.expiration);

Client client = Client(credentials,
          identifier: clientId,
          secret: null,
          basicAuth: true,
          httpClient: http.Client(),
          onCredentialsRefreshed: _onCredentialsRefreshed);

And then create a SpotifyApi instance with the .fromClient constructor.

spotify = SpotifyApi.fromClient(client);

However there are multiple things wrong with this, one of which being that a direct import from another package is required.

For now this works, but I'd like to see a better solution.

esskar commented 2 years ago

Will this be added?

adamkoch commented 9 months ago

Thanks for this! Getting the token and creating the client works fine using the hack from @Ruud14. But how do you manually refresh the token now that it's going outside the spotify-dart library?

hayribakici commented 9 months ago

@rinukkusu should we add a "help wanted" label for this issue?