Bump to 2.2.0
This commit is contained in:
parent
2d9630243f
commit
e960bdd49f
15 changed files with 135 additions and 131 deletions
|
@ -1,3 +1,7 @@
|
|||
# 2.2.0
|
||||
* Pass `client` to `exchangeAuthorizationCodeForToken`.
|
||||
* Apply `package:pedantic`.
|
||||
|
||||
# 2.1.0
|
||||
* Updates
|
||||
* Support `device_code` grants.
|
||||
|
|
12
README.md
12
README.md
|
@ -39,7 +39,7 @@ but that it can also verify its identity via a `client_secret`.
|
|||
|
||||
```dart
|
||||
class _Server extends AuthorizationServer<PseudoApplication, Map> {
|
||||
final Uuid _uuid = new Uuid();
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
@override
|
||||
FutureOr<PseudoApplication> findClient(String clientId) {
|
||||
|
@ -80,7 +80,7 @@ Future<AuthorizationCodeResponse> exchangeAuthCodeForAccessToken(
|
|||
String redirectUri,
|
||||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
return new AuthorizationCodeResponse('foo', refreshToken: 'bar');
|
||||
return AuthorizationCodeResponse('foo', refreshToken: 'bar');
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -137,7 +137,7 @@ Future requestAuthorizationCode(
|
|||
|
||||
// At this point, store `pkce.codeChallenge` and `pkce.codeChallengeMethod`,
|
||||
// so that when it's time to exchange the auth code for a token, we can
|
||||
// create a new [Pkce] object, and verify the client.
|
||||
// create a [Pkce] object, and verify the client.
|
||||
return await getAuthCodeSomehow(client, pkce.codeChallenge, pkce.codeChallengeMethod);
|
||||
}
|
||||
|
||||
|
@ -159,14 +159,14 @@ Future<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(
|
|||
var codeChallenge = await getTheChallenge();
|
||||
var codeChallengeMethod = await getTheChallengeMethod();
|
||||
|
||||
// Make a new [Pkce] object.
|
||||
var pkce = new Pkce(codeChallengeMethod, codeChallenge);
|
||||
// Make a [Pkce] object.
|
||||
var pkce = Pkce(codeChallengeMethod, codeChallenge);
|
||||
|
||||
// Call `validate`. If the client is invalid, it throws an OAuth2 exception.
|
||||
pkce.validate(codeVerifier);
|
||||
|
||||
// If we reach here, we know that the `code_verifier` was valid,
|
||||
// so we can return our authorization token as per usual.
|
||||
return new AuthorizationTokenResponse('...');
|
||||
return AuthorizationTokenResponse('...');
|
||||
}
|
||||
```
|
|
@ -1,3 +1,8 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
linter:
|
||||
rules:
|
||||
- unnecessary_const
|
||||
- unnecessary_new
|
|
@ -4,9 +4,9 @@ import 'package:angel_framework/angel_framework.dart';
|
|||
import 'package:angel_oauth2/angel_oauth2.dart';
|
||||
|
||||
main() async {
|
||||
var app = new Angel();
|
||||
var oauth2 = new _ExampleAuthorizationServer();
|
||||
var _rgxBearer = new RegExp(r'^[Bb]earer ([^\n\s]+)$');
|
||||
var app = Angel();
|
||||
var oauth2 = _ExampleAuthorizationServer();
|
||||
var _rgxBearer = RegExp(r'^[Bb]earer ([^\n\s]+)$');
|
||||
|
||||
app.group('/auth', (router) {
|
||||
router
|
||||
|
@ -40,13 +40,13 @@ class _ExampleAuthorizationServer
|
|||
@override
|
||||
FutureOr<ThirdPartyApp> findClient(String clientId) {
|
||||
// TODO: Add your code to find the app associated with a client ID.
|
||||
throw new UnimplementedError();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<bool> verifyClient(ThirdPartyApp client, String clientSecret) {
|
||||
// TODO: Add your code to verify a client secret, if given one.
|
||||
throw new UnimplementedError();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -58,17 +58,18 @@ class _ExampleAuthorizationServer
|
|||
RequestContext req,
|
||||
ResponseContext res) {
|
||||
// TODO: In many cases, here you will render a view displaying to the user which scopes are being requested.
|
||||
throw new UnimplementedError();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(
|
||||
ThirdPartyApp client,
|
||||
String authCode,
|
||||
String redirectUri,
|
||||
RequestContext req,
|
||||
ResponseContext res) {
|
||||
// TODO: Here, you'll convert the auth code into a full-fledged token.
|
||||
// You might have the auth code stored in a database somewhere.
|
||||
throw new UnimplementedError();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ class ErrorResponse {
|
|||
/// The authorization request is still pending as the end user hasn't
|
||||
/// yet completed the user interaction steps (Section 3.3). The
|
||||
/// client SHOULD repeat the Access Token Request to the token
|
||||
/// endpoint (a process known as polling). Before each new request
|
||||
/// endpoint (a process known as polling). Before each request
|
||||
/// the client MUST wait at least the number of seconds specified by
|
||||
/// the "interval" parameter of the Device Authorization Response (see
|
||||
/// Section 3.2), or 5 seconds if none was provided, and respect any
|
||||
|
@ -66,7 +66,7 @@ class ErrorResponse {
|
|||
static const String slowDown = 'slow_down';
|
||||
|
||||
/// The "device_code" has expired and the device flow authorization
|
||||
/// session has concluded. The client MAY commence a new Device
|
||||
/// session has concluded. The client MAY commence a Device
|
||||
/// Authorization Request but SHOULD wait for user interaction before
|
||||
/// restarting to avoid unnecessary polling.
|
||||
static const String expiredToken = 'expired_token';
|
||||
|
|
|
@ -23,20 +23,20 @@ class Pkce {
|
|||
data['code_challenge_method']?.toString() ?? 'plain';
|
||||
|
||||
if (codeChallengeMethod != 'plain' && codeChallengeMethod != 's256') {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
throw AuthorizationException(ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
"The `code_challenge_method` parameter must be either 'plain' or 's256'.",
|
||||
state,
|
||||
uri: uri));
|
||||
} else if (codeChallenge?.isNotEmpty != true) {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
throw AuthorizationException(ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
'Missing `code_challenge` parameter.',
|
||||
state,
|
||||
uri: uri));
|
||||
}
|
||||
|
||||
return new Pkce(codeChallengeMethod, codeChallenge);
|
||||
return Pkce(codeChallengeMethod, codeChallenge);
|
||||
}
|
||||
|
||||
/// Returns [true] if the [codeChallengeMethod] is `plain`.
|
||||
|
@ -57,8 +57,8 @@ class Pkce {
|
|||
}
|
||||
|
||||
if (foreignChallenge != codeChallenge) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(ErrorResponse.invalidGrant,
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(ErrorResponse.invalidGrant,
|
||||
"The given `code_verifier` parameter is invalid.", state,
|
||||
uri: uri),
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ typedef FutureOr<AuthorizationTokenResponse> ExtensionGrant(
|
|||
RequestContext req, ResponseContext res);
|
||||
|
||||
Future<String> _getParam(RequestContext req, String name, String state,
|
||||
{bool body: false, bool throwIfEmpty: true}) async {
|
||||
{bool body = false, bool throwIfEmpty = true}) async {
|
||||
Map<String, dynamic> data;
|
||||
|
||||
if (body == true) {
|
||||
|
@ -23,8 +23,8 @@ Future<String> _getParam(RequestContext req, String name, String state,
|
|||
var value = data.containsKey(name) ? data[name]?.toString() : null;
|
||||
|
||||
if (value?.isNotEmpty != true && throwIfEmpty) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
'Missing required parameter "$name".',
|
||||
state,
|
||||
|
@ -37,7 +37,7 @@ Future<String> _getParam(RequestContext req, String name, String state,
|
|||
}
|
||||
|
||||
Future<Iterable<String>> _getScopes(RequestContext req,
|
||||
{bool body: false}) async {
|
||||
{bool body = false}) async {
|
||||
Map<String, dynamic> data;
|
||||
|
||||
if (body == true) {
|
||||
|
@ -67,23 +67,19 @@ abstract class AuthorizationServer<Client, User> {
|
|||
|
||||
/// Retrieves the PKCE `code_verifier` parameter from a [RequestContext], or throws.
|
||||
Future<String> getPkceCodeVerifier(RequestContext req,
|
||||
{bool body: true, String state, Uri uri}) async {
|
||||
{bool body = true, String state, Uri uri}) async {
|
||||
var data = body
|
||||
? await req.parseBody().then((_) => req.bodyAsMap)
|
||||
: req.queryParameters;
|
||||
var codeVerifier = data['code_verifier'];
|
||||
|
||||
if (codeVerifier == null) {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
"Missing `code_verifier` parameter.",
|
||||
state,
|
||||
throw AuthorizationException(ErrorResponse(ErrorResponse.invalidRequest,
|
||||
"Missing `code_verifier` parameter.", state,
|
||||
uri: uri));
|
||||
} else if (codeVerifier is! String) {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
"The `code_verifier` parameter must be a string.",
|
||||
state,
|
||||
throw AuthorizationException(ErrorResponse(ErrorResponse.invalidRequest,
|
||||
"The `code_verifier` parameter must be a string.", state,
|
||||
uri: uri));
|
||||
} else {
|
||||
return codeVerifier as String;
|
||||
|
@ -100,8 +96,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
String state,
|
||||
RequestContext req,
|
||||
ResponseContext res) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Authorization code grants are not supported.',
|
||||
state,
|
||||
|
@ -121,8 +117,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
String state,
|
||||
RequestContext req,
|
||||
ResponseContext res) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Authorization code grants are not supported.',
|
||||
state,
|
||||
|
@ -133,12 +129,13 @@ abstract class AuthorizationServer<Client, User> {
|
|||
|
||||
/// Exchanges an authorization code for an authorization token.
|
||||
FutureOr<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(
|
||||
Client client,
|
||||
String authCode,
|
||||
String redirectUri,
|
||||
RequestContext req,
|
||||
ResponseContext res) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Authorization code grants are not supported.',
|
||||
req.uri.queryParameters['state'] ?? '',
|
||||
|
@ -155,8 +152,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Refreshing authorization tokens is not supported.',
|
||||
body['state']?.toString() ?? '',
|
||||
|
@ -174,8 +171,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Resource owner password credentials grants are not supported.',
|
||||
body['state']?.toString() ?? '',
|
||||
|
@ -188,8 +185,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
FutureOr<AuthorizationTokenResponse> clientCredentialsGrant(
|
||||
Client client, RequestContext req, ResponseContext res) async {
|
||||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Client credentials grants are not supported.',
|
||||
body['state']?.toString() ?? '',
|
||||
|
@ -202,8 +199,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
FutureOr<DeviceCodeResponse> requestDeviceCode(Client client,
|
||||
Iterable<String> scopes, RequestContext req, ResponseContext res) async {
|
||||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Device code grants are not supported.',
|
||||
body['state']?.toString() ?? '',
|
||||
|
@ -220,8 +217,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unsupportedResponseType,
|
||||
'Device code grants are not supported.',
|
||||
body['state']?.toString() ?? '',
|
||||
|
@ -241,19 +238,18 @@ abstract class AuthorizationServer<Client, User> {
|
|||
var responseType = await _getParam(req, 'response_type', state);
|
||||
|
||||
req.container.registerLazySingleton<Pkce>((_) {
|
||||
return new Pkce.fromJson(req.queryParameters, state: state);
|
||||
return Pkce.fromJson(req.queryParameters, state: state);
|
||||
});
|
||||
|
||||
if (responseType == 'code') {
|
||||
// Ensure client ID
|
||||
// TODO: Handle confidential clients
|
||||
var clientId = await _getParam(req, 'client_id', state);
|
||||
|
||||
// Find client
|
||||
var client = await findClient(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
throw AuthorizationException(ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Unknown client "$clientId".',
|
||||
state,
|
||||
|
@ -275,7 +271,7 @@ abstract class AuthorizationServer<Client, User> {
|
|||
var client = await findClient(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
throw AuthorizationException(ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Unknown client "$clientId".',
|
||||
state,
|
||||
|
@ -307,8 +303,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
if (token.scope != null)
|
||||
queryParameters['scope'] = token.scope.join(' ');
|
||||
|
||||
var fragment = queryParameters.keys
|
||||
.fold<StringBuffer>(new StringBuffer(), (buf, k) {
|
||||
var fragment =
|
||||
queryParameters.keys.fold<StringBuffer>(StringBuffer(), (buf, k) {
|
||||
if (buf.isNotEmpty) buf.write('&');
|
||||
return buf
|
||||
..write(
|
||||
|
@ -320,8 +316,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
res.redirect(target.toString());
|
||||
return false;
|
||||
} on FormatException {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
'Invalid URI provided as "redirect_uri" parameter',
|
||||
state,
|
||||
|
@ -330,8 +326,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
}
|
||||
}
|
||||
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
'Invalid or no "response_type" parameter provided',
|
||||
state,
|
||||
|
@ -340,8 +336,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
} on AngelHttpException {
|
||||
rethrow;
|
||||
} catch (e, st) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.serverError,
|
||||
_internalServerError,
|
||||
state,
|
||||
|
@ -353,8 +349,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
}
|
||||
}
|
||||
|
||||
static final RegExp _rgxBasic = new RegExp(r'Basic ([^$]+)');
|
||||
static final RegExp _rgxBasicAuth = new RegExp(r'([^:]*):([^$]*)');
|
||||
static final RegExp _rgxBasic = RegExp(r'Basic ([^$]+)');
|
||||
static final RegExp _rgxBasicAuth = RegExp(r'([^:]*):([^$]*)');
|
||||
|
||||
/// A request handler that either exchanges authorization codes for authorization tokens,
|
||||
/// or refreshes authorization tokens.
|
||||
|
@ -369,7 +365,7 @@ abstract class AuthorizationServer<Client, User> {
|
|||
state = body['state']?.toString() ?? '';
|
||||
|
||||
req.container.registerLazySingleton<Pkce>((_) {
|
||||
return new Pkce.fromJson(req.bodyAsMap, state: state);
|
||||
return Pkce.fromJson(req.bodyAsMap, state: state);
|
||||
});
|
||||
|
||||
var grantType = await _getParam(req, 'grant_type', state,
|
||||
|
@ -383,12 +379,12 @@ abstract class AuthorizationServer<Client, User> {
|
|||
|
||||
if (match != null) {
|
||||
match = _rgxBasicAuth
|
||||
.firstMatch(new String.fromCharCodes(base64Url.decode(match[1])));
|
||||
.firstMatch(String.fromCharCodes(base64Url.decode(match[1])));
|
||||
}
|
||||
|
||||
if (match == null) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Invalid or no "Authorization" header.',
|
||||
state,
|
||||
|
@ -400,8 +396,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
client = await findClient(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Invalid "client_id" parameter.',
|
||||
state,
|
||||
|
@ -411,8 +407,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
}
|
||||
|
||||
if (!await verifyClient(client, clientSecret)) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Invalid "client_secret" parameter.',
|
||||
state,
|
||||
|
@ -428,7 +424,7 @@ abstract class AuthorizationServer<Client, User> {
|
|||
var redirectUri =
|
||||
await _getParam(req, 'redirect_uri', state, body: true);
|
||||
response = await exchangeAuthorizationCodeForToken(
|
||||
code, redirectUri, req, res);
|
||||
client, code, redirectUri, req, res);
|
||||
} else if (grantType == 'refresh_token') {
|
||||
var refreshToken =
|
||||
await _getParam(req, 'refresh_token', state, body: true);
|
||||
|
@ -446,7 +442,7 @@ abstract class AuthorizationServer<Client, User> {
|
|||
|
||||
if (response.refreshToken != null) {
|
||||
// Remove refresh token
|
||||
response = new AuthorizationTokenResponse(
|
||||
response = AuthorizationTokenResponse(
|
||||
response.accessToken,
|
||||
expiresIn: response.expiresIn,
|
||||
scope: response.scope,
|
||||
|
@ -460,8 +456,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
client = await findClient(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Invalid "client_id" parameter.',
|
||||
state,
|
||||
|
@ -479,8 +475,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
client = await findClient(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.unauthorizedClient,
|
||||
'Invalid "client_id" parameter.',
|
||||
state,
|
||||
|
@ -499,8 +495,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
..addAll(response.toJson());
|
||||
}
|
||||
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.invalidRequest,
|
||||
'Invalid or no "grant_type" parameter provided',
|
||||
state,
|
||||
|
@ -510,8 +506,8 @@ abstract class AuthorizationServer<Client, User> {
|
|||
} on AngelHttpException {
|
||||
rethrow;
|
||||
} catch (e, st) {
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.serverError,
|
||||
_internalServerError,
|
||||
state,
|
||||
|
|
|
@ -2,7 +2,7 @@ name: angel_oauth2
|
|||
author: Tobe O <thosakwe@gmail.com>
|
||||
description: A class containing handlers that can be used within Angel to build a spec-compliant OAuth 2.0 server.
|
||||
homepage: https://github.com/angel-dart/oauth2.git
|
||||
version: 2.1.0
|
||||
version: 2.2.0
|
||||
environment:
|
||||
sdk: ">=2.0.0-dev <3.0.0"
|
||||
dependencies:
|
||||
|
@ -14,5 +14,6 @@ dev_dependencies:
|
|||
angel_test: ^2.0.0-alpha
|
||||
logging:
|
||||
oauth2: ^1.0.0
|
||||
pedantic: ^1.0.0
|
||||
test: ^1.0.0
|
||||
uuid: ^1.0.0
|
||||
|
|
|
@ -17,11 +17,11 @@ main() {
|
|||
TestClient testClient;
|
||||
|
||||
setUp(() async {
|
||||
app = new Angel();
|
||||
app = Angel();
|
||||
app.configuration['properties'] = app.configuration;
|
||||
app.container.registerSingleton(new AuthCodes());
|
||||
app.container.registerSingleton(AuthCodes());
|
||||
|
||||
var server = new _Server();
|
||||
var server = _Server();
|
||||
|
||||
app.group('/oauth2', (router) {
|
||||
router
|
||||
|
@ -29,14 +29,14 @@ main() {
|
|||
..post('/token', server.tokenEndpoint);
|
||||
});
|
||||
|
||||
app.logger = new Logger('angel')
|
||||
app.logger = Logger('angel')
|
||||
..onRecord.listen((rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||
});
|
||||
|
||||
var http = new AngelHttp(app);
|
||||
var http = AngelHttp(app);
|
||||
var s = await http.startServer();
|
||||
var url = 'http://${s.address.address}:${s.port}';
|
||||
authorizationEndpoint = Uri.parse('$url/oauth2/authorize');
|
||||
|
@ -52,7 +52,7 @@ main() {
|
|||
|
||||
group('auth code', () {
|
||||
oauth2.AuthorizationCodeGrant createGrant() =>
|
||||
new oauth2.AuthorizationCodeGrant(
|
||||
oauth2.AuthorizationCodeGrant(
|
||||
pseudoApplication.id,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
|
@ -119,7 +119,7 @@ main() {
|
|||
}
|
||||
|
||||
class _Server extends AuthorizationServer<PseudoApplication, Map> {
|
||||
final Uuid _uuid = new Uuid();
|
||||
final Uuid _uuid = Uuid();
|
||||
|
||||
@override
|
||||
FutureOr<PseudoApplication> findClient(String clientId) {
|
||||
|
@ -155,6 +155,7 @@ class _Server extends AuthorizationServer<PseudoApplication, Map> {
|
|||
|
||||
@override
|
||||
Future<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(
|
||||
PseudoApplication client,
|
||||
String authCode,
|
||||
String redirectUri,
|
||||
RequestContext req,
|
||||
|
@ -162,7 +163,7 @@ class _Server extends AuthorizationServer<PseudoApplication, Map> {
|
|||
var authCodes = req.container.make<AuthCodes>();
|
||||
var state = authCodes[authCode];
|
||||
var refreshToken = state == 'can_refresh' ? '${authCode}_refresh' : null;
|
||||
return new AuthorizationTokenResponse('${authCode}_access',
|
||||
return AuthorizationTokenResponse('${authCode}_access',
|
||||
refreshToken: refreshToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ main() {
|
|||
TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
var app = new Angel();
|
||||
var oauth2 = new _AuthorizationServer();
|
||||
var app = Angel();
|
||||
var oauth2 = _AuthorizationServer();
|
||||
|
||||
app.group('/oauth2', (router) {
|
||||
router
|
||||
|
@ -47,7 +47,7 @@ main() {
|
|||
allOf(
|
||||
hasStatus(200),
|
||||
hasContentType('application/json'),
|
||||
hasValidBody(new Validator({
|
||||
hasValidBody(Validator({
|
||||
'token_type': equals('bearer'),
|
||||
'access_token': equals('foo'),
|
||||
})),
|
||||
|
@ -101,6 +101,6 @@ class _AuthorizationServer
|
|||
@override
|
||||
Future<AuthorizationTokenResponse> clientCredentialsGrant(
|
||||
PseudoApplication client, RequestContext req, ResponseContext res) async {
|
||||
return new AuthorizationTokenResponse('foo');
|
||||
return AuthorizationTokenResponse('foo');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const PseudoApplication pseudoApplication =
|
||||
const PseudoApplication('foo', 'bar', 'http://foo.bar/baz');
|
||||
PseudoApplication('foo', 'bar', 'http://foo.bar/baz');
|
||||
|
||||
class PseudoApplication {
|
||||
final String id, secret, redirectUri;
|
||||
|
@ -7,10 +7,10 @@ class PseudoApplication {
|
|||
const PseudoApplication(this.id, this.secret, this.redirectUri);
|
||||
}
|
||||
|
||||
const List<PseudoUser> pseudoUsers = const [
|
||||
const PseudoUser(username: 'foo', password: 'bar'),
|
||||
const PseudoUser(username: 'michael', password: 'jackson'),
|
||||
const PseudoUser(username: 'jon', password: 'skeet'),
|
||||
const List<PseudoUser> pseudoUsers = [
|
||||
PseudoUser(username: 'foo', password: 'bar'),
|
||||
PseudoUser(username: 'michael', password: 'jackson'),
|
||||
PseudoUser(username: 'jon', password: 'skeet'),
|
||||
];
|
||||
|
||||
class PseudoUser {
|
||||
|
|
|
@ -11,8 +11,8 @@ main() {
|
|||
TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
var app = new Angel();
|
||||
var oauth2 = new _AuthorizationServer();
|
||||
var app = Angel();
|
||||
var oauth2 = _AuthorizationServer();
|
||||
|
||||
app.group('/oauth2', (router) {
|
||||
router
|
||||
|
@ -20,7 +20,7 @@ main() {
|
|||
..post('/token', oauth2.tokenEndpoint);
|
||||
});
|
||||
|
||||
app.logger = new Logger('angel_oauth2')
|
||||
app.logger = Logger('angel_oauth2')
|
||||
..onRecord.listen((rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
|
@ -145,7 +145,7 @@ class _AuthorizationServer
|
|||
@override
|
||||
FutureOr<DeviceCodeResponse> requestDeviceCode(PseudoApplication client,
|
||||
Iterable<String> scopes, RequestContext req, ResponseContext res) {
|
||||
return new DeviceCodeResponse(
|
||||
return DeviceCodeResponse(
|
||||
'foo',
|
||||
'bar',
|
||||
Uri.parse('https://regiostech.com')
|
||||
|
@ -161,12 +161,12 @@ class _AuthorizationServer
|
|||
RequestContext req,
|
||||
ResponseContext res) {
|
||||
if (deviceCode == 'brute') {
|
||||
throw new AuthorizationException(new ErrorResponse(
|
||||
throw AuthorizationException(ErrorResponse(
|
||||
ErrorResponse.slowDown,
|
||||
"Ho, brother! Ho, whoa, whoa, whoa now! You got too much dip on your chip!",
|
||||
state));
|
||||
}
|
||||
|
||||
return new AuthorizationTokenResponse('foo');
|
||||
return AuthorizationTokenResponse('foo');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ main() {
|
|||
TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
var app = new Angel();
|
||||
var oauth2 = new _AuthorizationServer();
|
||||
var app = Angel();
|
||||
var oauth2 = _AuthorizationServer();
|
||||
|
||||
app.group('/oauth2', (router) {
|
||||
router
|
||||
|
@ -65,6 +65,6 @@ class _AuthorizationServer
|
|||
String state,
|
||||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
return new AuthorizationTokenResponse('foo');
|
||||
return AuthorizationTokenResponse('foo');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ main() {
|
|||
Uri tokenEndpoint;
|
||||
|
||||
setUp(() async {
|
||||
app = new Angel();
|
||||
var auth = new _AuthorizationServer();
|
||||
app = Angel();
|
||||
var auth = _AuthorizationServer();
|
||||
|
||||
app.group('/oauth2', (router) {
|
||||
router
|
||||
|
@ -25,9 +25,9 @@ main() {
|
|||
res.json(e.toJson());
|
||||
};
|
||||
|
||||
app.logger = new Logger('password_test')..onRecord.listen(print);
|
||||
app.logger = Logger('password_test')..onRecord.listen(print);
|
||||
|
||||
var http = new AngelHttp(app);
|
||||
var http = AngelHttp(app);
|
||||
var server = await http.startServer();
|
||||
var url = 'http://${server.address.address}:${server.port}';
|
||||
tokenEndpoint = Uri.parse('$url/oauth2/token');
|
||||
|
@ -61,7 +61,7 @@ main() {
|
|||
secret: 'bar',
|
||||
);
|
||||
|
||||
throw new StateError('should fail');
|
||||
throw StateError('should fail');
|
||||
} on oauth2.AuthorizationException catch (e) {
|
||||
expect(e.error, ErrorResponse.accessDenied);
|
||||
} finally {
|
||||
|
@ -105,7 +105,7 @@ class _AuthorizationServer
|
|||
Iterable<String> scopes,
|
||||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
return new AuthorizationTokenResponse('baz', refreshToken: 'bar');
|
||||
return AuthorizationTokenResponse('baz', refreshToken: 'bar');
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -122,8 +122,8 @@ class _AuthorizationServer
|
|||
|
||||
if (user == null) {
|
||||
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
||||
throw new AuthorizationException(
|
||||
new ErrorResponse(
|
||||
throw AuthorizationException(
|
||||
ErrorResponse(
|
||||
ErrorResponse.accessDenied,
|
||||
'Invalid username or password.',
|
||||
body['state']?.toString() ?? '',
|
||||
|
@ -132,6 +132,6 @@ class _AuthorizationServer
|
|||
);
|
||||
}
|
||||
|
||||
return new AuthorizationTokenResponse('foo', refreshToken: 'bar');
|
||||
return AuthorizationTokenResponse('foo', refreshToken: 'bar');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,21 +6,19 @@ import 'package:angel_framework/http.dart';
|
|||
import 'package:angel_oauth2/angel_oauth2.dart';
|
||||
import 'package:angel_test/angel_test.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:oauth2/oauth2.dart' as oauth2;
|
||||
import 'package:test/test.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
Angel app;
|
||||
Uri authorizationEndpoint, tokenEndpoint, redirectUri;
|
||||
Uri authorizationEndpoint, tokenEndpoint;
|
||||
TestClient testClient;
|
||||
|
||||
setUp(() async {
|
||||
app = new Angel();
|
||||
app.container.registerSingleton(new AuthCodes());
|
||||
app = Angel();
|
||||
app.container.registerSingleton(AuthCodes());
|
||||
|
||||
var server = new _Server();
|
||||
var server = _Server();
|
||||
|
||||
app.group('/oauth2', (router) {
|
||||
router
|
||||
|
@ -28,19 +26,18 @@ main() {
|
|||
..post('/token', server.tokenEndpoint);
|
||||
});
|
||||
|
||||
app.logger = new Logger('angel')
|
||||
app.logger = Logger('angel')
|
||||
..onRecord.listen((rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||
});
|
||||
|
||||
var http = new AngelHttp(app);
|
||||
var http = AngelHttp(app);
|
||||
var s = await http.startServer();
|
||||
var url = 'http://${s.address.address}:${s.port}';
|
||||
authorizationEndpoint = Uri.parse('$url/oauth2/authorize');
|
||||
tokenEndpoint = Uri.parse('$url/oauth2/token');
|
||||
redirectUri = Uri.parse('http://foo.bar/baz');
|
||||
|
||||
testClient = await connectTo(app);
|
||||
});
|
||||
|
@ -228,8 +225,6 @@ main() {
|
|||
}
|
||||
|
||||
class _Server extends AuthorizationServer<PseudoApplication, Map> {
|
||||
final Uuid _uuid = new Uuid();
|
||||
|
||||
@override
|
||||
FutureOr<PseudoApplication> findClient(String clientId) {
|
||||
return pseudoApplication;
|
||||
|
@ -255,14 +250,15 @@ class _Server extends AuthorizationServer<PseudoApplication, Map> {
|
|||
|
||||
@override
|
||||
Future<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(
|
||||
PseudoApplication client,
|
||||
String authCode,
|
||||
String redirectUri,
|
||||
RequestContext req,
|
||||
ResponseContext res) async {
|
||||
var codeVerifier = await getPkceCodeVerifier(req);
|
||||
var pkce = new Pkce('plain', 'hello');
|
||||
var pkce = Pkce('plain', 'hello');
|
||||
pkce.validate(codeVerifier);
|
||||
return new AuthorizationTokenResponse('yes');
|
||||
return AuthorizationTokenResponse('yes');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue