From 51002fecf46f13c43da7c7dc8e82a4fd2d91b3bd Mon Sep 17 00:00:00 2001 From: TomaszCz Date: Wed, 26 Mar 2025 13:30:55 +0100 Subject: [PATCH] add prompt query param to oauth2 library --- pkgs/oauth2/CHANGELOG.md | 4 ++ .../lib/src/authorization_code_grant.dart | 39 ++++++++++++------- pkgs/oauth2/pubspec.yaml | 2 +- .../test/authorization_code_grant_test.dart | 19 +++++++++ 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/pkgs/oauth2/CHANGELOG.md b/pkgs/oauth2/CHANGELOG.md index ff39de3fb4..4348c2bbd9 100644 --- a/pkgs/oauth2/CHANGELOG.md +++ b/pkgs/oauth2/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.5 + +* Added prompt param + ## 2.0.4-wip * Require Dart 3.4 diff --git a/pkgs/oauth2/lib/src/authorization_code_grant.dart b/pkgs/oauth2/lib/src/authorization_code_grant.dart index fac56ba0d0..bb0f8757bd 100644 --- a/pkgs/oauth2/lib/src/authorization_code_grant.dart +++ b/pkgs/oauth2/lib/src/authorization_code_grant.dart @@ -182,32 +182,27 @@ class AuthorizationCodeGrant { /// query parameters provided to the redirect URL. /// /// It is a [StateError] to call this more than once. + Uri getAuthorizationUrl(Uri redirect, - {Iterable? scopes, String? state}) { + {Iterable? scopes, String? state, OAuthPrompt? prompt}) { if (_state != _State.initial) { throw StateError('The authorization URL has already been generated.'); } _state = _State.awaitingResponse; - var scopeList = scopes?.toList() ?? []; - var codeChallenge = base64Url - .encode(sha256.convert(ascii.encode(_codeVerifier)).bytes) - .replaceAll('=', ''); - - _redirectEndpoint = redirect; - _scopes = scopeList; - _stateString = state; var parameters = { 'response_type': 'code', 'client_id': identifier, 'redirect_uri': redirect.toString(), - 'code_challenge': codeChallenge, - 'code_challenge_method': 'S256' + 'code_challenge': base64Url + .encode(sha256.convert(ascii.encode(_codeVerifier)).bytes) + .replaceAll('=', ''), + 'code_challenge_method': 'S256', + if (state != null) 'state': state, + if (scopes?.isNotEmpty ?? false) 'scope': scopes!.join(_delimiter), + if (prompt != null) 'prompt': prompt.value, }; - if (state != null) parameters['state'] = state; - if (scopeList.isNotEmpty) parameters['scope'] = scopeList.join(_delimiter); - return addQueryParameters(authorizationEndpoint, parameters); } @@ -369,3 +364,19 @@ class _State { @override String toString() => _name; } + +enum OAuthPrompt { + // Forces user reauthentication + login('login'), + // Forces user to reapprove permissions + consent('consent'), + // Prompts user to select an account + selectAccount('select_account'), + // Silent authentication (fails if interaction needed) + none('none'), + // Forces multi-factor authentication (if supported) + mfaRequired('mfa'); + + final String value; + const OAuthPrompt(this.value); +} diff --git a/pkgs/oauth2/pubspec.yaml b/pkgs/oauth2/pubspec.yaml index ab705ba401..b60d537a07 100644 --- a/pkgs/oauth2/pubspec.yaml +++ b/pkgs/oauth2/pubspec.yaml @@ -1,5 +1,5 @@ name: oauth2 -version: 2.0.4-wip +version: 2.0.5 description: >- A client library for authenticating with a remote service via OAuth2 on behalf of a user, and making authorized HTTP requests with the user's diff --git a/pkgs/oauth2/test/authorization_code_grant_test.dart b/pkgs/oauth2/test/authorization_code_grant_test.dart index 06e88afdd1..5beb9d4dff 100644 --- a/pkgs/oauth2/test/authorization_code_grant_test.dart +++ b/pkgs/oauth2/test/authorization_code_grant_test.dart @@ -100,6 +100,25 @@ void main() { ])); }); + test('builds the correct URL with prompt parameter', () { + var authorizationUrl = grant.getAuthorizationUrl( + redirectUrl, + prompt: oauth2.OAuthPrompt.login, + ); + + expect( + authorizationUrl.toString(), + allOf([ + startsWith('https://example.com/authorization?response_type=code'), + contains('&client_id=identifier'), + contains('&redirect_uri=http%3A%2F%2Fexample.com%2Fredirect'), + matches(r'&code_challenge=[A-Za-z0-9\+\/\-\_]{43}'), + contains('&code_challenge_method=S256'), + contains('&prompt=login'), + ]), + ); + }); + test('builds the correct URL with state', () { var authorizationUrl = grant.getAuthorizationUrl(redirectUrl, state: 'state');