Closed gmpassos closed 8 months ago
@gmpassos Could you maybe give an example on how this could look like? Better, open a PR? :)
What is necessary is be able to call SpotifyApi.authorizationCodeGrant
passing an extra parameter codeVerifier
(optional).
SpotifyApi.authorizationCodeGrant(credentials, codeVerifier: codeVerifier);
Then internally it needs to repass it to SpotifyApiBase.authorizationCodeGrant
:
SpotifyApiBase.authorizationCodeGrant(credentials, http.Client(), onCredentialsRefreshed, codeVerifier: codeVerifier);
And then oauth2.AuthorizationCodeGrant
should be called passing codeVerifier
.
Here's a real world OAuth integration with Spotify in Dart using a DB stored codeVerifier
:
(relevant code only):
import 'dart:math';
import 'dart:convert';
import 'package:spotify/spotify.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:http/http.dart' as http;
//...
Future<String?> getAuthorizationUrl(int establishmentId) async {
//...
final clientId = _clientId!;
final clientSecret = _clientSecret!;
final redirectUrl = _redirectUri!;
final credentials = SpotifyApiCredentials(clientId, clientSecret);
final info = <String, dynamic>{
'establishmentId': establishmentId,
};
final state = json.encode(info);
final codeVerifier = _createCodeVerifier(); // my own code verifier.
// save in the DB the OAuth integration data:
var oAuthIntegration = await _saveOAuthIntegration(
establishmentId,
clientId,
clientSecret,
codeVerifier: codeVerifier,
);
if (oAuthIntegration == null) {
return throw StateError("Invalid `oAuthIntegration`");
}
final grant = oauth2.AuthorizationCodeGrant(
credentials.clientId!,
Uri.parse(_spotifyAuthorizationUrl),
Uri.parse(_spotifyTokenUrl),
secret: credentials.clientSecret,
httpClient: http.Client(),
codeVerifier: codeVerifier,
);
var redirectUri = Uri.parse(redirectUrl);
var authorizationUrl = grant.getAuthorizationUrl(
redirectUri,
scopes: _spotifyScopes,
state: state,
);
return authorizationUrl.toString();
}
Future<bool> oauth(
String? code, String? state, String requestedUri) async {
if (code == null || code.isEmpty) {
throw StateError("Invalid `code`");
}
if (state == null || state.isEmpty) {
throw StateError("Invalid `state`");
}
var parameters = json.decode(state) as Map;
var establishmentId = parameters['establishmentId'] as int?;
if (establishmentId == null) {
throw StateError("Invalid `establishmentId`");
}
//...
final clientId = _clientId!;
final clientSecret = _clientSecret!;
final redirectUrl = _redirectUri!;
var oAuthIntegration = await _getOAuthIntegration(
establishmentId, clientId, clientSecret);
if (oAuthIntegration == null) {
throw StateError("Invalid `oAuthIntegration`");
}
final codeVerifier = oAuthIntegration.codeVerifier;
if (codeVerifier == null || codeVerifier.isEmpty) {
throw StateError("Invalid `codeVerifier`");
}
final credentials = SpotifyApiCredentials(clientId, clientSecret);
//
// Can't pass `codeVerifier` to:
// SpotifyApi.authorizationCodeGrant
//
// Using `oauth2.AuthorizationCodeGrant` directly:
final grant = oauth2.AuthorizationCodeGrant(
credentials.clientId!,
Uri.parse(_spotifyAuthorizationUrl),
Uri.parse(_spotifyTokenUrl),
secret: credentials.clientSecret,
httpClient: http.Client(),
codeVerifier: codeVerifier,
);
var redirectUri = Uri.parse(redirectUrl);
var authorizationUrl = grant.getAuthorizationUrl(
redirectUri,
scopes: _spotifyScopes,
state: state,
);
print('-- authorizationUrl: $authorizationUrl');
final spotify = SpotifyApi.fromAuthCodeGrant(grant, requestedUri);
var resolvedCredential = await spotify.getCredentials();
var oAuthIntegration2 = await oAuthIntegrationModule
.save(
oAuthIntegration.id!,
codeVerifier: '',
accessToken: resolvedCredential.accessToken,
refreshToken: resolvedCredential.refreshToken,
scopes: resolvedCredential.scopes,
expires: resolvedCredential.expiration,
refreshTokenURL: resolvedCredential.tokenEndpoint.toString(),
)
.payloadAsync;
if (oAuthIntegration2 == null) {
throw StateError("Invalid `oAuthIntegration2`");
}
return true;
}
Thank you for your contribution - it's published with version 0.13.2!
The library needs to expose the parameter
codeVerifier
so that we can use a personalized or database-storedcodeVerifier
.This needs to be done in
SpotifyApi.authorizationCodeGrant
andSpotifyApiBase.authorizationCodeGrant
, and then passed tooauth2.AuthorizationCodeGrant
.