platform/lib/angel_auth_oauth2.dart

128 lines
4.1 KiB
Dart
Raw Normal View History

2017-01-12 22:48:51 +00:00
library angel_auth_oauth2;
import 'dart:async';
import 'package:angel_auth/angel_auth.dart';
2018-03-30 16:44:15 +00:00
import 'package:angel_framework/angel_framework.dart';
2017-01-12 22:48:51 +00:00
import 'package:angel_validate/angel_validate.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;
final Validator OAUTH2_OPTIONS_SCHEMA = new Validator({
'key*': isString,
'secret*': isString,
2018-03-30 16:44:15 +00:00
'authorizationEndpoint*': anyOf(isString, const isInstanceOf<Uri>()),
'tokenEndpoint*': anyOf(isString, const isInstanceOf<Uri>()),
2017-01-12 22:48:51 +00:00
'callback*': isString,
2018-03-30 16:44:15 +00:00
'scopes': const isInstanceOf<Iterable<String>>()
2017-01-12 22:48:51 +00:00
}, defaultValues: {
'scopes': <String>[]
}, customErrorMessages: {
'scopes': "'scopes' must be an Iterable of strings. You provided: {{value}}"
});
2017-06-03 20:43:36 +00:00
/// Holds credentials and also specifies the means of authenticating users against a remote server.
2017-02-23 01:13:23 +00:00
class AngelAuthOAuth2Options {
2017-06-03 20:43:36 +00:00
/// Your application's client key or client ID, registered with the remote server.
final String key;
2017-01-12 22:48:51 +00:00
2017-06-03 20:43:36 +00:00
/// Your application's client secret, registered with the remote server.
final String secret;
/// The remote endpoint that prompts external users for authentication credentials.
2018-03-30 16:44:15 +00:00
final authorizationEndpoint;
2017-06-03 20:43:36 +00:00
/// The remote endpoint that exchanges auth codes for access tokens.
2018-03-30 16:44:15 +00:00
final tokenEndpoint;
2017-06-03 20:43:36 +00:00
/// The callback URL that the OAuth2 server should redirect authenticated users to.
final String callback;
/// Used to split application scopes. Defaults to `' '`.
final String delimiter;
final Iterable<String> scopes;
2018-03-30 16:44:15 +00:00
final Map<String, String> Function(MediaType, String) getParameters;
2017-06-03 20:43:36 +00:00
const AngelAuthOAuth2Options(
2017-01-12 22:48:51 +00:00
{this.key,
this.secret,
this.authorizationEndpoint,
this.tokenEndpoint,
this.callback,
2017-06-03 20:43:36 +00:00
this.delimiter: ' ',
2018-03-30 16:44:15 +00:00
this.scopes: const [],
this.getParameters});
2017-01-12 22:48:51 +00:00
2017-02-23 01:13:23 +00:00
factory AngelAuthOAuth2Options.fromJson(Map json) =>
new AngelAuthOAuth2Options(
2017-01-12 22:48:51 +00:00
key: json['key'],
secret: json['secret'],
authorizationEndpoint: json['authorizationEndpoint'],
tokenEndpoint: json['tokenEndpoint'],
callback: json['callback'],
scopes: json['scopes'] ?? <String>[]);
2018-03-30 16:44:15 +00:00
Map<String, dynamic> toJson() {
2017-01-12 22:48:51 +00:00
return {
'key': key,
'secret': secret,
'authorizationEndpoint': authorizationEndpoint,
'tokenEndpoint': tokenEndpoint,
'callback': callback,
'scopes': scopes.toList()
};
}
}
2018-03-30 16:44:15 +00:00
class OAuth2Strategy<T> implements AuthStrategy {
final FutureOr<T> Function(oauth2.Client) verifier;
String name;
2017-01-12 22:48:51 +00:00
2018-03-30 16:44:15 +00:00
AngelAuthOAuth2Options _options;
2017-01-12 22:48:51 +00:00
2017-02-23 01:13:23 +00:00
/// [options] can be either a `Map` or an instance of [AngelAuthOAuth2Options].
2018-03-30 16:44:15 +00:00
OAuth2Strategy(this.name, options, this.verifier) {
2017-02-23 01:13:23 +00:00
if (options is AngelAuthOAuth2Options)
2017-01-12 22:48:51 +00:00
_options = options;
else if (options is Map)
2017-02-23 01:13:23 +00:00
_options = new AngelAuthOAuth2Options.fromJson(
2017-01-12 22:48:51 +00:00
OAUTH2_OPTIONS_SCHEMA.enforce(options));
else
throw new ArgumentError('Invalid OAuth2 options: $options');
}
oauth2.AuthorizationCodeGrant createGrant() =>
new oauth2.AuthorizationCodeGrant(
_options.key,
Uri.parse(_options.authorizationEndpoint),
Uri.parse(_options.tokenEndpoint),
2017-06-03 20:43:36 +00:00
secret: _options.secret,
2018-03-30 16:44:15 +00:00
delimiter: _options.delimiter ?? ' ',
getParameters: _options.getParameters);
2017-01-12 22:48:51 +00:00
@override
Future authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions options]) async {
if (options != null) return authenticateCallback(req, res, options);
var grant = createGrant();
res.redirect(grant
.getAuthorizationUrl(Uri.parse(_options.callback),
scopes: _options.scopes)
.toString());
return false;
}
Future authenticateCallback(RequestContext req, ResponseContext res,
[AngelAuthOptions options]) async {
var grant = createGrant();
await grant.getAuthorizationUrl(Uri.parse(_options.callback),
scopes: _options.scopes);
var client = await grant.handleAuthorizationResponse(req.query);
2017-02-23 01:13:23 +00:00
return await verifier(client);
2017-01-12 22:48:51 +00:00
}
@override
Future<bool> canLogout(RequestContext req, ResponseContext res) async => true;
}