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