This commit is contained in:
Tobe O 2019-01-05 18:54:48 -05:00
parent b660eef2db
commit 46318e5fa6
10 changed files with 57 additions and 33 deletions

View file

@ -1,3 +1,6 @@
# 2.1.1
* Added `scopes` to `ExternalAuthOptions`.
# 2.1.0
* Added `ExternalAuthOptions`.

View file

@ -36,10 +36,10 @@ class AuthToken {
AuthToken(
{this.ipAddress,
this.lifeSpan: -1,
this.lifeSpan = -1,
this.userId,
DateTime issuedAt,
Map payload: const {}}) {
Map payload = const {}}) {
this.issuedAt = issuedAt ?? new DateTime.now();
this.payload.addAll(
payload?.keys?.fold({}, (out, k) => out..[k.toString()] = payload[k]) ??

View file

@ -1,4 +1,5 @@
import 'package:charcode/ascii.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
import 'package:quiver_hashcode/hashcode.dart';
@ -13,7 +14,11 @@ class ExternalAuthOptions {
/// The user's redirect URI.
final Uri redirectUri;
ExternalAuthOptions._(this.clientId, this.clientSecret, this.redirectUri) {
/// The scopes to be passed to the external server.
final Set<String> scopes;
ExternalAuthOptions._(
this.clientId, this.clientSecret, this.redirectUri, this.scopes) {
if (clientId == null) {
throw new ArgumentError.notNull('clientId');
} else if (clientSecret == null) {
@ -24,12 +29,14 @@ class ExternalAuthOptions {
factory ExternalAuthOptions(
{@required String clientId,
@required String clientSecret,
@required redirectUri}) {
@required redirectUri,
Iterable<String> scopes = const []}) {
if (redirectUri is String) {
return new ExternalAuthOptions._(
clientId, clientSecret, Uri.parse(redirectUri));
clientId, clientSecret, Uri.parse(redirectUri), scopes.toSet());
} else if (redirectUri is Uri) {
return new ExternalAuthOptions._(clientId, clientSecret, redirectUri);
return new ExternalAuthOptions._(
clientId, clientSecret, redirectUri, scopes.toSet());
} else {
throw new ArgumentError.value(
redirectUri, 'redirectUri', 'must be a String or Uri');
@ -47,26 +54,34 @@ class ExternalAuthOptions {
clientId: map['client_id'] as String,
clientSecret: map['client_secret'] as String,
redirectUri: map['redirect_uri'],
scopes: map['scopes'] is Iterable
? ((map['scopes'] as Iterable).map((x) => x.toString()))
: <String>[],
);
}
@override
int get hashCode => hash3(clientId, clientSecret, redirectUri);
int get hashCode => hash4(clientId, clientSecret, redirectUri, scopes);
@override
bool operator ==(other) =>
other is ExternalAuthOptions &&
other.clientId == clientId &&
other.clientSecret == other.clientSecret &&
other.redirectUri == other.redirectUri;
other.redirectUri == other.redirectUri &&
const SetEquality<String>().equals(other.scopes, scopes);
/// Creates a copy of this object, with the specified changes.
ExternalAuthOptions copyWith(
{String clientId, String clientSecret, redirectUri}) {
{String clientId,
String clientSecret,
redirectUri,
Iterable<String> scopes}) {
return new ExternalAuthOptions(
clientId: clientId ?? this.clientId,
clientSecret: clientSecret ?? this.clientSecret,
redirectUri: redirectUri ?? this.redirectUri,
scopes: (scopes ??= []).followedBy(this.scopes),
);
}
@ -79,11 +94,12 @@ class ExternalAuthOptions {
///
/// If [obscureSecret] is `true` (default), then the [clientSecret] will
/// be replaced by the string `<redacted>`.
Map<String, String> toJson({bool obscureSecret = true}) {
Map<String, dynamic> toJson({bool obscureSecret = true}) {
return {
'client_id': clientId,
'client_secret': obscureSecret ? '<redacted>' : clientSecret,
'redirect_uri': redirectUri.toString(),
'scopes': scopes.toList(),
};
}
@ -110,6 +126,7 @@ class ExternalAuthOptions {
b.write('clientId=$clientId');
b.write(', clientSecret=$secret');
b.write(', redirectUri=$redirectUri');
b.write(', scopes=${scopes.toList()}');
b.write(')');
return b.toString();
}

View file

@ -14,7 +14,7 @@ RequestHandler forceBasicAuth<User>({String realm}) {
/// Restricts access to a resource via authentication.
RequestHandler requireAuthentication<User>() {
return (RequestContext req, ResponseContext res,
{bool throwError: true}) async {
{bool throwError = true}) async {
bool _reject(ResponseContext res) {
if (throwError) {
res.statusCode = 403;

View file

@ -24,7 +24,7 @@ class AngelAuthOptions<User> {
AngelAuthOptions(
{this.callback,
this.tokenCallback,
this.canRespondWithJson: true,
this.canRespondWithJson = true,
this.successRedirect,
String this.failureRedirect});
}

View file

@ -63,8 +63,8 @@ class AngelAuth<User> {
Hmac get hmac => _hs256;
String _randomString(
{int length: 32,
String validChars:
{int length = 32,
String validChars =
"ABCDEFHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"}) {
var chars = <int>[];
while (chars.length < length) chars.add(_random.nextInt(validChars.length));
@ -77,13 +77,13 @@ class AngelAuth<User> {
this.serializer,
this.deserializer,
num jwtLifeSpan,
this.allowCookie: true,
this.allowTokenInQuery: true,
this.enforceIp: true,
this.allowCookie = true,
this.allowTokenInQuery = true,
this.enforceIp = true,
this.cookieDomain,
this.cookiePath: '/',
this.secureCookies: true,
this.reviveTokenEndpoint: "/auth/token"})
this.cookiePath = '/',
this.secureCookies = true,
this.reviveTokenEndpoint = "/auth/token"})
: super() {
_hs256 = new Hmac(sha256, (jwtKey ?? _randomString()).codeUnits);
_jwtLifeSpan = jwtLifeSpan?.toInt() ?? -1;

View file

@ -4,7 +4,7 @@ import 'package:http_parser/http_parser.dart';
import 'options.dart';
/// Displays a default callback page to confirm authentication via popups.
AngelAuthCallback confirmPopupAuthentication({String eventName: 'token'}) {
AngelAuthCallback confirmPopupAuthentication({String eventName = 'token'}) {
return (req, ResponseContext res, String jwt) {
var evt = json.encode(eventName);
var detail = json.encode({'detail': jwt});

View file

@ -23,13 +23,13 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
String realm;
LocalAuthStrategy(this.verifier,
{String this.usernameField: 'username',
String this.passwordField: 'password',
String this.invalidMessage:
{String this.usernameField = 'username',
String this.passwordField = 'password',
String this.invalidMessage =
'Please provide a valid username and password.',
bool this.allowBasic: true,
bool this.forceBasic: false,
String this.realm: 'Authentication is required.'}) {}
bool this.allowBasic = true,
bool this.forceBasic = false,
String this.realm = 'Authentication is required.'}) {}
@override
Future<User> authenticate(RequestContext req, ResponseContext res,
@ -54,8 +54,8 @@ class LocalAuthStrategy<User> extends AuthStrategy<User> {
if (verificationResult == false || verificationResult == null) {
res
..statusCode = 401
..headers['www-authenticate'] = 'Basic realm="$realm"'
..close();
..headers['www-authenticate'] = 'Basic realm="$realm"';
await res.close();
return null;
}

View file

@ -1,6 +1,6 @@
name: angel_auth
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
version: 2.1.0
version: 2.1.1
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_auth
environment:

View file

@ -23,11 +23,13 @@ void main() {
clientId: 'hey',
clientSecret: 'hello',
redirectUri: 'https://yes.no',
scopes: ['a', 'b'],
),
new ExternalAuthOptions(
clientId: 'hey',
clientSecret: 'hello',
redirectUri: 'https://yes.no',
scopes: ['a', 'b'],
),
);
});
@ -113,21 +115,21 @@ void main() {
test('produces correct string', () {
expect(
options.toString(obscureSecret: false),
'ExternalAuthOptions(clientId=foo, clientSecret=bar, redirectUri=http://example.com)',
'ExternalAuthOptions(clientId=foo, clientSecret=bar, redirectUri=http://example.com, scopes=[])',
);
});
test('obscures secret', () {
expect(
options.toString(),
'ExternalAuthOptions(clientId=foo, clientSecret=***, redirectUri=http://example.com)',
'ExternalAuthOptions(clientId=foo, clientSecret=***, redirectUri=http://example.com, scopes=[])',
);
});
test('asteriskCount', () {
expect(
options.toString(asteriskCount: 7),
'ExternalAuthOptions(clientId=foo, clientSecret=*******, redirectUri=http://example.com)',
'ExternalAuthOptions(clientId=foo, clientSecret=*******, redirectUri=http://example.com, scopes=[])',
);
});
});
@ -140,6 +142,7 @@ void main() {
'client_id': 'foo',
'client_secret': '<redacted>',
'redirect_uri': 'http://example.com',
'scopes': [],
},
);
});
@ -151,6 +154,7 @@ void main() {
'client_id': 'foo',
'client_secret': 'bar',
'redirect_uri': 'http://example.com',
'scopes': [],
},
);
});