diff --git a/README.md b/README.md index 2137d41..f0fd903 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ AndroidManifest.xml - + ``` @@ -155,7 +155,7 @@ import 'package:http/http.dart' as http; //Instantiate an OAuth2Client... GoogleOAuth2Client client = GoogleOAuth2Client( customUriScheme: 'my.test.app' //Must correspond to the AndroidManifest's "android:scheme" attribute - redirectUri: 'my.test.app:/oauth2redirect', //Can be any URI, but the scheme part must correspond to the customeUriScheme + redirectUri: 'my.test.app:/oauth2redirect', //Can be any URI, but the scheme part must correspond to the customUriScheme ); //Then, instantiate the helper passing the previously instantiated client @@ -217,7 +217,7 @@ if(tknResp.isExpired()) { } ``` -## Acessing custom/non standard response fields ## +## Accessing custom/non standard response fields ## You can access non standard fields in the response by calling the ```getRespField``` method. For example: @@ -302,7 +302,7 @@ Then you can instantiate an helper class or directly use the client methods to a ## GitHub client ## -In order to use this client you need to first create a new OAuth2 App in the GittHub Developer Settings (https://github.com/settings/developers) +In order to use this client you need to first create a new OAuth2 App in the GitHub Developer Settings (https://github.com/settings/developers) Then in your code: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index a0a4948..e88cfc1 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -8,7 +8,7 @@ repository: https://github.com/teranetsrl/oauth2_client publish_to: none environment: - sdk: '>=2.12.0 <3.11.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: diff --git a/lib/access_token_response.dart b/lib/access_token_response.dart index 6c68e83..f5996c0 100644 --- a/lib/access_token_response.dart +++ b/lib/access_token_response.dart @@ -14,18 +14,20 @@ class AccessTokenResponse extends OAuth2Response { AccessTokenResponse.fromMap(Map map) : super.fromMap(map); @override - factory AccessTokenResponse.fromHttpResponse(http.Response response, - {List? requestedScopes}) { + factory AccessTokenResponse.fromHttpResponse( + http.Response response, { + List? requestedScopes, + }) { AccessTokenResponse resp; - var defMap = {'http_status_code': response.statusCode}; + final defMap = {'http_status_code': response.statusCode}; if (response.body != '') { - Map rMap = jsonDecode(response.body); + final Map rMap = jsonDecode(response.body); //From Section 4.2.2. (Access Token Response) of OAuth2 rfc, the "scope" parameter in the Access Token Response is //"OPTIONAL, if identical to the scope requested by the client; otherwise, REQUIRED." - if ((!rMap.containsKey('scope') || + if (!rMap.containsKey('scope') || rMap['scope'] == null || - rMap['scope'].isEmpty)) { + rMap['scope'].isEmpty) { if (requestedScopes != null) { rMap['scope'] = requestedScopes; } @@ -53,7 +55,7 @@ class AccessTokenResponse extends OAuth2Response { } else { resp = AccessTokenResponse.fromMap({ ...defMap, - ...{'scope': requestedScopes} + ...{'scope': requestedScopes}, }); } @@ -64,7 +66,7 @@ class AccessTokenResponse extends OAuth2Response { Map toMap() { return { ...respMap, - ...{'scope': scope} + ...{'scope': scope}, }; } @@ -73,7 +75,7 @@ class AccessTokenResponse extends OAuth2Response { var expired = false; if (expirationDate != null) { - var now = DateTime.now(); + final now = DateTime.now(); expired = expirationDate!.difference(now).inSeconds < 0; } @@ -81,11 +83,11 @@ class AccessTokenResponse extends OAuth2Response { } ///Checks if the access token must be refreshed - bool refreshNeeded({secondsToExpiration = 30}) { + bool refreshNeeded({int secondsToExpiration = 30}) { var needsRefresh = false; if (expirationDate != null) { - var now = DateTime.now(); + final now = DateTime.now(); needsRefresh = expirationDate!.difference(now).inSeconds < secondsToExpiration; } diff --git a/lib/authorization_response.dart b/lib/authorization_response.dart index 492bae2..7d8d92b 100644 --- a/lib/authorization_response.dart +++ b/lib/authorization_response.dart @@ -1,15 +1,10 @@ /// Represents the response to an Authorization Request. /// see https://tools.ietf.org/html/rfc6749#page-26 class AuthorizationResponse { - String? code; - String? state; - late Map queryParams; - - String? error; - String? errorDescription; - AuthorizationResponse.fromRedirectUri( - String redirectUri, String? checkState) { + String redirectUri, + String? checkState, + ) { queryParams = Uri.parse(redirectUri).queryParameters; error = getQueryParam('error'); @@ -30,11 +25,18 @@ class AuthorizationResponse { if (state != checkState) { throw Exception( - '"state" parameter in response doesn\'t correspond to the expected value'); + '"state" parameter in response doesn\'t correspond to the expected value', + ); } } } } + String? code; + String? state; + late Map queryParams; + + String? error; + String? errorDescription; /// Returns the value of the [paramName] key in the queryParams map dynamic getQueryParam(String paramName) { @@ -45,6 +47,6 @@ class AuthorizationResponse { } bool isAccessGranted() { - return error != null ? error!.isEmpty : true; + return error?.isEmpty ?? true; } } diff --git a/lib/facebook_oauth2_client.dart b/lib/facebook_oauth2_client.dart index c3cb032..81baa8f 100644 --- a/lib/facebook_oauth2_client.dart +++ b/lib/facebook_oauth2_client.dart @@ -5,11 +5,11 @@ import 'package:oauth2_client/oauth2_client.dart'; /// In order to use this client you need to first configure OAuth2 credentials in the Facebook dashboard. /// class FacebookOAuth2Client extends OAuth2Client { - FacebookOAuth2Client( - {required String redirectUri, required String customUriScheme}) - : super( - authorizeUrl: 'https://www.facebook.com/v5.0/dialog/oauth', - tokenUrl: 'https://graph.facebook.com/oauth/access_token', - redirectUri: redirectUri, - customUriScheme: customUriScheme); + FacebookOAuth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( + authorizeUrl: 'https://www.facebook.com/v5.0/dialog/oauth', + tokenUrl: 'https://graph.facebook.com/oauth/access_token', + ); } diff --git a/lib/github_oauth2_client.dart b/lib/github_oauth2_client.dart index a291d38..d5c860d 100644 --- a/lib/github_oauth2_client.dart +++ b/lib/github_oauth2_client.dart @@ -1,53 +1,56 @@ +import 'package:http/http.dart'; import 'package:oauth2_client/access_token_response.dart'; import 'package:oauth2_client/oauth2_client.dart'; -import 'src/base_web_auth.dart'; +import 'package:oauth2_client/src/base_web_auth.dart'; /// Implements an OAuth2 client against GitHub /// /// In order to use this client you need to first create a new OAuth2 App in the GittHub Developer Settings (https://github.com/settings/developers) /// class GitHubOAuth2Client extends OAuth2Client { - GitHubOAuth2Client( - {required String redirectUri, required String customUriScheme}) - : super( - authorizeUrl: 'https://github.com/login/oauth/authorize', - tokenUrl: 'https://github.com/login/oauth/access_token', - redirectUri: redirectUri, - customUriScheme: customUriScheme); + GitHubOAuth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( + authorizeUrl: 'https://github.com/login/oauth/authorize', + tokenUrl: 'https://github.com/login/oauth/access_token', + ); @override - Future getTokenWithAuthCodeFlow( - {required String clientId, - List? scopes, - String? clientSecret, - bool enablePKCE = true, - bool enableState = true, - String? state, - String? codeVerifier, - Function? afterAuthorizationCodeCb, - Map? authCodeParams, - Map? accessTokenParams, - Map? accessTokenHeaders, - httpClient, - BaseWebAuth? webAuthClient, - Map? webAuthOpts}) async { + Future getTokenWithAuthCodeFlow({ + required String clientId, + Client? httpClient, + List? scopes, + String? clientSecret, + bool enablePKCE = true, + bool enableState = true, + String? state, + String? codeVerifier, + Function? afterAuthorizationCodeCb, + Map? authCodeParams, + Map? accessTokenParams, + Map? accessTokenHeaders, + BaseWebAuth? webAuthClient, + Map? webAuthOpts, + }) async { return super.getTokenWithAuthCodeFlow( - clientId: clientId, - scopes: scopes, - clientSecret: clientSecret, - enablePKCE: enablePKCE, - enableState: enableState, - state: state, - codeVerifier: codeVerifier, - afterAuthorizationCodeCb: afterAuthorizationCodeCb, - authCodeParams: authCodeParams, - accessTokenParams: accessTokenParams, - accessTokenHeaders: { - ...?accessTokenHeaders, - ...{'Accept': 'application/json'} - }, - httpClient: httpClient, - webAuthClient: webAuthClient, - webAuthOpts: webAuthOpts); + clientId: clientId, + scopes: scopes, + clientSecret: clientSecret, + enablePKCE: enablePKCE, + enableState: enableState, + state: state, + codeVerifier: codeVerifier, + afterAuthorizationCodeCb: afterAuthorizationCodeCb, + authCodeParams: authCodeParams, + accessTokenParams: accessTokenParams, + accessTokenHeaders: { + ...?accessTokenHeaders, + ...{'Accept': 'application/json'}, + }, + httpClient: httpClient, + webAuthClient: webAuthClient, + webAuthOpts: webAuthOpts, + ); } } diff --git a/lib/google_oauth2_client.dart b/lib/google_oauth2_client.dart index 9b913cb..0c57f8d 100644 --- a/lib/google_oauth2_client.dart +++ b/lib/google_oauth2_client.dart @@ -8,12 +8,12 @@ import 'package:oauth2_client/oauth2_client.dart'; /// (for example 'com.example.app', but you can use whatever uri scheme you want). /// class GoogleOAuth2Client extends OAuth2Client { - GoogleOAuth2Client( - {required String redirectUri, required String customUriScheme}) - : super( - authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth', - tokenUrl: 'https://oauth2.googleapis.com/token', - revokeUrl: 'https://oauth2.googleapis.com/revoke', - redirectUri: redirectUri, - customUriScheme: customUriScheme); + GoogleOAuth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( + authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth', + tokenUrl: 'https://oauth2.googleapis.com/token', + revokeUrl: 'https://oauth2.googleapis.com/revoke', + ); } diff --git a/lib/linkedin_oauth2_client.dart b/lib/linkedin_oauth2_client.dart index 19fe3fd..7d39a07 100644 --- a/lib/linkedin_oauth2_client.dart +++ b/lib/linkedin_oauth2_client.dart @@ -5,13 +5,12 @@ import 'package:oauth2_client/oauth2_client.dart'; /// In order to use this client you need to first configure OAuth2 credentials (see https://docs.microsoft.com/it-it/linkedin/shared/authentication/authorization-code-flow) /// class LinkedInOAuth2Client extends OAuth2Client { - LinkedInOAuth2Client( - {required String redirectUri, required String customUriScheme}) - : super( + LinkedInOAuth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( authorizeUrl: 'https://www.linkedin.com/oauth/v2/authorization', tokenUrl: 'https://www.linkedin.com/oauth/v2/accessToken', - redirectUri: redirectUri, - customUriScheme: customUriScheme, credentialsLocation: CredentialsLocation.body, ); } diff --git a/lib/microsoft_oauth2_client.dart b/lib/microsoft_oauth2_client.dart index bd7be55..0f86274 100644 --- a/lib/microsoft_oauth2_client.dart +++ b/lib/microsoft_oauth2_client.dart @@ -4,16 +4,13 @@ import 'package:oauth2_client/oauth2_client.dart'; /// /// class MicrosoftOauth2Client extends OAuth2Client { - static const String _myAuthority = "https://login.microsoftonline.com/"; - MicrosoftOauth2Client({ required String tenant, - required String redirectUri, - required String customUriScheme, + required super.redirectUri, + required super.customUriScheme, }) : super( authorizeUrl: '$_myAuthority$tenant/oauth2/v2.0/authorize', tokenUrl: '$_myAuthority$tenant/oauth2/v2.0/token', - redirectUri: redirectUri, - customUriScheme: customUriScheme, ); + static const String _myAuthority = 'https://login.microsoftonline.com/'; } diff --git a/lib/oauth2_client.dart b/lib/oauth2_client.dart index 3e13a6e..188a4fc 100644 --- a/lib/oauth2_client.dart +++ b/lib/oauth2_client.dart @@ -2,20 +2,18 @@ import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; +import 'package:http/http.dart'; import 'package:oauth2_client/access_token_response.dart'; import 'package:oauth2_client/authorization_response.dart'; import 'package:oauth2_client/oauth2_response.dart'; +import 'package:oauth2_client/src/base_web_auth.dart'; import 'package:oauth2_client/src/oauth2_utils.dart'; -import 'package:random_string/random_string.dart'; - -// import 'package:oauth2_client/src/web_auth.dart'; - -import 'src/base_web_auth.dart'; -import 'src/web_auth.dart' +import 'package:oauth2_client/src/web_auth.dart' // ignore: uri_does_not_exist if (dart.library.io) 'src/io_web_auth.dart' // ignore: uri_does_not_exist if (dart.library.html) 'src/browser_web_auth.dart'; +import 'package:random_string/random_string.dart'; enum CredentialsLocation { header, body } @@ -40,6 +38,26 @@ enum CredentialsLocation { header, body } /// /// class OAuth2Client { + /// Creates a new client instance with the following parameters: + /// + /// * [authorizeUrl]: the url that must be used to fetch authorization codes (for Authorization Code flows) + /// * [tokenUrl]: the url to be used for generating the OAuth2 access tokens + /// * [refreshUrl]: the url that must be used for refreshing an Access Token + /// * [revokeUrl]: the url to be invoked for token revocation + /// * [redirectUri]: the redirect uri defined in the provider's client registration panel + /// * [customUriScheme]: the scheme used for the redirect uri + /// * [credentialsLocation]: where the credentials (client ID / client secret) should be passed (header / body) + /// * [scopeSeparator]: the separator that has to be used to serialize scopes in the token request + OAuth2Client({ + required this.authorizeUrl, + required this.tokenUrl, + required this.redirectUri, + required this.customUriScheme, + this.refreshUrl, + this.revokeUrl, + this.credentialsLocation = CredentialsLocation.header, + this.scopeSeparator = ' ', + }); String redirectUri; String customUriScheme; @@ -52,57 +70,40 @@ class OAuth2Client { BaseWebAuth webAuthClient = createWebAuth(); CredentialsLocation credentialsLocation; - /// Creates a new client instance with the following parameters: - /// - /// * [authorizeUrl]: the url that must be used to fetch authorization codes (for Authorization Code flows) - /// * [tokenUrl]: the url to be used for generating the OAuth2 access tokens - /// * [refreshUrl]: the url that must be used for refreshing an Access Token - /// * [revokeUrl]: the url to be invoked for token revocation - /// * [redirectUri]: the redirect uri defined in the provider's client registration panel - /// * [customUriScheme]: the scheme used for the redirect uri - /// * [credentialsLocation]: where the credentials (client ID / client secret) should be passed (header / body) - /// * [scopeSeparator]: the separator that has to be used to serialize scopes in the token request - OAuth2Client( - {required this.authorizeUrl, - required this.tokenUrl, - this.refreshUrl, - this.revokeUrl, - required this.redirectUri, - required this.customUriScheme, - this.credentialsLocation = CredentialsLocation.header, - this.scopeSeparator = ' '}); - /// Requests an Access Token to the OAuth2 endpoint using the Implicit grant flow (https://tools.ietf.org/html/rfc6749#page-31) - Future getTokenWithImplicitGrantFlow( - {required String clientId, - List? scopes, - bool enableState = true, - String? state, - httpClient, - BaseWebAuth? webAuthClient, - Map? webAuthOpts, - Map? customParams}) async { + Future getTokenWithImplicitGrantFlow({ + required String clientId, + List? scopes, + bool enableState = true, + String? state, + Client? httpClient, + BaseWebAuth? webAuthClient, + Map? webAuthOpts, + Map? customParams, + }) async { httpClient ??= http.Client(); webAuthClient ??= this.webAuthClient; if (enableState) state ??= randomAlphaNumeric(25); final authorizeUrl = getAuthorizeUrl( - clientId: clientId, - responseType: 'token', - scopes: scopes, - enableState: enableState, - state: state, - redirectUri: redirectUri, - customParams: customParams); + clientId: clientId, + responseType: 'token', + scopes: scopes, + enableState: enableState, + state: state, + redirectUri: redirectUri, + customParams: customParams, + ); // Present the dialog to the user try { final result = await webAuthClient.authenticate( - url: authorizeUrl, - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri, - opts: webAuthOpts); + url: authorizeUrl, + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + opts: webAuthOpts, + ); final fragment = Uri.splitQueryString(Uri.parse(result).fragment); @@ -110,7 +111,8 @@ class OAuth2Client { final checkState = fragment['state']; if (state != checkState) { throw Exception( - '"state" parameter in response doesn\'t correspond to the expected value'); + '"state" parameter in response doesn\'t correspond to the expected value', + ); } } @@ -119,7 +121,7 @@ class OAuth2Client { 'token_type': fragment['token_type'], 'scope': fragment['scope'] ?? scopes, 'expires_in': fragment['expires_in'], - 'http_status_code': 200 + 'http_status_code': 200, }); } on PlatformException { return AccessTokenResponse.errorResponse(); @@ -127,21 +129,22 @@ class OAuth2Client { } /// Requests an Access Token to the OAuth2 endpoint using the Authorization Code Flow. - Future getTokenWithAuthCodeFlow( - {required String clientId, - List? scopes, - String? clientSecret, - bool enablePKCE = true, - bool enableState = true, - String? state, - String? codeVerifier, - Function? afterAuthorizationCodeCb, - Map? authCodeParams, - Map? accessTokenParams, - Map? accessTokenHeaders, - httpClient, - BaseWebAuth? webAuthClient, - Map? webAuthOpts}) async { + Future getTokenWithAuthCodeFlow({ + required String clientId, + List? scopes, + String? clientSecret, + bool enablePKCE = true, + bool enableState = true, + String? state, + String? codeVerifier, + Function? afterAuthorizationCodeCb, + Map? authCodeParams, + Map? accessTokenParams, + Map? accessTokenHeaders, + Client? httpClient, + BaseWebAuth? webAuthClient, + Map? webAuthOpts, + }) async { AccessTokenResponse? tknResp; String? codeChallenge; @@ -153,15 +156,16 @@ class OAuth2Client { } try { - var authResp = await requestAuthorization( - webAuthClient: webAuthClient, - clientId: clientId, - scopes: scopes, - codeChallenge: codeChallenge, - enableState: enableState, - state: state, - customParams: authCodeParams, - webAuthOpts: webAuthOpts); + final authResp = await requestAuthorization( + webAuthClient: webAuthClient, + clientId: clientId, + scopes: scopes, + codeChallenge: codeChallenge, + enableState: enableState, + state: state, + customParams: authCodeParams, + webAuthOpts: webAuthOpts, + ); if (authResp.isAccessGranted()) { if (afterAuthorizationCodeCb != null) { @@ -169,16 +173,17 @@ class OAuth2Client { } tknResp = await requestAccessToken( - httpClient: httpClient, - //If the authorization request was successfull, the code must be set - //otherwise an exception is raised in the OAuth2Response constructor - code: authResp.code!, - clientId: clientId, - scopes: scopes, - clientSecret: clientSecret, - codeVerifier: codeVerifier, - customParams: accessTokenParams, - customHeaders: accessTokenHeaders); + httpClient: httpClient, + //If the authorization request was successfull, the code must be set + //otherwise an exception is raised in the OAuth2Response constructor + code: authResp.code!, + clientId: clientId, + scopes: scopes, + clientSecret: clientSecret, + codeVerifier: codeVerifier, + customParams: accessTokenParams, + customHeaders: accessTokenHeaders, + ); } else { tknResp = AccessTokenResponse.errorResponse(); } @@ -190,39 +195,42 @@ class OAuth2Client { } /// Requests an Access Token to the OAuth2 endpoint using the Client Credentials flow. - Future getTokenWithClientCredentialsFlow( - {required String clientId, - required String clientSecret, - List? scopes, - Map? customHeaders, - httpClient}) async { - var params = {'grant_type': 'client_credentials'}; + Future getTokenWithClientCredentialsFlow({ + required String clientId, + required String clientSecret, + List? scopes, + Map? customHeaders, + Client? httpClient, + }) async { + final params = {'grant_type': 'client_credentials'}; if (scopes != null && scopes.isNotEmpty) { params['scope'] = serializeScopes(scopes); } - var response = await _performAuthorizedRequest( - url: tokenUrl, - clientId: clientId, - clientSecret: clientSecret, - params: params, - headers: customHeaders, - httpClient: httpClient); + final response = await _performAuthorizedRequest( + url: tokenUrl, + clientId: clientId, + clientSecret: clientSecret, + params: params, + headers: customHeaders, + httpClient: httpClient, + ); return http2TokenResponse(response, requestedScopes: scopes); } /// Requests an Authorization Code to be used in the Authorization Code grant. - Future requestAuthorization( - {required String clientId, - List? scopes, - String? codeChallenge, - bool enableState = true, - String? state, - Map? customParams, - BaseWebAuth? webAuthClient, - Map? webAuthOpts}) async { + Future requestAuthorization({ + required String clientId, + List? scopes, + String? codeChallenge, + bool enableState = true, + String? state, + Map? customParams, + BaseWebAuth? webAuthClient, + Map? webAuthOpts, + }) async { webAuthClient ??= this.webAuthClient; if (enableState) { @@ -230,111 +238,148 @@ class OAuth2Client { } final authorizeUrl = getAuthorizeUrl( - clientId: clientId, - redirectUri: redirectUri, - scopes: scopes, - enableState: enableState, - state: state, - codeChallenge: codeChallenge, - customParams: customParams); + clientId: clientId, + redirectUri: redirectUri, + scopes: scopes, + enableState: enableState, + state: state, + codeChallenge: codeChallenge, + customParams: customParams, + ); // Present the dialog to the user final result = await webAuthClient.authenticate( - url: authorizeUrl, - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri, - opts: webAuthOpts); + url: authorizeUrl, + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + opts: webAuthOpts, + ); return AuthorizationResponse.fromRedirectUri(result, state); } /// Requests and Access Token using the provided Authorization [code]. - Future requestAccessToken( - {required String code, - required String clientId, - String? clientSecret, - String? codeVerifier, - List? scopes, - Map? customParams, - Map? customHeaders, - httpClient}) async { + Future requestAccessToken({ + required String code, + required String clientId, + String? clientSecret, + String? codeVerifier, + List? scopes, + Map? customParams, + Map? customHeaders, + Client? httpClient, + }) async { final params = getTokenUrlParams( - code: code, - redirectUri: redirectUri, - codeVerifier: codeVerifier, - customParams: customParams); - - var response = await _performAuthorizedRequest( - url: tokenUrl, - clientId: clientId, - clientSecret: clientSecret, - params: params, - headers: customHeaders, - httpClient: httpClient); + code: code, + redirectUri: redirectUri, + codeVerifier: codeVerifier, + customParams: customParams, + ); + + final response = await _performAuthorizedRequest( + url: tokenUrl, + clientId: clientId, + clientSecret: clientSecret, + params: params, + headers: customHeaders, + httpClient: httpClient, + ); return http2TokenResponse(response, requestedScopes: scopes); } /// Refreshes an Access Token issuing a refresh_token grant to the OAuth2 server. - Future refreshToken(String refreshToken, - {httpClient, - required String clientId, - String? clientSecret, - List? scopes}) async { - final Map params = getRefreshUrlParams(refreshToken: refreshToken); - - var response = await _performAuthorizedRequest( - url: _getRefreshUrl(), - clientId: clientId, - clientSecret: clientSecret, - params: params, - httpClient: httpClient); + Future refreshToken( + String refreshToken, { + required String clientId, + Client? httpClient, + String? clientSecret, + List? scopes, + }) async { + final params = getRefreshUrlParams(refreshToken: refreshToken); + + final response = await _performAuthorizedRequest( + url: _getRefreshUrl(), + clientId: clientId, + clientSecret: clientSecret, + params: params, + httpClient: httpClient, + ); return http2TokenResponse(response, requestedScopes: scopes); } /// Revokes both the Access and the Refresh tokens in the provided [tknResp] - Future revokeToken(AccessTokenResponse tknResp, - {String? clientId, String? clientSecret, httpClient}) async { - var tokenRevocationResp = await revokeAccessToken(tknResp, - clientId: clientId, clientSecret: clientSecret, httpClient: httpClient); + Future revokeToken( + AccessTokenResponse tknResp, { + String? clientId, + String? clientSecret, + Client? httpClient, + }) async { + var tokenRevocationResp = await revokeAccessToken( + tknResp, + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); if (tokenRevocationResp.isValid()) { - tokenRevocationResp = await revokeRefreshToken(tknResp, - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + tokenRevocationResp = await revokeRefreshToken( + tknResp, + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); } return tokenRevocationResp; } /// Revokes the Access Token in the provided [tknResp] - Future revokeAccessToken(AccessTokenResponse tknResp, - {String? clientId, String? clientSecret, httpClient}) async { - return await _revokeTokenByType(tknResp, 'access_token', - clientId: clientId, clientSecret: clientSecret, httpClient: httpClient); + Future revokeAccessToken( + AccessTokenResponse tknResp, { + String? clientId, + String? clientSecret, + Client? httpClient, + }) async { + return _revokeTokenByType( + tknResp, + 'access_token', + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); } /// Revokes the Refresh Token in the provided [tknResp] - Future revokeRefreshToken(AccessTokenResponse tknResp, - {String? clientId, String? clientSecret, httpClient}) async { - return await _revokeTokenByType(tknResp, 'refresh_token', - clientId: clientId, clientSecret: clientSecret, httpClient: httpClient); + Future revokeRefreshToken( + AccessTokenResponse tknResp, { + String? clientId, + String? clientSecret, + Client? httpClient, + }) async { + return _revokeTokenByType( + tknResp, + 'refresh_token', + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); } /// Generates the url to be used for fetching the authorization code. - String getAuthorizeUrl( - {required String clientId, - String responseType = 'code', - String? redirectUri, - List? scopes, - bool enableState = true, - String? state, - String? codeChallenge, - Map? customParams}) { + String getAuthorizeUrl({ + required String clientId, + String responseType = 'code', + String? redirectUri, + List? scopes, + bool enableState = true, + String? state, + String? codeChallenge, + Map? customParams, + }) { final params = { 'response_type': responseType, - 'client_id': clientId + 'client_id': clientId, }; if (redirectUri != null && redirectUri.isNotEmpty) { @@ -362,14 +407,15 @@ class OAuth2Client { } /// Returns the parameters needed for the authorization code request - Map getTokenUrlParams( - {required String code, - String? redirectUri, - String? codeVerifier, - Map? customParams}) { + Map getTokenUrlParams({ + required String code, + String? redirectUri, + String? codeVerifier, + Map? customParams, + }) { final params = { 'grant_type': 'authorization_code', - 'code': code + 'code': code, }; if (redirectUri != null && redirectUri.isNotEmpty) { @@ -398,13 +444,14 @@ class OAuth2Client { /// Performs a post request to the specified [url], /// adding authentication credentials as described here: https://tools.ietf.org/html/rfc6749#section-2.3 - Future _performAuthorizedRequest( - {required String url, - required String clientId, - String? clientSecret, - Map? params, - Map? headers, - httpClient}) async { + Future _performAuthorizedRequest({ + required String url, + required String clientId, + String? clientSecret, + Map? params, + Map? headers, + Client? httpClient, + }) async { httpClient ??= http.Client(); headers ??= {}; @@ -418,30 +465,32 @@ class OAuth2Client { } else { switch (credentialsLocation) { case CredentialsLocation.header: - headers.addAll(getAuthorizationHeader( - clientId: clientId, - clientSecret: clientSecret, - )); - break; + headers.addAll( + getAuthorizationHeader( + clientId: clientId, + clientSecret: clientSecret, + ), + ); case CredentialsLocation.body: params['client_id'] = clientId; params['client_secret'] = clientSecret; - break; } } - var response = + final response = await httpClient.post(Uri.parse(url), body: params, headers: headers); return response; } - Map getAuthorizationHeader( - {required String clientId, String? clientSecret}) { - var headers = {}; + Map getAuthorizationHeader({ + required String clientId, + String? clientSecret, + }) { + final headers = {}; if ((clientId.isNotEmpty) && (clientSecret != null)) { - var credentials = base64.encode(utf8.encode('$clientId:$clientSecret')); + final credentials = base64.encode(utf8.encode('$clientId:$clientSecret')); headers['Authorization'] = 'Basic $credentials'; } @@ -453,16 +502,20 @@ class OAuth2Client { Map getRefreshUrlParams({required String refreshToken}) { final params = { 'grant_type': 'refresh_token', - 'refresh_token': refreshToken + 'refresh_token': refreshToken, }; return params; } - AccessTokenResponse http2TokenResponse(http.Response response, - {List? requestedScopes}) { - return AccessTokenResponse.fromHttpResponse(response, - requestedScopes: requestedScopes); + AccessTokenResponse http2TokenResponse( + http.Response response, { + List? requestedScopes, + }) { + return AccessTokenResponse.fromHttpResponse( + response, + requestedScopes: requestedScopes, + ); } String serializeScopes(List scopes) { @@ -471,25 +524,29 @@ class OAuth2Client { /// Revokes the specified token [type] in the [tknResp] Future _revokeTokenByType( - AccessTokenResponse tknResp, String tokenType, - {String? clientId, String? clientSecret, httpClient}) async { + AccessTokenResponse tknResp, + String tokenType, { + String? clientId, + String? clientSecret, + Client? httpClient, + }) async { var resp = OAuth2Response(); if (revokeUrl == null) return resp; httpClient ??= http.Client(); - var token = tokenType == 'access_token' + final token = tokenType == 'access_token' ? tknResp.accessToken : tknResp.refreshToken; if (token != null) { - var params = {'token': token, 'token_type_hint': tokenType}; + final params = {'token': token, 'token_type_hint': tokenType}; if (clientId != null) params['client_id'] = clientId; if (clientSecret != null) params['client_secret'] = clientSecret; - http.Response response = + final response = await httpClient.post(Uri.parse(revokeUrl!), body: params); resp = http2TokenResponse(response); diff --git a/lib/oauth2_exception.dart b/lib/oauth2_exception.dart index a60945c..5745e15 100644 --- a/lib/oauth2_exception.dart +++ b/lib/oauth2_exception.dart @@ -1,6 +1,5 @@ class OAuth2Exception implements Exception { + OAuth2Exception(this.error, {this.errorDescription}); String error; String? errorDescription; - - OAuth2Exception(this.error, {this.errorDescription}); } diff --git a/lib/oauth2_helper.dart b/lib/oauth2_helper.dart index f181718..97ce299 100644 --- a/lib/oauth2_helper.dart +++ b/lib/oauth2_helper.dart @@ -1,4 +1,5 @@ import 'package:http/http.dart' as http; +import 'package:http/http.dart'; import 'package:oauth2_client/access_token_response.dart'; import 'package:oauth2_client/oauth2_client.dart'; import 'package:oauth2_client/oauth2_exception.dart'; @@ -14,6 +15,24 @@ import 'package:oauth2_client/src/token_storage.dart'; /// /// class OAuth2Helper { + OAuth2Helper( + this.client, { + required this.clientId, + this.grantType = authorizationCode, + this.clientSecret, + this.scopes, + this.enablePKCE = true, + this.enableState = true, + TokenStorage? tokenStorage, + this.afterAuthorizationCodeCb, + this.authCodeParams, + this.accessTokenParams, + this.accessTokenHeaders, + this.webAuthClient, + this.webAuthOpts, + }) { + this.tokenStorage = tokenStorage ?? TokenStorage(client.tokenUrl); + } static const authorizationCode = 1; static const clientCredentials = 2; static const implicitGrant = 3; @@ -37,23 +56,6 @@ class OAuth2Helper { BaseWebAuth? webAuthClient; Map? webAuthOpts; - OAuth2Helper(this.client, - {this.grantType = authorizationCode, - required this.clientId, - this.clientSecret, - this.scopes, - this.enablePKCE = true, - this.enableState = true, - tokenStorage, - this.afterAuthorizationCodeCb, - this.authCodeParams, - this.accessTokenParams, - this.accessTokenHeaders, - this.webAuthClient, - this.webAuthOpts}) { - this.tokenStorage = tokenStorage ?? TokenStorage(client.tokenUrl); - } - /// Returns a previously required token, if any, or requires a new one. /// /// If a token already exists but is expired, a new token is generated through the refresh_token grant. @@ -78,7 +80,8 @@ class OAuth2Helper { if (!tknResp.isValid()) { throw Exception( - 'Provider error ${tknResp.httpStatusCode}: ${tknResp.error}: ${tknResp.errorDescription}'); + 'Provider error ${tknResp.httpStatusCode}: ${tknResp.error}: ${tknResp.errorDescription}', + ); } if (!tknResp.isBearer()) { @@ -90,7 +93,7 @@ class OAuth2Helper { /// Returns the previously stored Access Token from the storage, if any Future getTokenFromStorage() async { - return await tokenStorage.getToken(scopes ?? []); + return tokenStorage.getToken(scopes ?? []); } /// Fetches a new token and saves it in the storage @@ -101,32 +104,35 @@ class OAuth2Helper { if (grantType == authorizationCode) { tknResp = await client.getTokenWithAuthCodeFlow( - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - enablePKCE: enablePKCE, - enableState: enableState, - authCodeParams: authCodeParams, - accessTokenParams: accessTokenParams, - accessTokenHeaders: accessTokenHeaders, - afterAuthorizationCodeCb: afterAuthorizationCodeCb, - webAuthClient: webAuthClient, - webAuthOpts: webAuthOpts); + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + enablePKCE: enablePKCE, + enableState: enableState, + authCodeParams: authCodeParams, + accessTokenParams: accessTokenParams, + accessTokenHeaders: accessTokenHeaders, + afterAuthorizationCodeCb: afterAuthorizationCodeCb, + webAuthClient: webAuthClient, + webAuthOpts: webAuthOpts, + ); } else if (grantType == clientCredentials) { tknResp = await client.getTokenWithClientCredentialsFlow( - clientId: clientId, - //The clientSecret param can't be null at this point... It has been validated by the above _validateAuthorizationParams call... - clientSecret: clientSecret!, - customHeaders: accessTokenHeaders, - scopes: scopes); + clientId: clientId, + //The clientSecret param can't be null at this point... It has been validated by the above _validateAuthorizationParams call... + clientSecret: clientSecret!, + customHeaders: accessTokenHeaders, + scopes: scopes, + ); } else if (grantType == implicitGrant) { tknResp = await client.getTokenWithImplicitGrantFlow( - clientId: clientId, - scopes: scopes, - enableState: enableState, - webAuthClient: webAuthClient, - webAuthOpts: webAuthOpts, - customParams: authCodeParams); + clientId: clientId, + scopes: scopes, + enableState: enableState, + webAuthClient: webAuthClient, + webAuthOpts: webAuthOpts, + customParams: authCodeParams, + ); } else { tknResp = AccessTokenResponse.errorResponse(); } @@ -140,16 +146,19 @@ class OAuth2Helper { /// Performs a refresh_token request using the [refreshToken]. Future refreshToken( - AccessTokenResponse curTknResp) async { + AccessTokenResponse curTknResp, + ) async { AccessTokenResponse? tknResp; - var refreshToken = curTknResp.refreshToken!; + final refreshToken = curTknResp.refreshToken!; try { - tknResp = await client.refreshToken(refreshToken, - clientId: clientId, - clientSecret: clientSecret, - scopes: curTknResp.scope); + tknResp = await client.refreshToken( + refreshToken, + clientId: clientId, + clientSecret: clientSecret, + scopes: curTknResp.scope, + ); } catch (_) { - return await fetchToken(); + return fetchToken(); } if (tknResp.isValid()) { @@ -166,8 +175,10 @@ class OAuth2Helper { //Fetch another access token tknResp = await getToken(); } else { - throw OAuth2Exception(tknResp.error ?? 'Error', - errorDescription: tknResp.errorDescription); + throw OAuth2Exception( + tknResp.error ?? 'Error', + errorDescription: tknResp.errorDescription, + ); } } @@ -175,96 +186,130 @@ class OAuth2Helper { } /// Revokes the previously fetched token - Future disconnect({httpClient}) async { + Future disconnect({Client? httpClient}) async { httpClient ??= http.Client(); final tknResp = await tokenStorage.getToken(scopes ?? []); if (tknResp != null) { await tokenStorage.deleteToken(scopes ?? []); - return await client.revokeToken(tknResp, - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + return client.revokeToken( + tknResp, + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); } else { return OAuth2Response(); } } - Future removeAllTokens() async { - await tokenStorage.deleteAllTokens(); + Future removeAllTokens() async { + return tokenStorage.deleteAllTokens(); } /// Performs a POST request to the specified [url], adding the authorization token. /// /// If no token already exists, or if it is expired, a new one is requested. - Future post(String url, - {Map? headers, - dynamic body, - http.Client? httpClient}) async { - return _request('POST', url, - headers: headers, body: body, httpClient: httpClient); + Future post( + String url, { + Map? headers, + dynamic body, + http.Client? httpClient, + }) async { + return _request( + 'POST', + url, + headers: headers, + body: body, + httpClient: httpClient, + ); } /// Performs a PUT request to the specified [url], adding the authorization token. /// /// If no token already exists, or if it is expired, a new one is requested. - Future put(String url, - {Map? headers, - dynamic body, - http.Client? httpClient}) async { - return _request('PUT', url, - headers: headers, body: body, httpClient: httpClient); + Future put( + String url, { + Map? headers, + dynamic body, + http.Client? httpClient, + }) async { + return _request( + 'PUT', + url, + headers: headers, + body: body, + httpClient: httpClient, + ); } /// Performs a PATCH request to the specified [url], adding the authorization token. /// /// If no token already exists, or if it is expired, a new one is requested. - Future patch(String url, - {Map? headers, - dynamic body, - http.Client? httpClient}) async { - return _request('PATCH', url, - headers: headers, body: body, httpClient: httpClient); + Future patch( + String url, { + Map? headers, + dynamic body, + http.Client? httpClient, + }) async { + return _request( + 'PATCH', + url, + headers: headers, + body: body, + httpClient: httpClient, + ); } /// Performs a GET request to the specified [url], adding the authorization token. /// /// If no token already exists, or if it is expired, a new one is requested. - Future get(String url, - {Map? headers, http.Client? httpClient}) async { + Future get( + String url, { + Map? headers, + http.Client? httpClient, + }) async { return _request('GET', url, headers: headers, httpClient: httpClient); } /// Performs a DELETE request to the specified [url], adding the authorization token. /// /// If no token already exists, or if it is expired, a new one is requested. - Future delete(String url, - {Map? headers, http.Client? httpClient}) async { + Future delete( + String url, { + Map? headers, + http.Client? httpClient, + }) async { return _request('DELETE', url, headers: headers, httpClient: httpClient); } /// Performs a HEAD request to the specified [url], adding the authorization token. /// /// If no token already exists, or if it is expired, a new one is requested. - Future head(String url, - {Map? headers, - dynamic body, - http.Client? httpClient}) async { + Future head( + String url, { + Map? headers, + dynamic body, + http.Client? httpClient, + }) async { return _request('HEAD', url, headers: headers, httpClient: httpClient); } /// Common method for making http requests /// Tries to use a previously fetched token, otherwise fetches a new token by means of a refresh flow or by issuing a new authorization flow - Future _request(String method, String url, - {Map? headers, - dynamic body, - http.Client? httpClient}) async { + Future _request( + String method, + String url, { + Map? headers, + dynamic body, + http.Client? httpClient, + }) async { httpClient ??= http.Client(); headers ??= {}; - sendRequest(accessToken) async { + Future sendRequest(String? accessToken) async { http.Response resp; headers!['Authorization'] = 'Bearer $accessToken'; diff --git a/lib/oauth2_response.dart b/lib/oauth2_response.dart index 668114d..52f4bf3 100644 --- a/lib/oauth2_response.dart +++ b/lib/oauth2_response.dart @@ -5,15 +5,6 @@ import 'package:http/http.dart' as http; /// Represents the base response for the OAuth 2 requests. /// see https://tools.ietf.org/html/rfc6749#section-5.2 class OAuth2Response { - // String? error; - // String? errorDescription; - // String? errorUri; - // late int httpStatusCode; - - // DateTime ts = DateTime.now(); - - Map respMap = {}; - OAuth2Response(); OAuth2Response.fromMap(Map map) { @@ -27,7 +18,7 @@ class OAuth2Response { factory OAuth2Response.fromHttpResponse(http.Response response) { OAuth2Response resp; - var defMap = {'http_status_code': response.statusCode}; + final defMap = {'http_status_code': response.statusCode}; if (response.body != '') { resp = OAuth2Response.fromMap({...jsonDecode(response.body), ...defMap}); @@ -57,6 +48,14 @@ class OAuth2Response { return resp; */ } + // String? error; + // String? errorDescription; + // String? errorUri; + // late int httpStatusCode; + + // DateTime ts = DateTime.now(); + + Map respMap = {}; Map toMap() { return respMap; diff --git a/lib/reddit_oauth_client.dart b/lib/reddit_oauth_client.dart index ad66b8c..cc3a6bf 100644 --- a/lib/reddit_oauth_client.dart +++ b/lib/reddit_oauth_client.dart @@ -5,15 +5,13 @@ import 'package:oauth2_client/oauth2_client.dart'; /// In order to use this client you need to first create a new OAuth2 App in Reddit autorized apps settings (https://www.reddit.com/prefs/apps) /// class RedditOauth2Client extends OAuth2Client { - RedditOauth2Client( - {required String redirectUri, required String customUriScheme}) - : super( + RedditOauth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( authorizeUrl: 'https://www.reddit.com/api/v1/authorize.compact', //Your service's authorization url tokenUrl: 'https://www.reddit.com/api/v1/access_token', - //Your service access token url - redirectUri: redirectUri, - customUriScheme: customUriScheme, scopeSeparator: ',', credentialsLocation: CredentialsLocation.header, ); diff --git a/lib/shopify_oauth2_client.dart b/lib/shopify_oauth2_client.dart index 51baba1..7ce70aa 100644 --- a/lib/shopify_oauth2_client.dart +++ b/lib/shopify_oauth2_client.dart @@ -1,4 +1,4 @@ -import 'oauth2_client.dart'; +import 'package:oauth2_client/oauth2_client.dart'; /// Implements an OAuth2 client that uses Shopify services to authorize requests. /// @@ -10,13 +10,11 @@ import 'oauth2_client.dart'; class ShopifyOAuth2Client extends OAuth2Client { ShopifyOAuth2Client({ required String shop, - required String redirectUri, - required String customUriScheme, + required super.redirectUri, + required super.customUriScheme, }) : super( authorizeUrl: 'https://$shop.myshopify.com/admin/oauth/authorize', tokenUrl: 'https://$shop.myshopify.com/admin/oauth/access_token', - redirectUri: redirectUri, - customUriScheme: customUriScheme, credentialsLocation: CredentialsLocation.body, ); } diff --git a/lib/spotify_oauth2_client.dart b/lib/spotify_oauth2_client.dart index 95f2dde..54abefe 100644 --- a/lib/spotify_oauth2_client.dart +++ b/lib/spotify_oauth2_client.dart @@ -1,12 +1,11 @@ -import 'oauth2_client.dart'; +import 'package:oauth2_client/oauth2_client.dart'; class SpotifyOAuth2Client extends OAuth2Client { - SpotifyOAuth2Client( - {required String redirectUri, required String customUriScheme}) - : super( + SpotifyOAuth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( authorizeUrl: 'https://accounts.spotify.com/authorize', tokenUrl: 'https://accounts.spotify.com/api/token', - redirectUri: redirectUri, - customUriScheme: customUriScheme, ); } diff --git a/lib/src/base_web_auth.dart b/lib/src/base_web_auth.dart index d853e2d..233c827 100644 --- a/lib/src/base_web_auth.dart +++ b/lib/src/base_web_auth.dart @@ -1,7 +1,8 @@ abstract class BaseWebAuth { - Future authenticate( - {required String callbackUrlScheme, - required String url, - required String redirectUrl, - Map? opts}); + Future authenticate({ + required String callbackUrlScheme, + required String url, + required String redirectUrl, + Map? opts, + }); } diff --git a/lib/src/browser_web_auth.dart b/lib/src/browser_web_auth.dart index 8fa2158..9d94b39 100644 --- a/lib/src/browser_web_auth.dart +++ b/lib/src/browser_web_auth.dart @@ -2,24 +2,26 @@ import 'dart:html' as html; -import 'base_web_auth.dart'; +import 'package:oauth2_client/src/base_web_auth.dart'; BaseWebAuth createWebAuth() => BrowserWebAuth(); class BrowserWebAuth implements BaseWebAuth { @override - Future authenticate( - {required String callbackUrlScheme, - required String url, - required String redirectUrl, - Map? opts}) async { + Future authenticate({ + required String callbackUrlScheme, + required String url, + required String redirectUrl, + Map? opts, + }) async { // ignore: unsafe_html final popupLogin = html.window.open( - url, - 'oauth2_client::authenticateWindow', - 'menubar=no, status=no, scrollbars=no, menubar=no, width=1000, height=500'); + url, + 'oauth2_client::authenticateWindow', + 'menubar=no, status=no, scrollbars=no, menubar=no, width=1000, height=500', + ); - var messageEvt = await html.window.onMessage + final messageEvt = await html.window.onMessage .firstWhere((evt) => evt.origin == Uri.parse(redirectUrl).origin); popupLogin.close(); diff --git a/lib/src/io_web_auth.dart b/lib/src/io_web_auth.dart index fdd1fe0..f02de46 100644 --- a/lib/src/io_web_auth.dart +++ b/lib/src/io_web_auth.dart @@ -1,20 +1,20 @@ import 'package:flutter_web_auth_2/flutter_web_auth_2.dart'; - -import 'base_web_auth.dart'; +import 'package:oauth2_client/src/base_web_auth.dart'; BaseWebAuth createWebAuth() => IoWebAuth(); class IoWebAuth implements BaseWebAuth { @override - Future authenticate( - {required String callbackUrlScheme, - required String url, - required String redirectUrl, - Map? opts}) async { - return await FlutterWebAuth2.authenticate( + Future authenticate({ + required String callbackUrlScheme, + required String url, + required String redirectUrl, + Map? opts, + }) async { + return FlutterWebAuth2.authenticate( callbackUrlScheme: callbackUrlScheme, url: url, - preferEphemeral: (opts?['preferEphemeral'] == true), + options: FlutterWebAuth2Options.fromJson(opts ?? {}), ); } } diff --git a/lib/src/oauth2_utils.dart b/lib/src/oauth2_utils.dart index f4bb6d8..f7819a5 100644 --- a/lib/src/oauth2_utils.dart +++ b/lib/src/oauth2_utils.dart @@ -5,9 +5,9 @@ import 'package:crypto/crypto.dart'; class OAuth2Utils { /// Generates a code challenge from the [codeVerifier] used for the PKCE extension static String generateCodeChallenge(String codeVerifier) { - var bytes = utf8.encode(codeVerifier); + final bytes = utf8.encode(codeVerifier); - var digest = sha256.convert(bytes); + final digest = sha256.convert(bytes); var codeChallenge = base64UrlEncode(digest.bytes); @@ -40,7 +40,7 @@ class OAuth2Utils { } static String addParamsToUrl(String url, Map params) { - var qs = params2qs(params); + final qs = params2qs(params); if (qs.isNotEmpty) url = '$url?$qs'; diff --git a/lib/src/secure_storage.dart b/lib/src/secure_storage.dart index 4b37236..26e9046 100644 --- a/lib/src/secure_storage.dart +++ b/lib/src/secure_storage.dart @@ -4,23 +4,22 @@ import 'package:oauth2_client/src/base_storage.dart'; BaseStorage createStorage() => SecureStorage(); class SecureStorage implements BaseStorage { + SecureStorage(); static const FlutterSecureStorage storage = FlutterSecureStorage( aOptions: AndroidOptions(encryptedSharedPreferences: true), ); - SecureStorage(); - @override Future read(String key) async { const options = IOSOptions(accessibility: KeychainAccessibility.first_unlock); - return await storage.read(key: key, iOptions: options); + return storage.read(key: key, iOptions: options); } @override Future write(String key, String value) async { const options = IOSOptions(accessibility: KeychainAccessibility.first_unlock); - return await storage.write(key: key, value: value, iOptions: options); + return storage.write(key: key, value: value, iOptions: options); } } diff --git a/lib/src/storage.dart b/lib/src/storage.dart index 0a539b7..bab71e9 100644 --- a/lib/src/storage.dart +++ b/lib/src/storage.dart @@ -1,5 +1,6 @@ -import 'base_storage.dart'; +import 'package:oauth2_client/src/base_storage.dart'; /// Implemented in `browser_client.dart` and `io_client.dart`. BaseStorage createStorage() => throw UnsupportedError( - 'Cannot create a storage without dart:html or dart:io.'); + 'Cannot create a storage without dart:html or dart:io.', + ); diff --git a/lib/src/token_storage.dart b/lib/src/token_storage.dart index c467f4c..176593a 100644 --- a/lib/src/token_storage.dart +++ b/lib/src/token_storage.dart @@ -1,22 +1,20 @@ import 'dart:convert'; import 'package:oauth2_client/access_token_response.dart'; - -import 'base_storage.dart'; -import 'storage.dart' +import 'package:oauth2_client/src/base_storage.dart'; +import 'package:oauth2_client/src/storage.dart' // ignore: uri_does_not_exist if (dart.library.io) 'secure_storage.dart' // ignore: uri_does_not_exist if (dart.library.html) 'browser_storage.dart'; class TokenStorage { - String key; - - BaseStorage storage = createStorage(); - TokenStorage(this.key, {BaseStorage? storage}) { if (storage != null) this.storage = storage; } + String key; + + BaseStorage storage = createStorage(); /// Looks for a token in the storage that matches the required [scopes]. /// If a token in the storage has been generated for a superset of the requested scopes, it is considered valid. @@ -31,27 +29,30 @@ class TokenStorage { final cleanScopes = clearScopes(scopes); - var tknMap = storedTokens.values.firstWhere((tkn) { - var found = false; - - if (cleanScopes.isEmpty) { - //If the scopes are empty, onlty tokens granted to empty scopes are considered valid... - found = (tkn['scope'] == null || tkn['scope'].isEmpty); - } else { - //...Otherwise look for a token granted to a superset of the requested scopes - if (tkn.containsKey('scope')) { - final tknCleanScopes = clearScopes(tkn['scope'].cast()); - - if (tknCleanScopes.isNotEmpty) { - var s1 = Set.from(tknCleanScopes); - var s2 = Set.from(cleanScopes); - found = s1.intersection(s2).length == cleanScopes.length; + final tknMap = storedTokens.values.firstWhere( + (tkn) { + var found = false; + + if (cleanScopes.isEmpty) { + //If the scopes are empty, onlty tokens granted to empty scopes are considered valid... + found = tkn['scope'] == null || tkn['scope'].isEmpty; + } else { + //...Otherwise look for a token granted to a superset of the requested scopes + if (tkn.containsKey('scope')) { + final tknCleanScopes = clearScopes(tkn['scope'].cast()); + + if (tknCleanScopes.isNotEmpty) { + final s1 = Set.of(tknCleanScopes); + final s2 = Set.of(cleanScopes); + found = s1.intersection(s2).length == cleanScopes.length; + } } } - } - return found; - }, orElse: () => null); + return found; + }, + orElse: () => null, + ); if (tknMap != null) tknResp = AccessTokenResponse.fromMap(tknMap); } @@ -60,7 +61,7 @@ class TokenStorage { } Future addToken(AccessTokenResponse tknResp) async { - var tokens = await insertToken(tknResp); + final tokens = await insertToken(tknResp); await storage.write(key, jsonEncode(tokens)); } @@ -109,10 +110,10 @@ class TokenStorage { return scopes.where((element) => element.trim().isNotEmpty).toList(); } - List getSortedScopes(List scopes) { - var sortedScopes = []; + List getSortedScopes(List scopes) { + var sortedScopes = []; - var cleanScopes = clearScopes(scopes); + final cleanScopes = clearScopes(scopes); if (cleanScopes.isNotEmpty) { sortedScopes = cleanScopes.toList() @@ -125,7 +126,7 @@ class TokenStorage { String getScopeKey(List scope) { var key = '_default_'; - var sortedScopes = getSortedScopes(scope); + final sortedScopes = getSortedScopes(scope); if (sortedScopes.isNotEmpty) { key = sortedScopes.join('__'); } diff --git a/lib/src/volatile_storage.dart b/lib/src/volatile_storage.dart index da7fb68..049c858 100644 --- a/lib/src/volatile_storage.dart +++ b/lib/src/volatile_storage.dart @@ -3,9 +3,8 @@ import 'package:oauth2_client/src/base_storage.dart'; BaseStorage createStorage() => VolatileStorage(); class VolatileStorage implements BaseStorage { - final Map storage = {}; - VolatileStorage(); + final Map storage = {}; @override Future read(String key) async { diff --git a/lib/src/web_auth.dart b/lib/src/web_auth.dart index f4bc767..08c7bb0 100644 --- a/lib/src/web_auth.dart +++ b/lib/src/web_auth.dart @@ -1,5 +1,6 @@ -import 'base_web_auth.dart'; +import 'package:oauth2_client/src/base_web_auth.dart'; /// Implemented in `browser_client.dart` and `io_client.dart`. BaseWebAuth createWebAuth() => throw UnsupportedError( - 'Cannot create a web auth without dart:html or dart:io.'); + 'Cannot create a web auth without dart:html or dart:io.', + ); diff --git a/lib/twitter_oauth2_client.dart b/lib/twitter_oauth2_client.dart index 898fbd3..3934be0 100644 --- a/lib/twitter_oauth2_client.dart +++ b/lib/twitter_oauth2_client.dart @@ -1,12 +1,12 @@ import 'package:oauth2_client/oauth2_client.dart'; class TwitterOAuth2Client extends OAuth2Client { - TwitterOAuth2Client( - {required String redirectUri, required String customUriScheme}) - : super( - authorizeUrl: 'https://twitter.com/i/oauth2/authorize', - tokenUrl: 'https://api.twitter.com/2/oauth2/token', - revokeUrl: 'https://api.twitter.com/oauth2/invalidate_token', - redirectUri: redirectUri, - customUriScheme: customUriScheme); + TwitterOAuth2Client({ + required super.redirectUri, + required super.customUriScheme, + }) : super( + authorizeUrl: 'https://twitter.com/i/oauth2/authorize', + tokenUrl: 'https://api.twitter.com/2/oauth2/token', + revokeUrl: 'https://api.twitter.com/oauth2/invalidate_token', + ); } diff --git a/pubspec.yaml b/pubspec.yaml index 26c8255..c9839a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,22 +5,22 @@ version: 3.2.2 homepage: https://github.com/teranetsrl/oauth2_client repository: https://github.com/teranetsrl/oauth2_client environment: - sdk: '>=2.14.0 <3.11.0' + sdk: '>=3.0.0 <4.0.0' dependencies: + crypto: ^3.0.3 flutter: sdk: flutter - crypto: ^3.0.3 - flutter_secure_storage: ^8.0.0 - flutter_web_auth_2: ^2.2.1 + flutter_secure_storage: ^9.2.2 + flutter_web_auth_2: ^3.1.2 http: ^1.1.0 meta: ^1.8.0 random_string: ^2.3.1 dev_dependencies: + build_runner: ^2.1.7 flutter_test: sdk: flutter - build_runner: ^2.1.7 mockito: ^5.4.2 flutter_lints: ^2.0.0 diff --git a/test/access_token_response_test.dart b/test/access_token_response_test.dart index 5c74fcb..a6a8ffe 100644 --- a/test/access_token_response_test.dart +++ b/test/access_token_response_test.dart @@ -17,7 +17,7 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }; final resp = AccessTokenResponse.fromMap(respMap); @@ -40,7 +40,7 @@ void main() { 'http_status_code': 200, 'expiration_date': DateTime.now() .add(const Duration(seconds: 1)) - .millisecondsSinceEpoch + .millisecondsSinceEpoch, }; final resp = AccessTokenResponse.fromMap(respMap); @@ -62,20 +62,22 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }; final resp = AccessTokenResponse.fromMap(respMap); expect( - resp.toMap(), - allOf( - containsPair('http_status_code', 200), - containsPair('access_token', accessToken), - containsPair('token_type', tokenType), - containsPair('refresh_token', refreshToken), - containsPair('scope', scopes), - containsPair('expires_in', expiresIn))); + resp.toMap(), + allOf( + containsPair('http_status_code', 200), + containsPair('access_token', accessToken), + containsPair('token_type', tokenType), + containsPair('refresh_token', refreshToken), + containsPair('scope', scopes), + containsPair('expires_in', expiresIn), + ), + ); }); }); @@ -83,16 +85,18 @@ void main() { //The OAuth 2 standard suggests that the scopes should be a space-separated list, //but some providers (i.e. GitHub) return a comma-separated list var response = http.Response( - '{"access_token": "TKN12345", "token_type": "Bearer", "scope": "scope1 scope2"}', - 200); + '{"access_token": "TKN12345", "token_type": "Bearer", "scope": "scope1 scope2"}', + 200, + ); var resp = AccessTokenResponse.fromHttpResponse(response); expect(resp.scope, ['scope1', 'scope2']); response = http.Response( - '{"access_token": "TKN12345", "token_type": "Bearer", "scope": "scope1,scope2"}', - 200); + '{"access_token": "TKN12345", "token_type": "Bearer", "scope": "scope1,scope2"}', + 200, + ); resp = AccessTokenResponse.fromHttpResponse(response); @@ -102,11 +106,15 @@ void main() { test('Conversion from HTTP response with no "scope" parameter', () async { //If no scope parameter is sent by the server in the Access Token Response //it means that it is identical to the one(s) requested by the client - var response = http.Response( - '{"access_token": "TKN12345", "token_type": "Bearer"}', 200); + final response = http.Response( + '{"access_token": "TKN12345", "token_type": "Bearer"}', + 200, + ); - var resp = AccessTokenResponse.fromHttpResponse(response, - requestedScopes: ['scope1', 'scope2']); + var resp = AccessTokenResponse.fromHttpResponse( + response, + requestedScopes: ['scope1', 'scope2'], + ); expect(resp.scope, ['scope1', 'scope2']); @@ -125,7 +133,7 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }; final resp = AccessTokenResponse.fromMap(respMap); @@ -137,7 +145,7 @@ void main() { final respMap = { 'error': 'generic_error', 'error_description': 'error_desc', - 'http_status_code': 400 + 'http_status_code': 400, }; final resp = AccessTokenResponse.fromMap(respMap); @@ -152,7 +160,7 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': '3600', - 'http_status_code': 200 + 'http_status_code': 200, }; final resp = AccessTokenResponse.fromMap(respMap); @@ -165,7 +173,7 @@ void main() { final respMap = { 'token_type': tokenType, 'http_status_code': 200, - 'custom_param': 'custom_val' + 'custom_param': 'custom_val', }; final resp = AccessTokenResponse.fromMap(respMap); diff --git a/test/authorization_response_test.dart b/test/authorization_response_test.dart index 8662671..cddac0b 100644 --- a/test/authorization_response_test.dart +++ b/test/authorization_response_test.dart @@ -26,22 +26,28 @@ void main() { test('Bad response (no code param)', () { const url = 'myurlscheme:/oauth2?state=$state'; - expect(() => AuthorizationResponse.fromRedirectUri(url, state), - throwsException); + expect( + () => AuthorizationResponse.fromRedirectUri(url, state), + throwsException, + ); }); test('Bad response (no state param)', () { const url = 'myurlscheme:/oauth2?code=$authCode'; - expect(() => AuthorizationResponse.fromRedirectUri(url, state), - throwsException); + expect( + () => AuthorizationResponse.fromRedirectUri(url, state), + throwsException, + ); }); test('Bad response (wrong state param)', () { const url = 'myurlscheme:/oauth2?code=$authCode&state=WRONGSTATE'; - expect(() => AuthorizationResponse.fromRedirectUri(url, state), - throwsException); + expect( + () => AuthorizationResponse.fromRedirectUri(url, state), + throwsException, + ); }); test('Fetch query parameters', () { diff --git a/test/oauth2_client_test.dart b/test/oauth2_client_test.dart index 69c55a8..fe04321 100644 --- a/test/oauth2_client_test.dart +++ b/test/oauth2_client_test.dart @@ -37,34 +37,38 @@ void main() { group('Authorization Code Grant.', () { final oauth2Client = OAuth2Client( - authorizeUrl: authorizeUrl, - tokenUrl: tokenUrl, - redirectUri: redirectUri, - customUriScheme: customUriScheme); + authorizeUrl: authorizeUrl, + tokenUrl: tokenUrl, + redirectUri: redirectUri, + customUriScheme: customUriScheme, + ); test('Authorization Request', () async { - var authParams = { + final authParams = { 'response_type': 'code', 'client_id': clientId, 'redirect_uri': redirectUri, 'scope': scopes, 'state': state, 'code_challenge': codeChallenge, - 'code_challenge_method': 'S256' + 'code_challenge_method': 'S256', }; - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri)) - .thenAnswer((_) async => '$redirectUri?code=$authCode&state=$state'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + ), + ).thenAnswer((_) async => '$redirectUri?code=$authCode&state=$state'); final authResponse = await oauth2Client.requestAuthorization( - webAuthClient: webAuthClient, - clientId: clientId, - scopes: scopes, - codeChallenge: codeChallenge, - state: state); + webAuthClient: webAuthClient, + clientId: clientId, + scopes: scopes, + codeChallenge: codeChallenge, + state: state, + ); expect(authResponse.code, authCode); }); @@ -75,7 +79,7 @@ void main() { const accessToken = '12345'; const refreshToken = '54321'; - var tokenParams = { + final tokenParams = { 'grant_type': 'authorization_code', 'code': authCode, 'redirect_uri': redirectUri, @@ -83,23 +87,36 @@ void main() { 'code_verifier': codeVerifier, }; - when(httpClient.post(Uri.parse(tokenUrl), - body: tokenParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response( - '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', - 200)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response( + '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', + 200, + ), + ); final tknResponse = await oauth2Client.requestAccessToken( - httpClient: httpClient, - code: authCode, - clientId: clientId, - codeVerifier: codeVerifier); + httpClient: httpClient, + code: authCode, + clientId: clientId, + codeVerifier: codeVerifier, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: tokenParams, headers: captureAnyNamed('headers'))) - .captured[0], - {}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {}, + ); expect(tknResponse.accessToken, accessToken); }); @@ -110,7 +127,7 @@ void main() { const accessToken = '12345'; const refreshToken = '54321'; - var tokenParams = { + final tokenParams = { 'grant_type': 'authorization_code', 'code': authCode, 'redirect_uri': redirectUri, @@ -118,67 +135,86 @@ void main() { 'code_verifier': codeVerifier, }; - when(httpClient.post(Uri.parse(tokenUrl), - body: tokenParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response( - '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', - 200)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response( + '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', + 200, + ), + ); final tknResponse = await oauth2Client.requestAccessToken( - httpClient: httpClient, - code: authCode, - clientId: clientId, - customHeaders: {'test': '42'}, - codeVerifier: codeVerifier); + httpClient: httpClient, + code: authCode, + clientId: clientId, + customHeaders: {'test': '42'}, + codeVerifier: codeVerifier, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: tokenParams, headers: captureAnyNamed('headers'))) - .captured[0], - {'test': '42'}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'test': '42'}, + ); expect(tknResponse.accessToken, accessToken); }); test('Fetch access token with preferEphemeral', () async { - var authParams = { + final authParams = { 'response_type': 'code', 'client_id': clientId, 'redirect_uri': redirectUri, 'scope': scopes, 'code_challenge': codeChallenge, - 'code_challenge_method': 'S256' + 'code_challenge_method': 'S256', }; - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri, - opts: captureAnyNamed('opts'))) - .thenAnswer((_) async => '$redirectUri?code=$authCode'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + opts: captureAnyNamed('opts'), + ), + ).thenAnswer((_) async => '$redirectUri?code=$authCode'); await oauth2Client.requestAuthorization( - webAuthClient: webAuthClient, - clientId: clientId, - scopes: scopes, - codeChallenge: codeChallenge, - enableState: false, - webAuthOpts: {'preferEphemeral': true}); + webAuthClient: webAuthClient, + clientId: clientId, + scopes: scopes, + codeChallenge: codeChallenge, + enableState: false, + webAuthOpts: {'preferEphemeral': true}, + ); expect( - verify(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri, - opts: captureAnyNamed('opts'))) - .captured[0], - {'preferEphemeral': true}); + verify( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + opts: captureAnyNamed('opts'), + ), + ).captured[0], + {'preferEphemeral': true}, + ); }); test('Error fetching Access Token', () async { final httpClient = MockClient(); - var tokenParams = { + final tokenParams = { 'grant_type': 'authorization_code', 'code': authCode, 'redirect_uri': redirectUri, @@ -187,22 +223,27 @@ void main() { // 'client_secret': clientSecret }; - when(httpClient.post(Uri.parse(tokenUrl), - body: tokenParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 404)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('', 404)); final tknResponse = await oauth2Client.requestAccessToken( - httpClient: httpClient, - code: authCode, - clientId: clientId, - // clientSecret: clientSecret, - codeVerifier: codeVerifier); + httpClient: httpClient, + code: authCode, + clientId: clientId, + // clientSecret: clientSecret, + codeVerifier: codeVerifier, + ); expect(tknResponse.isValid(), false); }); test('Token request with authorization code flow', () async { - var authParams = { + final authParams = { 'response_type': 'code', 'client_id': clientId, 'redirect_uri': redirectUri, @@ -210,7 +251,7 @@ void main() { 'state': state, 'code_challenge': codeChallenge, 'code_challenge_method': 'S256', - 'testParam': 'testVal' + 'testParam': 'testVal', }; final httpClient = MockClient(); @@ -218,7 +259,7 @@ void main() { const accessToken = '12345'; const refreshToken = '54321'; - var tokenParams = { + final tokenParams = { 'grant_type': 'authorization_code', 'code': authCode, 'redirect_uri': redirectUri, @@ -227,39 +268,49 @@ void main() { // 'client_secret': clientSecret }; - when(httpClient.post(Uri.parse(tokenUrl), - body: tokenParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response( - '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', - 200)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response( + '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', + 200, + ), + ); - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri)) - .thenAnswer((_) async => '$redirectUri?code=$authCode&state=$state'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + ), + ).thenAnswer((_) async => '$redirectUri?code=$authCode&state=$state'); final tknResponse = await oauth2Client.getTokenWithAuthCodeFlow( - webAuthClient: webAuthClient, - httpClient: httpClient, - clientId: clientId, - scopes: scopes, - state: state, - codeVerifier: codeVerifier, - authCodeParams: {'testParam': 'testVal'}); + webAuthClient: webAuthClient, + httpClient: httpClient, + clientId: clientId, + scopes: scopes, + state: state, + codeVerifier: codeVerifier, + authCodeParams: {'testParam': 'testVal'}, + ); expect(tknResponse.accessToken, accessToken); }); test('Authorization code flow with callback', () async { - var authParams = { + final authParams = { 'response_type': 'code', 'client_id': clientId, 'redirect_uri': redirectUri, 'scope': scopes, 'state': state, 'code_challenge': codeChallenge, - 'code_challenge_method': 'S256' + 'code_challenge_method': 'S256', }; final httpClient = MockClient(); @@ -267,7 +318,7 @@ void main() { const accessToken = '12345'; const refreshToken = '54321'; - var tokenParams = { + final tokenParams = { 'grant_type': 'authorization_code', 'code': authCode, 'redirect_uri': redirectUri, @@ -276,28 +327,38 @@ void main() { // 'client_secret': clientSecret }; - when(httpClient.post(Uri.parse('https://test.token.url'), - body: tokenParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response( - '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', - 200)); + when( + httpClient.post( + Uri.parse('https://test.token.url'), + body: tokenParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response( + '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', + 200, + ), + ); - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri)) - .thenAnswer((_) async => '$redirectUri?code=$authCode&state=$state'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + ), + ).thenAnswer((_) async => '$redirectUri?code=$authCode&state=$state'); await oauth2Client.getTokenWithAuthCodeFlow( - webAuthClient: webAuthClient, - httpClient: httpClient, - clientId: clientId, - scopes: scopes, - state: state, - codeVerifier: codeVerifier, - afterAuthorizationCodeCb: (authResp) { - oauth2Client.tokenUrl = 'https://test.token.url'; - }); + webAuthClient: webAuthClient, + httpClient: httpClient, + clientId: clientId, + scopes: scopes, + state: state, + codeVerifier: codeVerifier, + afterAuthorizationCodeCb: (authResp) { + oauth2Client.tokenUrl = 'https://test.token.url'; + }, + ); expect(oauth2Client.tokenUrl, 'https://test.token.url'); @@ -307,27 +368,39 @@ void main() { test('Refresh token', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(tokenUrl), - body: { - 'grant_type': 'refresh_token', - 'refresh_token': refreshToken, - }, - headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response( - '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', - 200)); - - var resp = await oauth2Client.refreshToken(refreshToken, - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: { + 'grant_type': 'refresh_token', + 'refresh_token': refreshToken, + }, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response( + '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', + 200, + ), + ); + + final resp = await oauth2Client.refreshToken( + refreshToken, + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[1], - {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[1], + {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}, + ); expect(resp.isValid(), true); expect(resp.accessToken, accessToken); @@ -336,25 +409,34 @@ void main() { test('Error in refreshing token', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(tokenUrl), - body: { - 'grant_type': 'refresh_token', - 'refresh_token': refreshToken, - }, - headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 404)); - - var resp = await oauth2Client.refreshToken(refreshToken, - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: { + 'grant_type': 'refresh_token', + 'refresh_token': refreshToken, + }, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('', 404)); + + final resp = await oauth2Client.refreshToken( + refreshToken, + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[1], - {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[1], + {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}, + ); expect(resp.isValid(), false); }); @@ -365,23 +447,30 @@ void main() { final urlParams = Uri.parse(authorizeUrl).queryParameters; expect( - urlParams, - allOf(containsPair('response_type', 'code'), - containsPair('client_id', clientId))); + urlParams, + allOf( + containsPair('response_type', 'code'), + containsPair('client_id', clientId), + ), + ); }); test('Authorization url params (2/5)', () { final authorizeUrl = oauth2Client.getAuthorizeUrl( - clientId: clientId, redirectUri: redirectUri); + clientId: clientId, + redirectUri: redirectUri, + ); final urlParams = Uri.parse(authorizeUrl).queryParameters; expect( - urlParams, - allOf( - containsPair('response_type', 'code'), - containsPair('client_id', clientId), - containsPair('redirect_uri', redirectUri))); + urlParams, + allOf( + containsPair('response_type', 'code'), + containsPair('client_id', clientId), + containsPair('redirect_uri', redirectUri), + ), + ); }); test('Authorization url params (3/5)', () { @@ -394,49 +483,54 @@ void main() { final urlParams = Uri.parse(authorizeUrl).queryParameters; expect( - urlParams, - allOf( - containsPair('response_type', 'code'), - containsPair('client_id', clientId), - containsPair('redirect_uri', redirectUri), - containsPair('scope', scopes.join(' ')), - )); + urlParams, + allOf( + containsPair('response_type', 'code'), + containsPair('client_id', clientId), + containsPair('redirect_uri', redirectUri), + containsPair('scope', scopes.join(' ')), + ), + ); }); test('Authorization url params (4/5)', () { final authorizeUrl = oauth2Client.getAuthorizeUrl( - clientId: clientId, - redirectUri: redirectUri, - scopes: scopes, - state: state); + clientId: clientId, + redirectUri: redirectUri, + scopes: scopes, + state: state, + ); final urlParams = Uri.parse(authorizeUrl).queryParameters; expect( - urlParams, - allOf( - containsPair('response_type', 'code'), - containsPair('client_id', clientId), - containsPair('redirect_uri', redirectUri), - containsPair('scope', scopes.join(' ')), - containsPair('state', state), - )); + urlParams, + allOf( + containsPair('response_type', 'code'), + containsPair('client_id', clientId), + containsPair('redirect_uri', redirectUri), + containsPair('scope', scopes.join(' ')), + containsPair('state', state), + ), + ); }); test('Authorization url params (5/5)', () { final authorizeUrl = oauth2Client.getAuthorizeUrl( - clientId: clientId, - redirectUri: redirectUri, - scopes: scopes, - state: state, - codeChallenge: codeChallenge, - customParams: {'authparm1': 'test1', 'authparm2': '5'}); + clientId: clientId, + redirectUri: redirectUri, + scopes: scopes, + state: state, + codeChallenge: codeChallenge, + customParams: {'authparm1': 'test1', 'authparm2': '5'}, + ); final urlParams = Uri.parse(authorizeUrl).queryParameters; expect( - urlParams, - allOf([ + urlParams, + allOf( + [ containsPair('response_type', 'code'), containsPair('client_id', clientId), containsPair('redirect_uri', redirectUri), @@ -445,101 +539,122 @@ void main() { containsPair('code_challenge', codeChallenge), containsPair('code_challenge_method', 'S256'), containsPair('authparm1', 'test1'), - containsPair('authparm2', '5') - ])); + containsPair('authparm2', '5'), + ], + ), + ); }); test('Token url params (1/5)', () { final params = oauth2Client.getTokenUrlParams(code: authorizationCode); expect( - params, - allOf(containsPair('grant_type', 'authorization_code'), - containsPair('code', authorizationCode))); + params, + allOf( + containsPair('grant_type', 'authorization_code'), + containsPair('code', authorizationCode), + ), + ); }); test('Token url params (2/5)', () { final params = oauth2Client.getTokenUrlParams( - code: authorizationCode, redirectUri: redirectUri); + code: authorizationCode, + redirectUri: redirectUri, + ); expect( - params, - allOf( - containsPair('grant_type', 'authorization_code'), - containsPair('code', authorizationCode), - containsPair('redirect_uri', redirectUri))); + params, + allOf( + containsPair('grant_type', 'authorization_code'), + containsPair('code', authorizationCode), + containsPair('redirect_uri', redirectUri), + ), + ); }); test('Token url params (3/5)', () { final params = oauth2Client.getTokenUrlParams( - code: authorizationCode, redirectUri: redirectUri); + code: authorizationCode, + redirectUri: redirectUri, + ); expect( - params, - allOf( - containsPair('grant_type', 'authorization_code'), - containsPair('code', authorizationCode), - containsPair('redirect_uri', redirectUri))); + params, + allOf( + containsPair('grant_type', 'authorization_code'), + containsPair('code', authorizationCode), + containsPair('redirect_uri', redirectUri), + ), + ); }); test('Token url params (4/5)', () { final params = oauth2Client.getTokenUrlParams( - code: authorizationCode, redirectUri: redirectUri); + code: authorizationCode, + redirectUri: redirectUri, + ); expect( - params, - allOf( - containsPair('grant_type', 'authorization_code'), - containsPair('code', authorizationCode), - containsPair('redirect_uri', redirectUri), - )); + params, + allOf( + containsPair('grant_type', 'authorization_code'), + containsPair('code', authorizationCode), + containsPair('redirect_uri', redirectUri), + ), + ); }); test('Token url params (5/5)', () { const verifier = 'test_verifier'; final params = oauth2Client.getTokenUrlParams( - code: authorizationCode, - redirectUri: redirectUri, - codeVerifier: verifier, - customParams: {'accTknparm1': 'test1', 'accTknparm2': '5'}); + code: authorizationCode, + redirectUri: redirectUri, + codeVerifier: verifier, + customParams: {'accTknparm1': 'test1', 'accTknparm2': '5'}, + ); expect( - params, - allOf([ - containsPair('grant_type', 'authorization_code'), - containsPair('code', authorizationCode), - containsPair('redirect_uri', redirectUri), - // containsPair('client_id', clientId), - // containsPair('client_secret', clientSecret), - containsPair('code_verifier', verifier), - containsPair('accTknparm1', 'test1'), - containsPair('accTknparm2', '5'), - ])); + params, + allOf([ + containsPair('grant_type', 'authorization_code'), + containsPair('code', authorizationCode), + containsPair('redirect_uri', redirectUri), + // containsPair('client_id', clientId), + // containsPair('client_secret', clientSecret), + containsPair('code_verifier', verifier), + containsPair('accTknparm1', 'test1'), + containsPair('accTknparm2', '5'), + ]), + ); }); test('Disabled state parameter', () async { - var authParams = { + final authParams = { 'response_type': 'code', 'client_id': clientId, 'redirect_uri': redirectUri, 'scope': scopes, 'code_challenge': codeChallenge, - 'code_challenge_method': 'S256' + 'code_challenge_method': 'S256', }; - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri)) - .thenAnswer((_) async => '$redirectUri?code=$authCode'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + ), + ).thenAnswer((_) async => '$redirectUri?code=$authCode'); final authResponse = await oauth2Client.requestAuthorization( - webAuthClient: webAuthClient, - clientId: clientId, - scopes: scopes, - codeChallenge: codeChallenge, - enableState: false); + webAuthClient: webAuthClient, + clientId: clientId, + scopes: scopes, + codeChallenge: codeChallenge, + enableState: false, + ); expect(authResponse.code, authCode); }); @@ -547,10 +662,11 @@ void main() { group('Client Credentials Grant.', () { final oauth2Client = OAuth2Client( - authorizeUrl: authorizeUrl, - tokenUrl: tokenUrl, - redirectUri: redirectUri, - customUriScheme: customUriScheme); + authorizeUrl: authorizeUrl, + tokenUrl: tokenUrl, + redirectUri: redirectUri, + customUriScheme: customUriScheme, + ); test('Get new token', () async { final httpClient = MockClient(); @@ -563,24 +679,36 @@ void main() { // 'scope': scopes }; - when(httpClient.post(Uri.parse(tokenUrl), - body: authParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response( - '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', - 200)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: authParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response( + '{"access_token": "$accessToken", "token_type": "Bearer", "refresh_token": "$refreshToken", "expires_in": 3600}', + 200, + ), + ); final tknResponse = await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - // List scopes, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + // List scopes, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[1], - {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[1], + {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}, + ); expect(tknResponse.accessToken, accessToken); }); @@ -593,22 +721,31 @@ void main() { // 'scope': scopes }; - when(httpClient.post(Uri.parse(tokenUrl), - body: authParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 404)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: authParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('', 404)); final tknResponse = await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - // List scopes, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + // List scopes, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[1], - {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[1], + {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}, + ); expect(tknResponse.isValid(), false); }); @@ -616,7 +753,7 @@ void main() { group('Credentials location', () { test('Credentials in BODY', () async { - var oauth2Client = OAuth2Client( + final oauth2Client = OAuth2Client( authorizeUrl: authorizeUrl, tokenUrl: tokenUrl, redirectUri: redirectUri, @@ -629,79 +766,101 @@ void main() { final authParams = { 'grant_type': 'client_credentials', 'client_id': clientId, - 'client_secret': clientSecret + 'client_secret': clientSecret, }; - when(httpClient.post(Uri.parse(tokenUrl), - body: authParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 404)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: authParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('', 404)); await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[0], - { - 'grant_type': 'client_credentials', - 'client_id': clientId, - 'client_secret': clientSecret - }); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + { + 'grant_type': 'client_credentials', + 'client_id': clientId, + 'client_secret': clientSecret, + }, + ); }); test('Credentials in HEADER (explicit)', () async { - var oauth2Client = OAuth2Client( + final oauth2Client = OAuth2Client( authorizeUrl: authorizeUrl, tokenUrl: tokenUrl, redirectUri: redirectUri, customUriScheme: customUriScheme, - credentialsLocation: CredentialsLocation.header, ); final httpClient = MockClient(); final authParams = {'grant_type': 'client_credentials'}; - when(httpClient.post(Uri.parse(tokenUrl), - body: authParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 404)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: authParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('', 404)); await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[1], - {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[1], + {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}, + ); await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[0], - isNot({ - 'grant_type': 'client_credentials', - 'client_id': clientId, - 'client_secret': clientSecret - })); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + isNot({ + 'grant_type': 'client_credentials', + 'client_id': clientId, + 'client_secret': clientSecret, + }), + ); }); test('Credentials in HEADER (default behaviour)', () async { //This is an exact copy of the previous method, except for the client initialization... //It tests the default credentials location. - var oauth2Client = OAuth2Client( + final oauth2Client = OAuth2Client( authorizeUrl: authorizeUrl, tokenUrl: tokenUrl, redirectUri: redirectUri, @@ -712,46 +871,61 @@ void main() { final authParams = {'grant_type': 'client_credentials'}; - when(httpClient.post(Uri.parse(tokenUrl), - body: authParams, headers: captureAnyNamed('headers'))) - .thenAnswer((_) async => http.Response('', 404)); + when( + httpClient.post( + Uri.parse(tokenUrl), + body: authParams, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('', 404)); await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[1], - {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[1], + {'Authorization': 'Basic bXljbGllbnRpZDp0ZXN0X3NlY3JldA=='}, + ); await oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient); + clientId: clientId, + clientSecret: clientSecret, + httpClient: httpClient, + ); expect( - verify(httpClient.post(Uri.parse(tokenUrl), - body: captureAnyNamed('body'), - headers: captureAnyNamed('headers'))) - .captured[0], - isNot({ - 'grant_type': 'client_credentials', - 'client_id': clientId, - 'client_secret': clientSecret - })); + verify( + httpClient.post( + Uri.parse(tokenUrl), + body: captureAnyNamed('body'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + isNot({ + 'grant_type': 'client_credentials', + 'client_id': clientId, + 'client_secret': clientSecret, + }), + ); }); }); group('Implicit flow Grant.', () { final oauth2Client = OAuth2Client( - authorizeUrl: authorizeUrl, - tokenUrl: tokenUrl, - redirectUri: redirectUri, - customUriScheme: customUriScheme); + authorizeUrl: authorizeUrl, + tokenUrl: tokenUrl, + redirectUri: redirectUri, + customUriScheme: customUriScheme, + ); test('Get new token', () async { final httpClient = MockClient(); @@ -766,19 +940,24 @@ void main() { // 'scope': scopes }; - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri)) - .thenAnswer((_) async => - '$redirectUri#access_token=$accessToken&token_type=bearer&state=$state'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + ), + ).thenAnswer( + (_) async => + '$redirectUri#access_token=$accessToken&token_type=bearer&state=$state', + ); final tknResponse = await oauth2Client.getTokenWithImplicitGrantFlow( - clientId: clientId, - state: state, - // List scopes, - httpClient: httpClient, - webAuthClient: webAuthClient); + clientId: clientId, + state: state, + // List scopes, + httpClient: httpClient, + webAuthClient: webAuthClient, + ); expect(tknResponse.accessToken, accessToken); }); @@ -798,49 +977,63 @@ void main() { // 'scope': scopes }; - when(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri, - opts: captureAnyNamed('opts'))) - .thenAnswer((_) async => - '$redirectUri#access_token=$accessToken&token_type=bearer&state=$state'); + when( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + opts: captureAnyNamed('opts'), + ), + ).thenAnswer( + (_) async => + '$redirectUri#access_token=$accessToken&token_type=bearer&state=$state', + ); await oauth2Client.getTokenWithImplicitGrantFlow( - clientId: clientId, - state: state, - // List scopes, - httpClient: httpClient, - webAuthClient: webAuthClient, - webAuthOpts: {'preferEphemeral': true}); + clientId: clientId, + state: state, + // List scopes, + httpClient: httpClient, + webAuthClient: webAuthClient, + webAuthOpts: {'preferEphemeral': true}, + ); expect( - verify(webAuthClient.authenticate( - url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), - callbackUrlScheme: customUriScheme, - redirectUrl: redirectUri, - opts: captureAnyNamed('opts'))) - .captured[0], - {'preferEphemeral': true}); + verify( + webAuthClient.authenticate( + url: OAuth2Utils.addParamsToUrl(authorizeUrl, authParams), + callbackUrlScheme: customUriScheme, + redirectUrl: redirectUri, + opts: captureAnyNamed('opts'), + ), + ).captured[0], + {'preferEphemeral': true}, + ); }); }); group('Token revocation.', () { final oauth2Client = OAuth2Client( - authorizeUrl: authorizeUrl, - tokenUrl: tokenUrl, - revokeUrl: revokeUrl, - redirectUri: redirectUri, - customUriScheme: customUriScheme); + authorizeUrl: authorizeUrl, + tokenUrl: tokenUrl, + revokeUrl: revokeUrl, + redirectUri: redirectUri, + customUriScheme: customUriScheme, + ); test('Access token revocation', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(revokeUrl), body: { - 'token': accessToken, - 'token_type_hint': 'access_token', - 'client_id': clientId - })).thenAnswer((_) async => http.Response('{}', 200)); + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': accessToken, + 'token_type_hint': 'access_token', + 'client_id': clientId, + }, + ), + ).thenAnswer((_) async => http.Response('{}', 200)); final respMap = { 'access_token': accessToken, @@ -848,13 +1041,16 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final tknResp = AccessTokenResponse.fromMap(respMap); - final revokeResp = await oauth2Client.revokeAccessToken(tknResp, - clientId: clientId, httpClient: httpClient); + final revokeResp = await oauth2Client.revokeAccessToken( + tknResp, + clientId: clientId, + httpClient: httpClient, + ); expect(revokeResp.isValid(), true); }); @@ -862,14 +1058,17 @@ void main() { test('Refresh token revocation', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': refreshToken, - 'token_type_hint': 'refresh_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer((_) async => http.Response('{}', 200)); + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': refreshToken, + 'token_type_hint': 'refresh_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('{}', 200)); final respMap = { 'access_token': accessToken, @@ -877,13 +1076,16 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final tknResp = AccessTokenResponse.fromMap(respMap); - final revokeResp = await oauth2Client.revokeRefreshToken(tknResp, - clientId: clientId, httpClient: httpClient); + final revokeResp = await oauth2Client.revokeRefreshToken( + tknResp, + clientId: clientId, + httpClient: httpClient, + ); expect(revokeResp.isValid(), true); }); @@ -891,23 +1093,29 @@ void main() { test('Revoke both Access and Refresh token', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': accessToken, - 'token_type_hint': 'access_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer((_) async => http.Response('{}', 200)); - - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': refreshToken, - 'token_type_hint': 'refresh_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer((_) async => http.Response('{}', 200)); + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': accessToken, + 'token_type_hint': 'access_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('{}', 200)); + + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': refreshToken, + 'token_type_hint': 'refresh_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('{}', 200)); final respMap = { 'access_token': accessToken, @@ -915,13 +1123,16 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final tknResp = AccessTokenResponse.fromMap(respMap); - final revokeResp = await oauth2Client.revokeToken(tknResp, - clientId: clientId, httpClient: httpClient); + final revokeResp = await oauth2Client.revokeToken( + tknResp, + clientId: clientId, + httpClient: httpClient, + ); expect(revokeResp.isValid(), true); }); @@ -929,24 +1140,32 @@ void main() { test('Error in token revocation(1)', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': accessToken, - 'token_type_hint': 'access_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer((_) async => - http.Response('{"error": "access token revocation error"}', 404)); - - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': refreshToken, - 'token_type_hint': 'refresh_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer((_) async => http.Response('{}', 200)); + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': accessToken, + 'token_type_hint': 'access_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer( + (_) async => + http.Response('{"error": "access token revocation error"}', 404), + ); + + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': refreshToken, + 'token_type_hint': 'refresh_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('{}', 200)); final respMap = { 'access_token': accessToken, @@ -954,13 +1173,16 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final tknResp = AccessTokenResponse.fromMap(respMap); - final revokeResp = await oauth2Client.revokeToken(tknResp, - clientId: clientId, httpClient: httpClient); + final revokeResp = await oauth2Client.revokeToken( + tknResp, + clientId: clientId, + httpClient: httpClient, + ); expect(revokeResp.isValid(), false); }); @@ -968,24 +1190,31 @@ void main() { test('Error in token revocation(2)', () async { final httpClient = MockClient(); - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': accessToken, - 'token_type_hint': 'access_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer((_) async => http.Response('{}', 200)); - - when(httpClient.post(Uri.parse(revokeUrl), - body: { - 'token': refreshToken, - 'token_type_hint': 'refresh_token', - 'client_id': clientId - }, - headers: anyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "generic error"}', 400)); + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': accessToken, + 'token_type_hint': 'access_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer((_) async => http.Response('{}', 200)); + + when( + httpClient.post( + Uri.parse(revokeUrl), + body: { + 'token': refreshToken, + 'token_type_hint': 'refresh_token', + 'client_id': clientId, + }, + headers: anyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "generic error"}', 400), + ); final respMap = { 'access_token': accessToken, @@ -993,13 +1222,16 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final tknResp = AccessTokenResponse.fromMap(respMap); - final revokeResp = await oauth2Client.revokeToken(tknResp, - clientId: clientId, httpClient: httpClient); + final revokeResp = await oauth2Client.revokeToken( + tknResp, + clientId: clientId, + httpClient: httpClient, + ); expect(revokeResp.isValid(), false); }); @@ -1007,23 +1239,25 @@ void main() { group('Non standard providers.', () { test('Standard scope separator', () async { - var oauth2Client = OAuth2Client( - authorizeUrl: authorizeUrl, - tokenUrl: tokenUrl, - revokeUrl: revokeUrl, - redirectUri: redirectUri, - customUriScheme: customUriScheme); + final oauth2Client = OAuth2Client( + authorizeUrl: authorizeUrl, + tokenUrl: tokenUrl, + revokeUrl: revokeUrl, + redirectUri: redirectUri, + customUriScheme: customUriScheme, + ); expect(oauth2Client.serializeScopes(scopes), 'scope1 scope2'); }); test('Custom scope separator', () async { - var oauth2Client = OAuth2Client( - authorizeUrl: authorizeUrl, - tokenUrl: tokenUrl, - revokeUrl: revokeUrl, - redirectUri: redirectUri, - customUriScheme: customUriScheme, - scopeSeparator: '_'); + final oauth2Client = OAuth2Client( + authorizeUrl: authorizeUrl, + tokenUrl: tokenUrl, + revokeUrl: revokeUrl, + redirectUri: redirectUri, + customUriScheme: customUriScheme, + scopeSeparator: '_', + ); expect(oauth2Client.serializeScopes(scopes), 'scope1_scope2'); }); diff --git a/test/oauth2_helper_test.dart b/test/oauth2_helper_test.dart index 03facba..26bd461 100644 --- a/test/oauth2_helper_test.dart +++ b/test/oauth2_helper_test.dart @@ -34,35 +34,43 @@ void main() { when(oauth2Client.tokenUrl).thenReturn('http://my.test.app/token'); when(oauth2Client.revokeUrl).thenReturn('http://my.test.app/revoke'); - void mockGetTokenWithAuthCodeFlow(oauth2Client, - {Map? respMap}) { - var accessTokenMap = { + void mockGetTokenWithAuthCodeFlow( + OAuth2Client oauth2Client, { + Map? respMap, + }) { + final accessTokenMap = { 'access_token': accessToken, 'token_type': tokenType, 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }; if (respMap != null) { respMap.forEach((k, v) => accessTokenMap[k] = v); } - when(oauth2Client.getTokenWithAuthCodeFlow( - clientId: clientId, clientSecret: clientSecret, scopes: scopes)) - .thenAnswer((_) async => AccessTokenResponse.fromMap(accessTokenMap)); + when( + oauth2Client.getTokenWithAuthCodeFlow( + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + ), + ).thenAnswer((_) async => AccessTokenResponse.fromMap(accessTokenMap)); } - void mockGetTokenWithClientCredentials(oauth2Client, - {Map? respMap}) { - var accessTokenMap = { + void mockGetTokenWithClientCredentials( + OAuth2Client oauth2Client, { + Map? respMap, + }) { + final accessTokenMap = { 'access_token': accessToken, 'token_type': tokenType, 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }; if (respMap != null) { @@ -73,41 +81,57 @@ void main() { .add(Duration(seconds: accessTokenMap['expires_in'])) .millisecondsSinceEpoch; - when(oauth2Client.getTokenWithClientCredentialsFlow( - clientId: clientId, clientSecret: clientSecret, scopes: scopes)) - .thenAnswer((_) async => AccessTokenResponse.fromMap(accessTokenMap)); + when( + oauth2Client.getTokenWithClientCredentialsFlow( + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + ), + ).thenAnswer((_) async => AccessTokenResponse.fromMap(accessTokenMap)); } - void mockGetTokenWithImplicitFlow(oauth2Client, - {Map? respMap}) { - var accessTokenMap = { + void mockGetTokenWithImplicitFlow( + OAuth2Client oauth2Client, { + Map? respMap, + }) { + final accessTokenMap = { 'access_token': accessToken, 'token_type': tokenType, 'scope': scopes, // 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }; if (respMap != null) { respMap.forEach((k, v) => accessTokenMap[k] = v); } - when(oauth2Client.getTokenWithImplicitGrantFlow( - clientId: clientId, scopes: scopes)) - .thenAnswer((_) async => AccessTokenResponse.fromMap(accessTokenMap)); + when( + oauth2Client.getTokenWithImplicitGrantFlow( + clientId: clientId, + scopes: scopes, + ), + ).thenAnswer((_) async => AccessTokenResponse.fromMap(accessTokenMap)); } - void mockRefreshToken(oauth2Client) { - when(oauth2Client.refreshToken(refreshToken, - clientId: clientId, clientSecret: clientSecret, scopes: scopes)) - .thenAnswer((_) async => AccessTokenResponse.fromMap({ - 'access_token': renewedAccessToken, - 'token_type': tokenType, - 'refresh_token': refreshToken, - 'scope': scopes, - 'expires_in': 3600, - 'http_status_code': 200 - })); + void mockRefreshToken(OAuth2Client oauth2Client) { + when( + oauth2Client.refreshToken( + refreshToken, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + ), + ).thenAnswer( + (_) async => AccessTokenResponse.fromMap({ + 'access_token': renewedAccessToken, + 'token_type': tokenType, + 'refresh_token': refreshToken, + 'scope': scopes, + 'expires_in': 3600, + 'http_status_code': 200, + }), + ); } group('Authorization Code Grant.', () { @@ -117,14 +141,15 @@ void main() { mockGetTokenWithAuthCodeFlow(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); - var tknResp = await hlp.getToken(); + final tknResp = await hlp.getToken(); expect(tknResp?.isValid(), true); expect(tknResp?.accessToken, accessToken); @@ -134,21 +159,25 @@ void main() { final tokenStorage = TokenStorage(oauth2Client.tokenUrl, storage: VolatileStorage()); - mockGetTokenWithAuthCodeFlow(oauth2Client, respMap: { - 'expires_in': 1, - 'expiration_date': DateTime.now() - .add(const Duration(seconds: 1)) - .millisecondsSinceEpoch - }); + mockGetTokenWithAuthCodeFlow( + oauth2Client, + respMap: { + 'expires_in': 1, + 'expiration_date': DateTime.now() + .add(const Duration(seconds: 1)) + .millisecondsSinceEpoch, + }, + ); mockRefreshToken(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); var tknResp = await hlp.getToken(); expect(tknResp?.isValid(), true); expect(tknResp?.accessToken, accessToken); @@ -166,23 +195,26 @@ void main() { final tokenStorage = TokenStorage(oauth2Client.tokenUrl, storage: VolatileStorage()); - when(httpClient.post(Uri.parse('https://my.test.url'), - // headers: {'Authorization': 'Bearer ' + accessToken}, - headers: captureAnyNamed('headers'), - body: null, - encoding: null)) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); + when( + httpClient.post( + Uri.parse('https://my.test.url'), + // headers: {'Authorization': 'Bearer ' + accessToken}, + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); mockGetTokenWithAuthCodeFlow(oauth2Client); mockRefreshToken(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); var tknResp = await hlp.getToken(); @@ -202,23 +234,35 @@ void main() { mockGetTokenWithAuthCodeFlow(oauth2Client); - when(oauth2Client.refreshToken(refreshToken, - clientId: clientId, clientSecret: clientSecret)) - .thenAnswer((_) async => AccessTokenResponse.fromMap( - {'error': 'invalid_grant', 'http_status_code': 400})); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, + when( + oauth2Client.refreshToken( + refreshToken, clientId: clientId, clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); - - var tknResp = await hlp.refreshToken(AccessTokenResponse.fromMap({ - 'refresh_token': refreshToken, - 'http_status_code': 200, - 'access_token': accessToken - })); + ), + ).thenAnswer( + (_) async => AccessTokenResponse.fromMap( + {'error': 'invalid_grant', 'http_status_code': 400}, + ), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); + + final tknResp = await hlp.refreshToken( + AccessTokenResponse.fromMap( + { + 'refresh_token': refreshToken, + 'http_status_code': 200, + 'access_token': accessToken, + }, + ), + ); expect(tknResp.isValid(), true); expect(tknResp.accessToken, accessToken); @@ -231,17 +275,22 @@ void main() { mockGetTokenWithAuthCodeFlow(oauth2Client); mockRefreshToken(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); - - when(httpClient.get(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); + + when( + httpClient.get( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); var tknResp = await hlp.getToken(); @@ -261,26 +310,39 @@ void main() { mockGetTokenWithAuthCodeFlow(oauth2Client); - when(oauth2Client.refreshToken(refreshToken, - clientId: clientId, clientSecret: clientSecret)) - .thenAnswer((_) async => AccessTokenResponse.fromMap( - {'error': 'generic_error', 'http_status_code': 400})); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, + when( + oauth2Client.refreshToken( + refreshToken, clientId: clientId, clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + ), + ).thenAnswer( + (_) async => AccessTokenResponse.fromMap( + {'error': 'generic_error', 'http_status_code': 400}, + ), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); // expect(() async => await hlp.refreshToken(refreshToken), expect( - () async => await hlp.refreshToken(AccessTokenResponse.fromMap({ - 'refresh_token': refreshToken, - 'http_status_code': 200, - 'access_token': accessToken - })), - throwsA(isInstanceOf())); + () async => hlp.refreshToken( + AccessTokenResponse.fromMap( + { + 'refresh_token': refreshToken, + 'http_status_code': 200, + 'access_token': accessToken, + }, + ), + ), + throwsA(isInstanceOf()), + ); }); test('Test GET method with custom headers', () async { @@ -292,26 +354,38 @@ void main() { clearInteractions(httpClient); - when(httpClient.get(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); - - await hlp.get('https://my.test.url', - httpClient: httpClient, headers: {'TestHeader': 'test'}); + when( + httpClient.get( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); + + await hlp.get( + 'https://my.test.url', + httpClient: httpClient, + headers: {'TestHeader': 'test'}, + ); expect( - verify(httpClient.get(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'TestHeader': 'test', 'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.get( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'TestHeader': 'test', 'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test GET method without custom headers', () async { @@ -323,25 +397,34 @@ void main() { clearInteractions(httpClient); - when(httpClient.get(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + httpClient.get( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.get('https://my.test.url', httpClient: httpClient); expect( - verify(httpClient.get(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.get( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test POST method with custom headers', () async { @@ -353,26 +436,38 @@ void main() { clearInteractions(httpClient); - when(httpClient.post(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); - - await hlp.post('https://my.test.url', - httpClient: httpClient, headers: {'TestHeader': 'test'}); + when( + httpClient.post( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); + + await hlp.post( + 'https://my.test.url', + httpClient: httpClient, + headers: {'TestHeader': 'test'}, + ); expect( - verify(httpClient.post(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'TestHeader': 'test', 'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.post( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'TestHeader': 'test', 'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test POST method without custom headers', () async { @@ -384,25 +479,34 @@ void main() { clearInteractions(httpClient); - when(httpClient.post(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + httpClient.post( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.post('https://my.test.url', httpClient: httpClient); expect( - verify(httpClient.post(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.post( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test PUT method', () async { @@ -414,25 +518,34 @@ void main() { clearInteractions(httpClient); - when(httpClient.put(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + httpClient.put( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.put('https://my.test.url', httpClient: httpClient); expect( - verify(httpClient.put(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.put( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test PATCH method', () async { @@ -444,25 +557,34 @@ void main() { clearInteractions(httpClient); - when(httpClient.patch(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + httpClient.patch( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.patch('https://my.test.url', httpClient: httpClient); expect( - verify(httpClient.patch(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.patch( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test DELETE method with custom headers', () async { @@ -474,26 +596,38 @@ void main() { clearInteractions(httpClient); - when(httpClient.delete(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); - - await hlp.delete('https://my.test.url', - httpClient: httpClient, headers: {'TestHeader': 'test'}); + when( + httpClient.delete( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); + + await hlp.delete( + 'https://my.test.url', + httpClient: httpClient, + headers: {'TestHeader': 'test'}, + ); expect( - verify(httpClient.delete(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'TestHeader': 'test', 'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.delete( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'TestHeader': 'test', 'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test DELETE method without custom headers', () async { @@ -505,25 +639,34 @@ void main() { clearInteractions(httpClient); - when(httpClient.delete(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + httpClient.delete( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.delete('https://my.test.url', httpClient: httpClient); expect( - verify(httpClient.delete(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.delete( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Test HEAD method', () async { @@ -535,25 +678,34 @@ void main() { clearInteractions(httpClient); - when(httpClient.head(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + httpClient.head( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.head('https://my.test.url', httpClient: httpClient); expect( - verify(httpClient.head(Uri.parse('https://my.test.url'), - headers: captureAnyNamed('headers'))) - .captured[0], - {'Authorization': 'Bearer test_token_renewed'}); + verify( + httpClient.head( + Uri.parse('https://my.test.url'), + headers: captureAnyNamed('headers'), + ), + ).captured[0], + {'Authorization': 'Bearer test_token_renewed'}, + ); }); test('Token revocation', () async { @@ -563,26 +715,32 @@ void main() { 'refresh_token': refreshToken, 'scope': scopes, 'expires_in': expiresIn, - 'http_status_code': 200 + 'http_status_code': 200, }); final tokenStorage = MockTokenStorage(); when(tokenStorage.getToken(scopes)).thenAnswer((_) async => tknResp); when(tokenStorage.deleteToken(scopes)).thenAnswer((_) async => true); - when(oauth2Client.revokeToken(tknResp, - clientId: clientId, - clientSecret: clientSecret, - httpClient: httpClient)) - .thenAnswer( - (_) async => OAuth2Response.fromMap({'http_status_code': 200})); - - final hlp = OAuth2Helper(oauth2Client, - tokenStorage: tokenStorage, - grantType: OAuth2Helper.clientCredentials, + when( + oauth2Client.revokeToken( + tknResp, clientId: clientId, clientSecret: clientSecret, - scopes: scopes); + httpClient: httpClient, + ), + ).thenAnswer( + (_) async => OAuth2Response.fromMap({'http_status_code': 200}), + ); + + final hlp = OAuth2Helper( + oauth2Client, + tokenStorage: tokenStorage, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + ); final revokeResp = await hlp.disconnect(httpClient: httpClient); @@ -594,12 +752,14 @@ void main() { final tokenStorage = MockTokenStorage(); when(tokenStorage.getToken(scopes)).thenAnswer((_) async => null); - final hlp = OAuth2Helper(oauth2Client, - tokenStorage: tokenStorage, - grantType: OAuth2Helper.clientCredentials, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes); + final hlp = OAuth2Helper( + oauth2Client, + tokenStorage: tokenStorage, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + ); final revokeResp = await hlp.disconnect(httpClient: httpClient); @@ -610,60 +770,68 @@ void main() { final tokenStorage = TokenStorage(oauth2Client.tokenUrl, storage: VolatileStorage()); - when(oauth2Client.getTokenWithAuthCodeFlow( - clientId: 'test_client', - scopes: ['scope1', 'scope2'], - clientSecret: 'test_secret', - enablePKCE: false, - enableState: true, - )).thenAnswer((_) async => AccessTokenResponse()); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.authorizationCode, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + when( + oauth2Client.getTokenWithAuthCodeFlow( + clientId: 'test_client', + scopes: ['scope1', 'scope2'], + clientSecret: 'test_secret', + enablePKCE: false, + ), + ).thenAnswer((_) async => AccessTokenResponse()); + + final hlp = OAuth2Helper( + oauth2Client, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); await hlp.fetchToken(); expect( - verify(oauth2Client.getTokenWithAuthCodeFlow( - clientId: captureAnyNamed('clientId'), - clientSecret: captureAnyNamed('clientSecret'), - scopes: captureAnyNamed('scopes'), - enablePKCE: captureAnyNamed('enablePKCE'), - state: captureAnyNamed('state'), - codeVerifier: captureAnyNamed('codeVerifier'), - afterAuthorizationCodeCb: - captureAnyNamed('afterAuthorizationCodeCb'), - authCodeParams: captureAnyNamed('authCodeParams'), - accessTokenParams: captureAnyNamed('accessTokenParams'), - httpClient: captureAnyNamed('httpClient'), - webAuthClient: captureAnyNamed('webAuthClient'))) - .captured[3], - true); + verify( + oauth2Client.getTokenWithAuthCodeFlow( + clientId: captureAnyNamed('clientId'), + clientSecret: captureAnyNamed('clientSecret'), + scopes: captureAnyNamed('scopes'), + enablePKCE: captureAnyNamed('enablePKCE'), + state: captureAnyNamed('state'), + codeVerifier: captureAnyNamed('codeVerifier'), + afterAuthorizationCodeCb: + captureAnyNamed('afterAuthorizationCodeCb'), + authCodeParams: captureAnyNamed('authCodeParams'), + accessTokenParams: captureAnyNamed('accessTokenParams'), + httpClient: captureAnyNamed('httpClient'), + webAuthClient: captureAnyNamed('webAuthClient'), + ), + ).captured[3], + true, + ); hlp.enablePKCE = true; await hlp.fetchToken(); expect( - verify(oauth2Client.getTokenWithAuthCodeFlow( - clientId: captureAnyNamed('clientId'), - clientSecret: captureAnyNamed('clientSecret'), - scopes: captureAnyNamed('scopes'), - enablePKCE: captureAnyNamed('enablePKCE'), - state: captureAnyNamed('state'), - codeVerifier: captureAnyNamed('codeVerifier'), - afterAuthorizationCodeCb: - captureAnyNamed('afterAuthorizationCodeCb'), - authCodeParams: captureAnyNamed('authCodeParams'), - accessTokenParams: captureAnyNamed('accessTokenParams'), - httpClient: captureAnyNamed('httpClient'), - webAuthClient: captureAnyNamed('webAuthClient'))) - .captured[3], - true); + verify( + oauth2Client.getTokenWithAuthCodeFlow( + clientId: captureAnyNamed('clientId'), + clientSecret: captureAnyNamed('clientSecret'), + scopes: captureAnyNamed('scopes'), + enablePKCE: captureAnyNamed('enablePKCE'), + state: captureAnyNamed('state'), + codeVerifier: captureAnyNamed('codeVerifier'), + afterAuthorizationCodeCb: + captureAnyNamed('afterAuthorizationCodeCb'), + authCodeParams: captureAnyNamed('authCodeParams'), + accessTokenParams: captureAnyNamed('accessTokenParams'), + httpClient: captureAnyNamed('httpClient'), + webAuthClient: captureAnyNamed('webAuthClient'), + ), + ).captured[3], + true, + ); //enablePKCE param passed as false... Must be false in the client instance hlp.enablePKCE = false; @@ -671,21 +839,24 @@ void main() { await hlp.fetchToken(); expect( - verify(oauth2Client.getTokenWithAuthCodeFlow( - clientId: captureAnyNamed('clientId'), - clientSecret: captureAnyNamed('clientSecret'), - scopes: captureAnyNamed('scopes'), - enablePKCE: captureAnyNamed('enablePKCE'), - state: captureAnyNamed('state'), - codeVerifier: captureAnyNamed('codeVerifier'), - afterAuthorizationCodeCb: - captureAnyNamed('afterAuthorizationCodeCb'), - authCodeParams: captureAnyNamed('authCodeParams'), - accessTokenParams: captureAnyNamed('accessTokenParams'), - httpClient: captureAnyNamed('httpClient'), - webAuthClient: captureAnyNamed('webAuthClient'))) - .captured[3], - false); + verify( + oauth2Client.getTokenWithAuthCodeFlow( + clientId: captureAnyNamed('clientId'), + clientSecret: captureAnyNamed('clientSecret'), + scopes: captureAnyNamed('scopes'), + enablePKCE: captureAnyNamed('enablePKCE'), + state: captureAnyNamed('state'), + codeVerifier: captureAnyNamed('codeVerifier'), + afterAuthorizationCodeCb: + captureAnyNamed('afterAuthorizationCodeCb'), + authCodeParams: captureAnyNamed('authCodeParams'), + accessTokenParams: captureAnyNamed('accessTokenParams'), + httpClient: captureAnyNamed('httpClient'), + webAuthClient: captureAnyNamed('webAuthClient'), + ), + ).captured[3], + false, + ); }); }); @@ -696,14 +867,16 @@ void main() { mockGetTokenWithClientCredentials(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.clientCredentials, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + final hlp = OAuth2Helper( + oauth2Client, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); - var tknResp = await hlp.getToken(); + final tknResp = await hlp.getToken(); expect(tknResp?.isValid(), true); expect(tknResp?.accessToken, accessToken); @@ -713,17 +886,21 @@ void main() { final tokenStorage = TokenStorage(oauth2Client.tokenUrl, storage: VolatileStorage()); - mockGetTokenWithClientCredentials(oauth2Client, - respMap: {'expires_in': 1}); + mockGetTokenWithClientCredentials( + oauth2Client, + respMap: {'expires_in': 1}, + ); mockRefreshToken(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.clientCredentials, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + final hlp = OAuth2Helper( + oauth2Client, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); var tknResp = await hlp.getToken(); @@ -746,17 +923,23 @@ void main() { mockGetTokenWithClientCredentials(oauth2Client); mockRefreshToken(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.clientCredentials, - clientId: clientId, - clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); - - when(httpClient.post(Uri.parse('https://my.test.url'), - body: null, headers: {'Authorization': 'Bearer $accessToken'})) - .thenAnswer( - (_) async => http.Response('{"error": "invalid_token"}', 401)); + final hlp = OAuth2Helper( + oauth2Client, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); + + when( + httpClient.post( + Uri.parse('https://my.test.url'), + headers: {'Authorization': 'Bearer $accessToken'}, + ), + ).thenAnswer( + (_) async => http.Response('{"error": "invalid_token"}', 401), + ); var tknResp = await hlp.getToken(); @@ -776,24 +959,37 @@ void main() { mockGetTokenWithClientCredentials(oauth2Client); - when(oauth2Client.refreshToken(refreshToken, - clientId: clientId, clientSecret: clientSecret)) - .thenAnswer((_) async => AccessTokenResponse.fromMap( - {'error': 'invalid_grant', 'http_status_code': 400})); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.clientCredentials, + when( + oauth2Client.refreshToken( + refreshToken, clientId: clientId, clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + ), + ).thenAnswer( + (_) async => AccessTokenResponse.fromMap( + {'error': 'invalid_grant', 'http_status_code': 400}, + ), + ); + + final hlp = OAuth2Helper( + oauth2Client, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); // var tknResp = await hlp.refreshToken(refreshToken); - var tknResp = await hlp.refreshToken(AccessTokenResponse.fromMap({ - 'refresh_token': refreshToken, - 'http_status_code': 200, - 'access_token': accessToken - })); + final tknResp = await hlp.refreshToken( + AccessTokenResponse.fromMap( + { + 'refresh_token': refreshToken, + 'http_status_code': 200, + 'access_token': accessToken, + }, + ), + ); expect(tknResp.isValid(), true); expect(tknResp.accessToken, accessToken); @@ -807,31 +1003,41 @@ void main() { mockGetTokenWithClientCredentials(oauth2Client); - when(oauth2Client.refreshToken(refreshToken, - clientId: clientId, clientSecret: clientSecret)) - .thenAnswer((_) async => AccessTokenResponse.fromMap({ - 'access_token': accessToken, - 'token_type': tokenType, - 'expires_in': expiresIn, - 'expiration_date': DateTime.now() - .add(const Duration(seconds: expiresIn)) - .millisecondsSinceEpoch, - 'http_status_code': 200 - })); - - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.clientCredentials, + when( + oauth2Client.refreshToken( + refreshToken, clientId: clientId, clientSecret: clientSecret, - scopes: scopes, - tokenStorage: tokenStorage); + ), + ).thenAnswer( + (_) async => AccessTokenResponse.fromMap({ + 'access_token': accessToken, + 'token_type': tokenType, + 'expires_in': expiresIn, + 'expiration_date': DateTime.now() + .add(const Duration(seconds: expiresIn)) + .millisecondsSinceEpoch, + 'http_status_code': 200, + }), + ); + + final hlp = OAuth2Helper( + oauth2Client, + grantType: OAuth2Helper.clientCredentials, + clientId: clientId, + clientSecret: clientSecret, + scopes: scopes, + tokenStorage: tokenStorage, + ); // var tknResp = await hlp.refreshToken(refreshToken); - var tknResp = await hlp.refreshToken(AccessTokenResponse.fromMap({ - 'refresh_token': refreshToken, - 'http_status_code': 200, - 'access_token': accessToken - })); + final tknResp = await hlp.refreshToken( + AccessTokenResponse.fromMap({ + 'refresh_token': refreshToken, + 'http_status_code': 200, + 'access_token': accessToken, + }), + ); expect(tknResp.isValid(), true); expect(tknResp.refreshToken, refreshToken); @@ -845,13 +1051,15 @@ void main() { mockGetTokenWithImplicitFlow(oauth2Client); - var hlp = OAuth2Helper(oauth2Client, - grantType: OAuth2Helper.implicitGrant, - clientId: clientId, - scopes: scopes, - tokenStorage: tokenStorage); + final hlp = OAuth2Helper( + oauth2Client, + grantType: OAuth2Helper.implicitGrant, + clientId: clientId, + scopes: scopes, + tokenStorage: tokenStorage, + ); - var tknResp = await hlp.getToken(); + final tknResp = await hlp.getToken(); expect(tknResp?.isValid(), true); expect(tknResp?.accessToken, accessToken); diff --git a/test/oauth2_helper_test.mocks.dart b/test/oauth2_helper_test.mocks.dart index f08d8cc..f626a0c 100644 --- a/test/oauth2_helper_test.mocks.dart +++ b/test/oauth2_helper_test.mocks.dart @@ -995,13 +995,13 @@ class MockTokenStorage extends _i1.Mock implements _i12.TokenStorage { returnValue: [], ) as List); @override - List getSortedScopes(List? scopes) => (super.noSuchMethod( + List getSortedScopes(List? scopes) => (super.noSuchMethod( Invocation.method( #getSortedScopes, [scopes], ), - returnValue: [], - ) as List); + returnValue: [], + ) as List); @override String getScopeKey(List? scope) => (super.noSuchMethod( Invocation.method( diff --git a/test/oauth2_response_test.dart b/test/oauth2_response_test.dart index 3491a68..1fdad4a 100644 --- a/test/oauth2_response_test.dart +++ b/test/oauth2_response_test.dart @@ -15,7 +15,7 @@ void main() { final respMap = { 'error': 'ERROR', 'error_description': 'ERROR_DESC', - 'http_status_code': 400 + 'http_status_code': 400, }; final resp = OAuth2Response.fromMap(respMap); @@ -27,17 +27,19 @@ void main() { final respMap = { 'error': 'generic_error', 'error_description': 'err_desc', - 'http_status_code': 400 + 'http_status_code': 400, }; final resp = OAuth2Response.fromMap(respMap); expect( - resp.toMap(), - allOf( - containsPair('error', 'generic_error'), - containsPair('error_description', 'err_desc'), - containsPair('http_status_code', 400))); + resp.toMap(), + allOf( + containsPair('error', 'generic_error'), + containsPair('error_description', 'err_desc'), + containsPair('http_status_code', 400), + ), + ); }); test('Conversion from HTTP response', () async { @@ -60,7 +62,7 @@ void main() { final respMap = { 'error': 'generic_error', 'error_description': 'err_desc', - 'http_status_code': 400 + 'http_status_code': 400, }; final resp = OAuth2Response.fromMap(respMap); diff --git a/test/token_storage_test.dart b/test/token_storage_test.dart index 58eccc8..5be4715 100644 --- a/test/token_storage_test.dart +++ b/test/token_storage_test.dart @@ -16,20 +16,20 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1'], - } + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp = await storage.getToken(['scope2']); + final tknResp = await storage.getToken(['scope2']); expect(tknResp, null); }); @@ -38,21 +38,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp = await storage.getToken(['scope1']); + final tknResp = await storage.getToken(['scope1']); expect(tknResp?.isValid(), true); }); @@ -61,21 +61,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp = await storage.getToken(['scope1']); + final tknResp = await storage.getToken(['scope1']); expect(tknResp?.isValid(), true); }); @@ -83,21 +83,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp2 = await storage.getToken(['scope2']); + final tknResp2 = await storage.getToken(['scope2']); expect(tknResp2?.isValid(), true); }); @@ -105,21 +105,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp3 = await storage.getToken(['scope1', 'scope2']); + final tknResp3 = await storage.getToken(['scope1', 'scope2']); expect(tknResp3?.isValid(), true); }); @@ -127,21 +127,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp4 = await storage.getToken(['scope2', 'scope1']); + final tknResp4 = await storage.getToken(['scope2', 'scope1']); expect(tknResp4?.isValid(), true); }); @@ -149,21 +149,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp4 = await storage.getToken(['scope2', 'scope1', 'scope3']); + final tknResp4 = await storage.getToken(['scope2', 'scope1', 'scope3']); expect(tknResp4, null); }); @@ -171,21 +171,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp4 = await storage.getToken(['scope3']); + final tknResp4 = await storage.getToken(['scope3']); expect(tknResp4, null); }); @@ -193,24 +193,24 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': ['scope1', 'scope2'], - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp = await storage.getToken([]); + final tknResp = await storage.getToken([]); expect(tknResp, null); - var tknResp2 = await storage.getToken([]); + final tknResp2 = await storage.getToken([]); expect(tknResp2, null); }); @@ -218,21 +218,21 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', 'scope': null, - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp = await storage.getToken([]); + final tknResp = await storage.getToken([]); expect(tknResp?.isValid(), true); }); @@ -240,41 +240,41 @@ void main() { final secStorage = MockSecureStorage(); final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = { + final tokens = >{ 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'expires_in': 3600, 'refresh_token': '0987654321', - 'scope': [], - 'http_status_code': 200 - } + 'scope': [], + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) .thenAnswer((_) async => jsonEncode(tokens)); - var tknResp = await storage.getToken([]); + final tknResp = await storage.getToken([]); expect(tknResp?.isValid(), true); }); test('Insert token', () async { - var scope1Map = { + final scope1Map = { 'access_token': '1234567890', 'token_type': 'Bearer', 'refresh_token': '0987654321', 'scope': ['scope1'], 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; - var scope2Map = { + final scope2Map = { 'access_token': '1234567890', 'token_type': 'Bearer', 'refresh_token': '0987654321', 'scope': ['scope2'], 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final secStorage = MockSecureStorage(); @@ -297,13 +297,13 @@ void main() { }); test('Add token', () async { - var scope1Map = { + final scope1Map = { 'access_token': '1234567890', 'token_type': 'Bearer', 'refresh_token': '0987654321', 'scope': ['scope1'], 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final secStorage = MockSecureStorage(); @@ -316,12 +316,12 @@ void main() { }); test('Add token without no scope', () async { - var noScopesMap = { + final noScopesMap = { 'access_token': '1234567890', 'token_type': 'Bearer', 'refresh_token': '0987654321', 'expires_in': 3600, - 'http_status_code': 200 + 'http_status_code': 200, }; final secStorage = MockSecureStorage(); @@ -330,7 +330,7 @@ void main() { final storage = TokenStorage('my_token_url', storage: secStorage); - var tokens = + final tokens = await storage.insertToken(AccessTokenResponse.fromMap(noScopesMap)); expect(tokens, contains('_default_')); @@ -342,15 +342,15 @@ void main() { final scopes = ['scope1']; - var tknMap = { + final tknMap = { 'scope1': { 'access_token': '1234567890', 'token_type': 'Bearer', 'refresh_token': '0987654321', 'scope': scopes, 'expires_in': 3600, - 'http_status_code': 200 - } + 'http_status_code': 200, + }, }; when(secStorage.read('my_token_url')) @@ -361,8 +361,10 @@ void main() { await storage.deleteToken(scopes); - expect(verify(secStorage.write('my_token_url', captureAny)).captured, - ['{}']); + expect( + verify(secStorage.write('my_token_url', captureAny)).captured, + ['{}'], + ); clearInteractions(secStorage); });