exchangeCodeForToken method
Exchange an authorization code for tokens. When the authorization
response carried an iss parameter (RFC 9207), pass it as
responseIssuer — it is validated against the AS issuer before the
exchange (mix-up defense).
Implementation
@override
Future<OAuthToken> exchangeCodeForToken({
required String code,
required String codeVerifier,
String? responseIssuer,
}) async {
await validateAuthorizationResponseIssuer(responseIssuer);
final metadata = await _discoverMetadata();
final body = <String, String>{
'grant_type': 'authorization_code',
'code': code,
'client_id': effectiveClientId,
'code_verifier': codeVerifier,
if (config.redirectUri != null) 'redirect_uri': config.redirectUri!,
};
final headers = <String, String>{
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
};
// Add client authentication if confidential client
if (config.clientSecret != null) {
final credentials = base64Encode(
utf8.encode('${config.clientId}:${config.clientSecret}'),
);
headers['Authorization'] = 'Basic $credentials';
}
final response = await _httpClient.post(
Uri.parse(metadata.tokenEndpoint),
headers: headers,
body: body.entries
.map((e) => '${e.key}=${Uri.encodeComponent(e.value)}')
.join('&'),
);
final json = jsonDecode(response.body) as Map<String, dynamic>;
if (response.statusCode != 200) {
throw OAuthError.fromJson(json);
}
return OAuthToken.fromJson(json);
}