platform/packages/auth_oauth2/lib/angel_auth_oauth2.dart
Tobe O c77d25f6d0 Add 'packages/auth_oauth2/' from commit '97bd61e7ee6736c8a871d6cc45a5203e07bb03e6'
git-subtree-dir: packages/auth_oauth2
git-subtree-mainline: a584b8ce02
git-subtree-split: 97bd61e7ee
2020-02-15 18:28:54 -05:00

87 lines
3 KiB
Dart

library angel_auth_oauth2;
import 'dart:async';
import 'package:angel_auth/angel_auth.dart';
import 'package:angel_framework/angel_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<User> implements AuthStrategy<User> {
/// A callback that uses the third-party service to authenticate a [User].
///
/// As always, return `null` if authentication fails.
final FutureOr<User> 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<dynamic> 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<String, dynamic> 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() =>
new oauth2.AuthorizationCodeGrant(options.clientId, authorizationEndpoint,
tokenEndpoint,
secret: options.clientSecret,
delimiter: delimiter,
getParameters: getParameters);
@override
FutureOr<User> authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions<User> 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,
);
}
res.redirect(_redirect);
return null;
}
/// The endpoint that is invoked by the third-party after successful authentication.
Future<dynamic> 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);
}
}
}