library angel3_auth_oauth2; import 'dart:async'; import 'package:angel3_auth/angel3_auth.dart'; import 'package:angel3_framework/angel3_framework.dart'; import 'package:http_parser/http_parser.dart'; import 'package:oauth2/oauth2.dart' as oauth2; /// An Angel [AuthStrategy] that signs users in via a third-party service that speaks OAuth 2.0. class OAuth2Strategy implements AuthStrategy { /// A callback that uses the third-party service to authenticate a [User]. /// /// As always, return `null` if authentication fails. final FutureOr Function(oauth2.Client, RequestContext, ResponseContext) verifier; /// A callback that is triggered when an OAuth2 error occurs (i.e. the user declines to login); final FutureOr Function( oauth2.AuthorizationException, RequestContext, ResponseContext) onError; /// The options defining how to connect to the third-party. final ExternalAuthOptions options; /// The URL to query to receive an authentication code. final Uri authorizationEndpoint; /// The URL to query to exchange an authentication code for a token. final Uri tokenEndpoint; /// An optional callback used to parse the response from a server who does not follow the OAuth 2.0 spec. final Map Function(MediaType?, String)? getParameters; /// An optional delimiter used to send requests to server who does not follow the OAuth 2.0 spec. final String delimiter; Uri? _redirect; OAuth2Strategy(this.options, this.authorizationEndpoint, this.tokenEndpoint, this.verifier, this.onError, {this.getParameters, this.delimiter = ' '}); oauth2.AuthorizationCodeGrant _createGrant() => oauth2.AuthorizationCodeGrant( options.clientId, authorizationEndpoint, tokenEndpoint, secret: options.clientSecret, delimiter: delimiter, getParameters: getParameters); @override FutureOr authenticate(RequestContext req, ResponseContext res, [AngelAuthOptions? options]) async { if (options != null) { var result = await authenticateCallback(req, res, options); if (result is User) { return result; } else { return null; } } if (_redirect == null) { var grant = _createGrant(); _redirect = grant.getAuthorizationUrl( this.options.redirectUri, scopes: this.options.scopes, ); } await res.redirect(_redirect); return null; } /// The endpoint that is invoked by the third-party after successful authentication. Future authenticateCallback(RequestContext req, ResponseContext res, [AngelAuthOptions? options]) async { var grant = _createGrant(); grant.getAuthorizationUrl(this.options.redirectUri, scopes: this.options.scopes); try { var client = await grant.handleAuthorizationResponse(req.uri!.queryParameters); return await verifier(client, req, res); } on oauth2.AuthorizationException catch (e) { return await onError(e, req, res); } } }