platform/packages/oauth2/lib/src/pkce.dart

74 lines
2.5 KiB
Dart
Raw Normal View History

2018-12-15 07:45:40 +00:00
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;
2018-12-15 07:45:40 +00:00
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}) {
2018-12-15 07:45:40 +00:00
var codeChallenge = data['code_challenge']?.toString();
var codeChallengeMethod =
data['code_challenge_method']?.toString() ?? 'plain';
if (codeChallengeMethod != 'plain' && codeChallengeMethod != 's256') {
2019-05-02 07:28:38 +00:00
throw AuthorizationException(ErrorResponse(
2018-12-15 07:45:40 +00:00
ErrorResponse.invalidRequest,
"The `code_challenge_method` parameter must be either 'plain' or 's256'.",
state,
uri: uri));
} else if (codeChallenge?.isNotEmpty != true) {
2019-05-02 07:28:48 +00:00
throw AuthorizationException(ErrorResponse(ErrorResponse.invalidRequest,
'Missing `code_challenge` parameter.', state,
2018-12-15 07:45:40 +00:00
uri: uri));
}
2019-05-02 07:28:38 +00:00
return Pkce(codeChallengeMethod, codeChallenge);
2018-12-15 07:45:40 +00:00
}
/// 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}) {
2018-12-15 07:45:40 +00:00
String foreignChallenge;
if (isS256) {
foreignChallenge =
2018-12-15 08:39:04 +00:00
base64Url.encode(sha256.convert(ascii.encode(codeVerifier)).bytes);
2018-12-15 07:45:40 +00:00
} else {
2018-12-15 08:39:04 +00:00
foreignChallenge = codeVerifier;
2018-12-15 07:45:40 +00:00
}
if (foreignChallenge != codeChallenge) {
2019-05-02 07:28:38 +00:00
throw AuthorizationException(
ErrorResponse(ErrorResponse.invalidGrant,
'The given `code_verifier` parameter is invalid.', state,
2018-12-15 07:45:40 +00:00
uri: uri),
);
}
}
2018-12-15 07:48:34 +00:00
/// Creates a JSON-serializable representation of this instance.
Map<String, dynamic> toJson() {
return {
'code_challenge': codeChallenge,
'code_challenge_method': codeChallengeMethod
};
}
2018-12-15 07:45:40 +00:00
}