This commit is contained in:
Tobe O 2018-09-11 23:23:42 -04:00
parent 5056738ef4
commit 3a5a31c5de
6 changed files with 58 additions and 54 deletions

2
.gitignore vendored
View file

@ -71,3 +71,5 @@ com_crashlytics_export_strings.xml
crashlytics.properties crashlytics.properties
crashlytics-build.properties crashlytics-build.properties
fabric.properties fabric.properties
.dart_tool

View file

@ -75,7 +75,7 @@ a popup window. In this case, use `confirmPopupAuthentication`, which is bundled
configureServer(Angel app) async { configureServer(Angel app) async {
// ... // ...
var auth = new AngelAuth(); var auth = new AngelAuth();
auth.strategies.add(oauth2Strategy); auth.strategies['github'] = oauth2Strategy;
// Redirect // Redirect
app.get('/auth/github', auth.authenticate('github')); app.get('/auth/github', auth.authenticate('github'));

View file

@ -1,2 +1,3 @@
analyzer: analyzer:
strong-mode: true strong-mode:
implicit-casts: false

View file

@ -2,7 +2,6 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:angel_auth/angel_auth.dart'; import 'package:angel_auth/angel_auth.dart';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/common.dart';
import 'package:angel_auth_oauth2/angel_auth_oauth2.dart'; import 'package:angel_auth_oauth2/angel_auth_oauth2.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:oauth2/oauth2.dart' as oauth2; import 'package:oauth2/oauth2.dart' as oauth2;
@ -17,29 +16,31 @@ final AngelAuthOAuth2Options oAuth2Config = new AngelAuthOAuth2Options(
if (contentType.type == 'application') { if (contentType.type == 'application') {
if (contentType.subtype == 'x-www-form-urlencoded') if (contentType.subtype == 'x-www-form-urlencoded')
return Uri.splitQueryString(body); return Uri.splitQueryString(body);
else if (contentType.subtype == 'json') return JSON.decode(body); else if (contentType.subtype == 'json')
return (json.decode(body) as Map).cast<String, String>();
} }
throw new FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.'); throw new FormatException(
'Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
}); });
main() async { main() async {
var app = new Angel(); var app = new Angel();
app.lazyParseBodies = true;
app.use('/users', new MapService()); app.use('/users', new MapService());
var auth = var auth =
new AngelAuth<User>(jwtKey: 'oauth2 example secret', allowCookie: false); new AngelAuth<User>(jwtKey: 'oauth2 example secret', allowCookie: false);
auth.deserializer = auth.deserializer =
(id) => app.service('users').read(id).then((u) => User.parse(u)); (id) => app.service('users').read(id).then((u) => User.parse(u as Map));
auth.serializer = (User user) async => user.id; auth.serializer = (User user) async => user.id;
auth.strategies.add( auth.strategies['github'] = new OAuth2Strategy(
new OAuth2Strategy('github', oAuth2Config, (oauth2.Client client) async { oAuth2Config,
(oauth2.Client client) async {
var response = await client.get('https://api.github.com/user'); var response = await client.get('https://api.github.com/user');
var ghUser = JSON.decode(response.body); var ghUser = json.decode(response.body);
var id = ghUser['id']; var id = ghUser['id'];
Iterable<Map> matchingUsers = await app.service('users').index({ Iterable<Map> matchingUsers = await app.service('users').index({
@ -53,9 +54,10 @@ main() async {
// Otherwise,create a user // Otherwise,create a user
return await app return await app
.service('users') .service('users')
.create({'githubId': id}).then((u) => User.parse(u)); .create({'githubId': id}).then((u) => User.parse(u as Map));
} }
})); },
);
app.get('/auth/github', auth.authenticate('github')); app.get('/auth/github', auth.authenticate('github'));
app.get( app.get(
@ -74,7 +76,7 @@ main() async {
app.logger = new Logger('angel')..onRecord.listen(print); app.logger = new Logger('angel')..onRecord.listen(print);
var http = new AngelHttp(app); var http = new AngelHttp(app);
var server = await http.startServer(InternetAddress.LOOPBACK_IP_V4, 3000); var server = await http.startServer(InternetAddress.loopbackIPv4, 3000);
var url = 'http://${server.address.address}:${server.port}'; var url = 'http://${server.address.address}:${server.port}';
print('Listening on $url'); print('Listening on $url');
print('View user listing: $url/users'); print('View user listing: $url/users');
@ -89,7 +91,7 @@ class User extends Model {
User({this.id, this.githubId}); User({this.id, this.githubId});
static User parse(Map map) => static User parse(Map map) =>
new User(id: map['id'], githubId: map['github_id']); new User(id: map['id'] as String, githubId: map['github_id'] as int);
Map<String, dynamic> toJson() => {'id': id, 'github_id': githubId}; Map<String, dynamic> toJson() => {'id': id, 'github_id': githubId};
} }

View file

@ -10,10 +10,10 @@ import 'package:oauth2/oauth2.dart' as oauth2;
final Validator OAUTH2_OPTIONS_SCHEMA = new Validator({ final Validator OAUTH2_OPTIONS_SCHEMA = new Validator({
'key*': isString, 'key*': isString,
'secret*': isString, 'secret*': isString,
'authorizationEndpoint*': anyOf(isString, const isInstanceOf<Uri>()), 'authorizationEndpoint*': anyOf(isString, const TypeMatcher<Uri>()),
'tokenEndpoint*': anyOf(isString, const isInstanceOf<Uri>()), 'tokenEndpoint*': anyOf(isString, const TypeMatcher<Uri>()),
'callback*': isString, 'callback*': isString,
'scopes': const isInstanceOf<Iterable<String>>() 'scopes': const TypeMatcher<Iterable<String>>()
}, defaultValues: { }, defaultValues: {
'scopes': <String>[] 'scopes': <String>[]
}, customErrorMessages: { }, customErrorMessages: {
@ -29,10 +29,10 @@ class AngelAuthOAuth2Options {
final String secret; final String secret;
/// The remote endpoint that prompts external users for authentication credentials. /// The remote endpoint that prompts external users for authentication credentials.
final authorizationEndpoint; final String authorizationEndpoint;
/// The remote endpoint that exchanges auth codes for access tokens. /// The remote endpoint that exchanges auth codes for access tokens.
final tokenEndpoint; final String tokenEndpoint;
/// The callback URL that the OAuth2 server should redirect authenticated users to. /// The callback URL that the OAuth2 server should redirect authenticated users to.
final String callback; final String callback;
@ -55,12 +55,13 @@ class AngelAuthOAuth2Options {
factory AngelAuthOAuth2Options.fromJson(Map json) => factory AngelAuthOAuth2Options.fromJson(Map json) =>
new AngelAuthOAuth2Options( new AngelAuthOAuth2Options(
key: json['key'], key: json['key'] as String,
secret: json['secret'], secret: json['secret'] as String,
authorizationEndpoint: json['authorizationEndpoint'], authorizationEndpoint: json['authorizationEndpoint'] as String,
tokenEndpoint: json['tokenEndpoint'], tokenEndpoint: json['tokenEndpoint'] as String,
callback: json['callback'], callback: json['callback'] as String,
scopes: json['scopes'] ?? <String>[]); scopes: (json['scopes'] as Iterable)?.cast<String>()?.toList() ??
<String>[]);
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
@ -74,14 +75,13 @@ class AngelAuthOAuth2Options {
} }
} }
class OAuth2Strategy<T> implements AuthStrategy { class OAuth2Strategy<User> implements AuthStrategy<User> {
final FutureOr<T> Function(oauth2.Client) verifier; final FutureOr<User> Function(oauth2.Client) verifier;
String name;
AngelAuthOAuth2Options _options; AngelAuthOAuth2Options _options;
/// [options] can be either a `Map` or an instance of [AngelAuthOAuth2Options]. /// [options] can be either a `Map` or an instance of [AngelAuthOAuth2Options].
OAuth2Strategy(this.name, options, this.verifier) { OAuth2Strategy(options, this.verifier) {
if (options is AngelAuthOAuth2Options) if (options is AngelAuthOAuth2Options)
_options = options; _options = options;
else if (options is Map) else if (options is Map)
@ -101,8 +101,8 @@ class OAuth2Strategy<T> implements AuthStrategy {
getParameters: _options.getParameters); getParameters: _options.getParameters);
@override @override
Future authenticate(RequestContext req, ResponseContext res, FutureOr<User> authenticate(RequestContext req, ResponseContext res,
[AngelAuthOptions options]) async { [AngelAuthOptions<User> options]) async {
if (options != null) return authenticateCallback(req, res, options); if (options != null) return authenticateCallback(req, res, options);
var grant = createGrant(); var grant = createGrant();
@ -110,18 +110,16 @@ class OAuth2Strategy<T> implements AuthStrategy {
.getAuthorizationUrl(Uri.parse(_options.callback), .getAuthorizationUrl(Uri.parse(_options.callback),
scopes: _options.scopes) scopes: _options.scopes)
.toString()); .toString());
return false; return null;
} }
Future authenticateCallback(RequestContext req, ResponseContext res, Future<User> authenticateCallback(RequestContext req, ResponseContext res,
[AngelAuthOptions options]) async { [AngelAuthOptions options]) async {
var grant = createGrant(); var grant = createGrant();
await grant.getAuthorizationUrl(Uri.parse(_options.callback), await grant.getAuthorizationUrl(Uri.parse(_options.callback),
scopes: _options.scopes); scopes: _options.scopes);
var client = await grant.handleAuthorizationResponse(req.query); var client =
await grant.handleAuthorizationResponse(req.uri.queryParameters);
return await verifier(client); return await verifier(client);
} }
@override
Future<bool> canLogout(RequestContext req, ResponseContext res) async => true;
} }

View file

@ -1,11 +1,12 @@
name: angel_auth_oauth2 name: angel_auth_oauth2
description: angel_auth strategy for OAuth2 login, i.e. Facebook. description: angel_auth strategy for OAuth2 login, i.e. Facebook.
version: 1.0.2 version: 2.0.0
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
environment: environment:
sdk: ">=1.19.0 <3.0.0" sdk: ">=2.0.0-dev <3.0.0"
homepage: https://github.com/angel-dart/auth_oauth2.git homepage: https://github.com/angel-dart/auth_oauth2.git
dependencies: dependencies:
angel_auth: ^1.0.0-dev angel_auth: ^2.0.0
angel_validate: ^1.0.0-beta angel_framework: ^2.0.0-alpha
angel_validate: ^2.0.0-alpha
oauth2: ^1.0.0 oauth2: ^1.0.0