tokenEndpoint method
A request handler that either exchanges authorization codes for authorization tokens, or refreshes authorization tokens.
Implementation
Future tokenEndpoint(RequestContext req, ResponseContext res) async {
var state = '';
Client? client;
try {
AuthorizationTokenResponse? response;
var body = await req.parseBody().then((_) => req.bodyAsMap);
state = body['state']?.toString() ?? '';
req.container!.registerLazySingleton<Pkce>((_) {
return Pkce.fromJson(req.bodyAsMap, state: state);
});
var grantType = await _getParam(req, 'grant_type', state,
body: true, throwIfEmpty: false);
if (grantType != 'urn:ietf:params:oauth:grant-type:device_code' &&
grantType != null) {
var match =
_rgxBasic.firstMatch(req.headers!.value('authorization') ?? '');
if (match != null) {
match = _rgxBasicAuth
.firstMatch(String.fromCharCodes(base64Url.decode(match[1]!)));
}
if (match == null) {
throw AuthorizationException(
ErrorResponse(
ErrorResponse.unauthorizedClient,
'Invalid or no "Authorization" header.',
state,
),
statusCode: 400,
);
} else {
var clientId = match[1], clientSecret = match[2];
client = await findClient(clientId);
if (client == null) {
throw AuthorizationException(
ErrorResponse(
ErrorResponse.unauthorizedClient,
'Invalid "client_id" parameter.',
state,
),
statusCode: 400,
);
}
if (!await verifyClient(client, clientSecret)) {
throw AuthorizationException(
ErrorResponse(
ErrorResponse.unauthorizedClient,
'Invalid "client_secret" parameter.',
state,
),
statusCode: 400,
);
}
}
}
if (grantType == 'authorization_code') {
var code = await _getParam(req, 'code', state, body: true);
var redirectUri =
await _getParam(req, 'redirect_uri', state, body: true);
response = await exchangeAuthorizationCodeForToken(
client, code, redirectUri, req, res);
} else if (grantType == 'refresh_token') {
var refreshToken =
await _getParam(req, 'refresh_token', state, body: true);
var scopes = await _getScopes(req);
response = await refreshAuthorizationToken(
client, refreshToken, scopes, req, res);
} else if (grantType == 'password') {
var username = await _getParam(req, 'username', state, body: true);
var password = await _getParam(req, 'password', state, body: true);
var scopes = await _getScopes(req);
response = await resourceOwnerPasswordCredentialsGrant(
client, username, password, scopes, req, res);
} else if (grantType == 'client_credentials') {
response = await clientCredentialsGrant(client, req, res);
if (response.refreshToken != null) {
// Remove refresh token
response = AuthorizationTokenResponse(
response.accessToken,
expiresIn: response.expiresIn,
scope: response.scope,
);
}
} else if (extensionGrants.containsKey(grantType)) {
response = await extensionGrants[grantType!]!(req, res);
} else if (grantType == null) {
// This is a device code grant.
var clientId = await _getParam(req, 'client_id', state, body: true);
client = await findClient(clientId);
if (client == null) {
throw AuthorizationException(
ErrorResponse(
ErrorResponse.unauthorizedClient,
'Invalid "client_id" parameter.',
state,
),
statusCode: 400,
);
}
var scopes = await _getScopes(req, body: true);
var deviceCodeResponse =
await requestDeviceCode(client, scopes, req, res);
return deviceCodeResponse.toJson();
} else if (grantType == 'urn:ietf:params:oauth:grant-type:device_code') {
var clientId = await _getParam(req, 'client_id', state, body: true);
client = await findClient(clientId);
if (client == null) {
throw AuthorizationException(
ErrorResponse(
ErrorResponse.unauthorizedClient,
'Invalid "client_id" parameter.',
state,
),
statusCode: 400,
);
}
var deviceCode = await _getParam(req, 'device_code', state, body: true);
response = await exchangeDeviceCodeForToken(
client, deviceCode, state, req, res);
}
if (response != null) {
return <String, dynamic>{'token_type': AuthorizationTokenType.bearer}
..addAll(response.toJson());
}
throw AuthorizationException(
ErrorResponse(
ErrorResponse.invalidRequest,
'Invalid or no "grant_type" parameter provided',
state,
),
statusCode: 400,
);
} on AngelHttpException {
rethrow;
} catch (e, st) {
throw AuthorizationException(
ErrorResponse(
ErrorResponse.serverError,
_internalServerError,
state,
),
error: e,
statusCode: 500,
stackTrace: st,
);
}
}