external auth opts
This commit is contained in:
parent
c78dc37e34
commit
b660eef2db
6 changed files with 285 additions and 2 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
# 2.1.0
|
||||||
|
* Added `ExternalAuthOptions`.
|
||||||
|
|
||||||
# 2.0.4
|
# 2.0.4
|
||||||
* `successRedirect` was previously explicitly returning a `200`; remove this and allow the default `302`.
|
* `successRedirect` was previously explicitly returning a `200`; remove this and allow the default `302`.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
include: package:pedantic/analysis_options.yaml
|
||||||
analyzer:
|
analyzer:
|
||||||
strong-mode:
|
strong-mode:
|
||||||
implicit-casts: false
|
implicit-casts: false
|
|
@ -3,6 +3,7 @@ library angel_auth;
|
||||||
export 'src/middleware/require_auth.dart';
|
export 'src/middleware/require_auth.dart';
|
||||||
export 'src/strategies/strategies.dart';
|
export 'src/strategies/strategies.dart';
|
||||||
export 'src/auth_token.dart';
|
export 'src/auth_token.dart';
|
||||||
|
export 'src/configuration.dart';
|
||||||
export 'src/options.dart';
|
export 'src/options.dart';
|
||||||
export 'src/plugin.dart';
|
export 'src/plugin.dart';
|
||||||
export 'src/popup_page.dart';
|
export 'src/popup_page.dart';
|
||||||
|
|
116
lib/src/configuration.dart
Normal file
116
lib/src/configuration.dart
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import 'package:charcode/ascii.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:quiver_hashcode/hashcode.dart';
|
||||||
|
|
||||||
|
/// A common class containing parsing and validation logic for third-party authentication configuration.
|
||||||
|
class ExternalAuthOptions {
|
||||||
|
/// The user's identifier, otherwise known as an "application id".
|
||||||
|
final String clientId;
|
||||||
|
|
||||||
|
/// The user's secret, other known as an "application secret".
|
||||||
|
final String clientSecret;
|
||||||
|
|
||||||
|
/// The user's redirect URI.
|
||||||
|
final Uri redirectUri;
|
||||||
|
|
||||||
|
ExternalAuthOptions._(this.clientId, this.clientSecret, this.redirectUri) {
|
||||||
|
if (clientId == null) {
|
||||||
|
throw new ArgumentError.notNull('clientId');
|
||||||
|
} else if (clientSecret == null) {
|
||||||
|
throw new ArgumentError.notNull('clientSecret');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ExternalAuthOptions(
|
||||||
|
{@required String clientId,
|
||||||
|
@required String clientSecret,
|
||||||
|
@required redirectUri}) {
|
||||||
|
if (redirectUri is String) {
|
||||||
|
return new ExternalAuthOptions._(
|
||||||
|
clientId, clientSecret, Uri.parse(redirectUri));
|
||||||
|
} else if (redirectUri is Uri) {
|
||||||
|
return new ExternalAuthOptions._(clientId, clientSecret, redirectUri);
|
||||||
|
} else {
|
||||||
|
throw new ArgumentError.value(
|
||||||
|
redirectUri, 'redirectUri', 'must be a String or Uri');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a JSON-friendly representation of this object.
|
||||||
|
///
|
||||||
|
/// Parses the following fields:
|
||||||
|
/// * `client_id`
|
||||||
|
/// * `client_secret`
|
||||||
|
/// * `redirect_uri`
|
||||||
|
factory ExternalAuthOptions.fromMap(Map map) {
|
||||||
|
return new ExternalAuthOptions(
|
||||||
|
clientId: map['client_id'] as String,
|
||||||
|
clientSecret: map['client_secret'] as String,
|
||||||
|
redirectUri: map['redirect_uri'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hash3(clientId, clientSecret, redirectUri);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
other is ExternalAuthOptions &&
|
||||||
|
other.clientId == clientId &&
|
||||||
|
other.clientSecret == other.clientSecret &&
|
||||||
|
other.redirectUri == other.redirectUri;
|
||||||
|
|
||||||
|
/// Creates a copy of this object, with the specified changes.
|
||||||
|
ExternalAuthOptions copyWith(
|
||||||
|
{String clientId, String clientSecret, redirectUri}) {
|
||||||
|
return new ExternalAuthOptions(
|
||||||
|
clientId: clientId ?? this.clientId,
|
||||||
|
clientSecret: clientSecret ?? this.clientSecret,
|
||||||
|
redirectUri: redirectUri ?? this.redirectUri,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a JSON-friendly representation of this object.
|
||||||
|
///
|
||||||
|
/// Contains the following fields:
|
||||||
|
/// * `client_id`
|
||||||
|
/// * `client_secret`
|
||||||
|
/// * `redirect_uri`
|
||||||
|
///
|
||||||
|
/// If [obscureSecret] is `true` (default), then the [clientSecret] will
|
||||||
|
/// be replaced by the string `<redacted>`.
|
||||||
|
Map<String, String> toJson({bool obscureSecret = true}) {
|
||||||
|
return {
|
||||||
|
'client_id': clientId,
|
||||||
|
'client_secret': obscureSecret ? '<redacted>' : clientSecret,
|
||||||
|
'redirect_uri': redirectUri.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [String] representation of this object.
|
||||||
|
///
|
||||||
|
/// If [obscureText] is `true` (default), then the [clientSecret] will be
|
||||||
|
/// replaced by asterisks in the output.
|
||||||
|
///
|
||||||
|
/// If no [asteriskCount] is given, then the number of asterisks will equal the length of
|
||||||
|
/// the actual [clientSecret].
|
||||||
|
@override
|
||||||
|
String toString({bool obscureSecret = true, int asteriskCount}) {
|
||||||
|
String secret;
|
||||||
|
|
||||||
|
if (!obscureSecret) {
|
||||||
|
secret = clientSecret;
|
||||||
|
} else {
|
||||||
|
var codeUnits =
|
||||||
|
new List<int>.filled(asteriskCount ?? clientSecret.length, $asterisk);
|
||||||
|
secret = new String.fromCharCodes(codeUnits);
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = new StringBuffer('ExternalAuthOptions(');
|
||||||
|
b.write('clientId=$clientId');
|
||||||
|
b.write(', clientSecret=$secret');
|
||||||
|
b.write(', redirectUri=$redirectUri');
|
||||||
|
b.write(')');
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,20 @@
|
||||||
name: angel_auth
|
name: angel_auth
|
||||||
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
|
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
|
||||||
version: 2.0.4
|
version: 2.1.0
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_auth
|
homepage: https://github.com/angel-dart/angel_auth
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0-dev <3.0.0"
|
sdk: ">=2.0.0-dev <3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^2.0.0-alpha
|
angel_framework: ^2.0.0-alpha
|
||||||
|
charcode: ^1.0.0
|
||||||
crypto: ^2.0.0
|
crypto: ^2.0.0
|
||||||
http_parser: ^3.0.0
|
http_parser: ^3.0.0
|
||||||
|
meta: ^1.0.0
|
||||||
|
quiver_hashcode: ^2.0.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
http: ^0.11.0
|
http: ^0.12.0
|
||||||
io: ^0.3.2
|
io: ^0.3.2
|
||||||
logging: ^0.11.0
|
logging: ^0.11.0
|
||||||
|
pedantic: ^1.0.0
|
||||||
test: ^1.0.0
|
test: ^1.0.0
|
||||||
|
|
158
test/config_test.dart
Normal file
158
test/config_test.dart
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import 'package:angel_auth/angel_auth.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var options = new ExternalAuthOptions(
|
||||||
|
clientId: 'foo',
|
||||||
|
clientSecret: 'bar',
|
||||||
|
redirectUri: 'http://example.com',
|
||||||
|
);
|
||||||
|
|
||||||
|
test('parses uri', () {
|
||||||
|
expect(options.redirectUri, Uri(scheme: 'http', host: 'example.com'));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('copyWith', () {
|
||||||
|
test('empty produces exact copy', () {
|
||||||
|
expect(options.copyWith(), options);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('all fields', () {
|
||||||
|
expect(
|
||||||
|
options.copyWith(
|
||||||
|
clientId: 'hey',
|
||||||
|
clientSecret: 'hello',
|
||||||
|
redirectUri: 'https://yes.no',
|
||||||
|
),
|
||||||
|
new ExternalAuthOptions(
|
||||||
|
clientId: 'hey',
|
||||||
|
clientSecret: 'hello',
|
||||||
|
redirectUri: 'https://yes.no',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('not equal to original if different', () {
|
||||||
|
expect(options.copyWith(clientId: 'hey'), isNot(options));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('new()', () {
|
||||||
|
test('accepts uri', () {
|
||||||
|
expect(
|
||||||
|
new ExternalAuthOptions(
|
||||||
|
clientId: 'foo',
|
||||||
|
clientSecret: 'bar',
|
||||||
|
redirectUri: Uri.parse('http://example.com'),
|
||||||
|
),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('accepts string', () {
|
||||||
|
expect(
|
||||||
|
new ExternalAuthOptions(
|
||||||
|
clientId: 'foo',
|
||||||
|
clientSecret: 'bar',
|
||||||
|
redirectUri: 'http://example.com',
|
||||||
|
),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects invalid redirectUri', () {
|
||||||
|
expect(
|
||||||
|
() => new ExternalAuthOptions(
|
||||||
|
clientId: 'foo', clientSecret: 'bar', redirectUri: 24.5),
|
||||||
|
throwsArgumentError,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ensures id not null', () {
|
||||||
|
expect(
|
||||||
|
() => new ExternalAuthOptions(
|
||||||
|
clientId: null,
|
||||||
|
clientSecret: 'bar',
|
||||||
|
redirectUri: 'http://example.com'),
|
||||||
|
throwsArgumentError,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ensures secret not null', () {
|
||||||
|
expect(
|
||||||
|
() => new ExternalAuthOptions(
|
||||||
|
clientId: 'foo',
|
||||||
|
clientSecret: null,
|
||||||
|
redirectUri: 'http://example.com'),
|
||||||
|
throwsArgumentError,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('fromMap()', () {
|
||||||
|
test('rejects invalid map', () {
|
||||||
|
expect(
|
||||||
|
() => new ExternalAuthOptions.fromMap({'yes': 'no'}),
|
||||||
|
throwsArgumentError,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('accepts correct map', () {
|
||||||
|
expect(
|
||||||
|
new ExternalAuthOptions.fromMap({
|
||||||
|
'client_id': 'foo',
|
||||||
|
'client_secret': 'bar',
|
||||||
|
'redirect_uri': 'http://example.com',
|
||||||
|
}),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('toString()', () {
|
||||||
|
test('produces correct string', () {
|
||||||
|
expect(
|
||||||
|
options.toString(obscureSecret: false),
|
||||||
|
'ExternalAuthOptions(clientId=foo, clientSecret=bar, redirectUri=http://example.com)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('obscures secret', () {
|
||||||
|
expect(
|
||||||
|
options.toString(),
|
||||||
|
'ExternalAuthOptions(clientId=foo, clientSecret=***, redirectUri=http://example.com)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('asteriskCount', () {
|
||||||
|
expect(
|
||||||
|
options.toString(asteriskCount: 7),
|
||||||
|
'ExternalAuthOptions(clientId=foo, clientSecret=*******, redirectUri=http://example.com)',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('toJson()', () {
|
||||||
|
test('obscures secret', () {
|
||||||
|
expect(
|
||||||
|
options.toJson(),
|
||||||
|
{
|
||||||
|
'client_id': 'foo',
|
||||||
|
'client_secret': '<redacted>',
|
||||||
|
'redirect_uri': 'http://example.com',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('produces correct map', () {
|
||||||
|
expect(
|
||||||
|
options.toJson(obscureSecret: false),
|
||||||
|
{
|
||||||
|
'client_id': 'foo',
|
||||||
|
'client_secret': 'bar',
|
||||||
|
'redirect_uri': 'http://example.com',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue