allow code verifier to be passed in constructor of AuthorizationCodeGrant (#92)
* allow code verifier to be passed in constructor
* update changelog and bump pubspec.yaml version
* Update lib/src/authorization_code_grant.dart
update docs for codeVerifier
Co-authored-by: Jonas Finnemann Jensen <jopsen@gmail.com>
Co-authored-by: Joseph Lejtman <jlejtman@clarityinnovates.com>
Co-authored-by: Jonas Finnemann Jensen <jopsen@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28cc6e4..3bb8c5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 1.6.3
+
+* Added optional `codeVerifier` parameter to `AuthorizationCodeGrant` constructor.
+
# 1.6.2-dev
# 1.6.1
diff --git a/lib/src/authorization_code_grant.dart b/lib/src/authorization_code_grant.dart
index c04bc0b..963e5b2 100644
--- a/lib/src/authorization_code_grant.dart
+++ b/lib/src/authorization_code_grant.dart
@@ -107,8 +107,8 @@
static const String _charset =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
- /// The generated PKCE code verifier
- String _codeVerifier;
+ /// The PKCE code verifier. Will be generated if one is not provided in the constructor.
+ final String _codeVerifier;
/// Creates a new grant.
///
@@ -126,6 +126,12 @@
/// [onCredentialsRefreshed] will be called by the constructed [Client]
/// whenever the credentials are refreshed.
///
+ /// [codeVerifier] String to be used as PKCE code verifier. If none is provided a
+ /// random codeVerifier will be generated.
+ /// The codeVerifier must meet requirements specified in [RFC 7636].
+ ///
+ /// [RFC 7636]: https://tools.ietf.org/html/rfc7636#section-4.1
+ ///
/// The scope strings will be separated by the provided [delimiter]. This
/// defaults to `" "`, the OAuth2 standard, but some APIs (such as Facebook's)
/// use non-standard delimiters.
@@ -147,12 +153,14 @@
http.Client httpClient,
CredentialsRefreshedCallback onCredentialsRefreshed,
Map<String, dynamic> Function(MediaType contentType, String body)
- getParameters})
+ getParameters,
+ String codeVerifier})
: _basicAuth = basicAuth,
_httpClient = httpClient ?? http.Client(),
_delimiter = delimiter ?? ' ',
_getParameters = getParameters ?? parseJsonParameters,
- _onCredentialsRefreshed = onCredentialsRefreshed;
+ _onCredentialsRefreshed = onCredentialsRefreshed,
+ _codeVerifier = codeVerifier ?? _createCodeVerifier();
/// Returns the URL to which the resource owner should be redirected to
/// authorize this client.
@@ -186,7 +194,6 @@
scopes = scopes.toList();
}
- _codeVerifier = _createCodeVerifier();
var codeChallenge = base64Url
.encode(sha256.convert(ascii.encode(_codeVerifier)).bytes)
.replaceAll('=', '');
diff --git a/pubspec.yaml b/pubspec.yaml
index 856aac8..3fad972 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: oauth2
-version: 1.6.2-dev
+version: 1.6.3
homepage: https://github.com/dart-lang/oauth2
description: >-
A client library for authenticating with a remote service via OAuth2 on
diff --git a/test/authorization_code_grant_test.dart b/test/authorization_code_grant_test.dart
index 4909bf5..f74c061 100644
--- a/test/authorization_code_grant_test.dart
+++ b/test/authorization_code_grant_test.dart
@@ -54,6 +54,29 @@
]));
});
+ test('builds the correct URL with passed in code verifier', () {
+ final codeVerifier =
+ 'it1shei7LooGoh3looxaa4sieveijeib2zecauz2oo8aebae5aehee0ahPirewoh5Bo6Maexooqui3uL2si6ahweiv7shauc1shahxooveoB3aeyahsaiye0Egh3raix';
+ final expectedCodeChallenge =
+ 'EjfFMv8TFPd3GuNxAn5COhlWBGpfZLimHett7ypJfJ0';
+ var grant = oauth2.AuthorizationCodeGrant(
+ 'identifier',
+ Uri.parse('https://example.com/authorization'),
+ Uri.parse('https://example.com/token'),
+ secret: 'secret',
+ httpClient: client,
+ codeVerifier: codeVerifier);
+ expect(
+ grant.getAuthorizationUrl(redirectUrl).toString(),
+ allOf([
+ startsWith('https://example.com/authorization?response_type=code'),
+ contains('&client_id=identifier'),
+ contains('&redirect_uri=http%3A%2F%2Fexample.com%2Fredirect'),
+ contains('&code_challenge=$expectedCodeChallenge'),
+ contains('&code_challenge_method=S256')
+ ]));
+ });
+
test('separates scopes with the correct delimiter', () {
var grant = oauth2.AuthorizationCodeGrant(
'identifier',