tokenEndpoint method

Future tokenEndpoint(
  1. RequestContext req,
  2. ResponseContext res
)

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,
    );
  }
}