platform/packages/auth_oauth2/lib/angel3_auth_oauth2.dart

88 lines
3 KiB
Dart
Raw Normal View History

library angel3_auth_oauth2;
2017-01-12 22:48:51 +00:00
import 'dart:async';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
2018-03-30 16:44:15 +00:00
import 'package:http_parser/http_parser.dart';
2017-01-12 22:48:51 +00:00
import 'package:oauth2/oauth2.dart' as oauth2;
2019-01-06 00:43:06 +00:00
/// 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;
2019-01-06 00:43:06 +00:00
/// An optional delimiter used to send requests to server who does not follow the OAuth 2.0 spec.
2017-06-03 20:43:36 +00:00
final String delimiter;
2017-01-12 22:48:51 +00:00
Uri? _redirect;
2019-01-06 00:43:06 +00:00
OAuth2Strategy(this.options, this.authorizationEndpoint, this.tokenEndpoint,
this.verifier, this.onError,
{this.getParameters, this.delimiter = ' '});
2017-01-12 22:48:51 +00:00
oauth2.AuthorizationCodeGrant _createGrant() => oauth2.AuthorizationCodeGrant(
options.clientId, authorizationEndpoint, tokenEndpoint,
secret: options.clientSecret,
delimiter: delimiter,
getParameters: getParameters);
2017-01-12 22:48:51 +00:00
@override
FutureOr<User?> authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions<User>? options]) async {
2019-01-06 00:43:06 +00:00
if (options != null) {
var result = await authenticateCallback(req, res, options);
if (result is User) {
2019-01-06 00:43:06 +00:00
return result;
} else {
2019-01-06 00:43:06 +00:00
return null;
}
2019-01-06 00:43:06 +00:00
}
if (_redirect == null) {
var grant = _createGrant();
_redirect = grant.getAuthorizationUrl(
this.options.redirectUri,
scopes: this.options.scopes,
);
}
await res.redirect(_redirect);
2018-09-12 03:23:42 +00:00
return null;
2017-01-12 22:48:51 +00:00
}
2019-01-06 00:43:06 +00:00
/// The endpoint that is invoked by the third-party after successful authentication.
Future<dynamic> authenticateCallback(RequestContext req, ResponseContext res,
[AngelAuthOptions? options]) async {
2019-01-06 00:43:06 +00:00
var grant = _createGrant();
grant.getAuthorizationUrl(this.options.redirectUri,
scopes: this.options.scopes);
try {
var client =
await grant.handleAuthorizationResponse(req.uri!.queryParameters);
2019-01-06 00:43:06 +00:00
return await verifier(client, req, res);
} on oauth2.AuthorizationException catch (e) {
return await onError(e, req, res);
}
2017-01-12 22:48:51 +00:00
}
}