I’m working on a Flutter app that integrates with Spotify’s authentication system. Everything seems to work fine during the login process, but after the user successfully logs in, my app receives a CANCELLED error message instead of proceeding with the authentication flow.
The issue happens right after the redirect URI brings the user back to my application. Instead of getting the expected response, I see a “user has canceled the login” message even though the login was completed successfully.
Here’s my authentication service code:
class SpotifyAuthService {
static const appId = '//your-client-id';
static const appSecret = '//your-client-secret';
static const callbackUri = 'myflutterapp://auth-callback';
static const String loginEndpoint = 'https://accounts.spotify.com/authorize';
static const String accessTokenEndpoint = 'https://accounts.spotify.com/api/token';
static const String profileEndpoint = 'https://api.spotify.com/v1/me';
static Future<String?> performLogin() async {
final loginUrlComplete =
'$loginEndpoint?response_type=code&client_id=$appId&scope=user-read-private user-read-email&redirect_uri=$callbackUri';
try {
final authResult = await FlutterWebAuth.authenticate(
url: loginUrlComplete, callbackUrlScheme: 'myflutterapp');
final authCode = Uri.parse(authResult).queryParameters['code'];
if (authCode != null) {
return await _getAccessTokenFromCode(authCode);
} else {
return null;
}
} catch (error) {
if (error.toString().contains('CANCELLED')) {
print('User cancelled the authentication process.');
return null;
} else {
print('Authentication error occurred: $error');
return null;
}
}
}
static Future<String?> _getAccessTokenFromCode(String authCode) async {
try {
final httpClient = Dio();
final tokenResponse = await httpClient.post(
accessTokenEndpoint,
data: {
'grant_type': 'authorization_code',
'code': authCode,
'redirect_uri': callbackUri,
},
options: Options(
headers: {
'Authorization': 'Basic ' + base64Encode(utf8.encode('$appId:$appSecret')),
},
),
);
if (tokenResponse.statusCode == 200) {
return tokenResponse.data['access_token'];
} else {
print('Failed to retrieve access token: ${tokenResponse.statusCode}');
return null;
}
} catch (error) {
print('Token exchange error: $error');
return null;
}
}
static Future<Map<String, dynamic>?> fetchUserData(String accessToken) async {
///
}
After successful authentication, I need to navigate to the main screen and display user information. Any ideas on what might be causing this CANCELLED error?