add pkce class

This commit is contained in:
Tobe O 2018-12-15 02:45:40 -05:00
parent d1213e779a
commit 71611f7f6a
5 changed files with 76 additions and 4 deletions

View file

@ -11,7 +11,7 @@ In your `pubspec.yaml`:
```yaml
dependencies:
angel_oauth2: ^1.0.0
angel_oauth2: ^2.0.0
```
# Usage
@ -49,7 +49,7 @@ class _Server extends AuthorizationServer<PseudoApplication, Map> {
```
Next, write some logic to be executed whenever a user visits the
authorization endpoint. In most cases, you will want to show a dialog:
authorization endpoint. In many cases, you will want to show a dialog:
```dart
@override
@ -99,7 +99,7 @@ The following are available, not including authorization code grant support (men
* `implicitGrant`
* `resourceOwnerPasswordCredentialsGrant`
* `clientCredentialsGrant`
* `deviceCode`
* `deviceCodeGrant`
Read the [OAuth2 specification](https://tools.ietf.org/html/rfc6749)
for in-depth information on each grant type.

View file

@ -1,4 +1,5 @@
export 'src/exception.dart';
export 'src/pkce.dart';
export 'src/response.dart';
export 'src/server.dart';
export 'src/token_type.dart';
export 'src/token_type.dart';

View file

@ -28,6 +28,9 @@ class ErrorResponse {
/// The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.
static const String invalidRequest = 'invalid_request';
/// The `code_verifier` given by the client does not match the expected value.
static const String invalidGrant = 'invalid_grant';
/// The client is not authorized to request an authorization code using this method.
static const String unauthorizedClient = 'unauthorized_client';

67
lib/src/pkce.dart Normal file
View file

@ -0,0 +1,67 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'exception.dart';
/// A class that facilitates verification of challenges for
/// [Proof Key for Code Exchange](https://oauth.net/2/pkce/).
class Pkce {
/// A [String] defining how to handle the [codeChallenge].
final String codeChallengeMethod;
/// The proof key that is used to secure public clients.
final String codeChallenge;
Pkce(this.codeChallengeMethod, this.codeChallenge) {
assert(codeChallengeMethod == 'plain' || codeChallengeMethod == 's256',
"The `code_challenge_method` parameter must be either 'plain' or 's256'.");
}
/// Attempts to parse a [codeChallenge] and [codeChallengeMethod] from a [Map].
factory Pkce.fromJson(Map data, {String state, Uri uri}) {
var codeChallenge = data['code_challenge']?.toString();
var codeChallengeMethod =
data['code_challenge_method']?.toString() ?? 'plain';
if (codeChallengeMethod != 'plain' && codeChallengeMethod != 's256') {
throw new AuthorizationException(new 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(
ErrorResponse.invalidRequest,
'Missing `code_challenge` parameter.',
state,
uri: uri));
}
return new Pkce(codeChallengeMethod, codeChallenge);
}
/// Returns [true] if the [codeChallengeMethod] is `plain`.
bool get isPlain => codeChallengeMethod == 'plain';
/// Returns [true] if the [codeChallengeMethod] is `s256`.
bool get isS256 => codeChallengeMethod == 's256';
/// Determines if a given [codeVerifier] is valid.
void validate(String codeVerifier, {String state, Uri uri}) {
String foreignChallenge;
if (isS256) {
foreignChallenge =
base64Url.encode(sha256.convert(ascii.encode(codeChallenge)).bytes);
} else {
foreignChallenge = codeChallenge;
}
if (foreignChallenge != codeChallenge) {
throw new AuthorizationException(
new ErrorResponse(ErrorResponse.invalidGrant,
"The given `code_verifier` parameter is invalid.", state,
uri: uri),
);
}
}
}

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:angel_framework/angel_framework.dart';
import 'exception.dart';
import 'pkce.dart';
import 'response.dart';
import 'token_type.dart';