From 7137cb940e209f943604a1a19264c65608bd9713 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Wed, 20 Mar 2024 04:15:55 +0200 Subject: [PATCH 1/2] - fix: Attempt to refresh expired tokens instead of throwing them away. - feat: Use clock package instead of directly calling DateTime.now() - docs: Fix formatting of the Initialization section --- docs/oidc-usage.md | 8 +- .../oidc/example/lib/pages/secret_page.dart | 4 +- .../oidc/lib/src/managers/user_manager.dart | 81 +++++-- packages/oidc/lib/src/models/event.dart | 3 +- packages/oidc/pubspec.yaml | 3 +- packages/oidc/test/mock_client.dart | 215 +++++++++++------- packages/oidc/test/oidc_test.dart | 175 ++++++++++++-- packages/oidc_core/lib/src/utils.dart | 7 + packages/oidc_core/pubspec.yaml | 2 +- 9 files changed, 364 insertions(+), 134 deletions(-) diff --git a/docs/oidc-usage.md b/docs/oidc-usage.md index cc375b7..9e2a09e 100644 --- a/docs/oidc-usage.md +++ b/docs/oidc-usage.md @@ -183,13 +183,15 @@ this is used for validating the JWT signature. ### Initialization After constructing the manager with the proper settings, you MUST call the `manager.init()` function, which will do the following: -1. If the function has already been initialized (checked via the hasInit variable), it simply returns. This is because certain configurations and setups do not need, and should not be, repeated. + +1. If the function has already been initialized (checked via the hasInit variable), it simply returns. This is because certain configurations and setups do not need to be repeated. 2. initialize the passed store (calls `OidcStore.init()`) 3. ensure that the discovery document has been retrieved (if the lazy constructor was used). 4. if the discovery document contains a `jwks_uri` adds it the to the keystore. 5. handle various state management tasks. It loads logout requests and state results (from samePage redirects), also attempts to load cached tokens if there are no state results or logout requests. -6. Starts Listening to incoming Front Channel Logout requests. -7. Starts listening to token expiry events for automatic refresh_token circulation. +6. If the loaded token has expired and a refresh token exists, it attempts to refresh it, otherwise the token will be removed form the store. +7. Starts Listening to incoming Front Channel Logout requests. +8. Starts listening to token expiry events for automatic `refresh_token` circulation. ### Login diff --git a/packages/oidc/example/lib/pages/secret_page.dart b/packages/oidc/example/lib/pages/secret_page.dart index 0552cd0..d2927c1 100644 --- a/packages/oidc/example/lib/pages/secret_page.dart +++ b/packages/oidc/example/lib/pages/secret_page.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -199,7 +201,7 @@ class _SecretPageState extends State { const Divider(), Text('id token: ${user.idToken}'), const Divider(), - Text('token: ${user.token.toJson()}'), + Text('token: ${jsonEncode(user.token.toJson())}'), ], ), ), diff --git a/packages/oidc/lib/src/managers/user_manager.dart b/packages/oidc/lib/src/managers/user_manager.dart index a58225e..316b366 100644 --- a/packages/oidc/lib/src/managers/user_manager.dart +++ b/packages/oidc/lib/src/managers/user_manager.dart @@ -534,6 +534,7 @@ class OidcUserManager { required String? nonce, required Map? attributes, required OidcProviderMetadata metadata, + bool validateAndSave = true, }) async { final currentUser = this.currentUser; OidcUser newUser; @@ -559,7 +560,11 @@ class OidcUserManager { 'Server returned a wrong id_token nonce, might be a replay attack.', ); } - return _validateAndSaveUser(user: newUser, metadata: metadata); + if (validateAndSave) { + return _validateAndSaveUser(user: newUser, metadata: metadata); + } else { + return newUser; + } } Future _saveUser(OidcUser user) async { @@ -665,14 +670,15 @@ class OidcUserManager { ), ); return _createUserFromToken( - token: OidcToken.fromResponse( - tokenResponse, - overrideExpiresIn: settings.getExpiresIn?.call(tokenResponse), - sessionState: currentUser?.token.sessionState, - ), - nonce: null, - attributes: null, - metadata: discoveryDocument); + token: OidcToken.fromResponse( + tokenResponse, + overrideExpiresIn: settings.getExpiresIn?.call(tokenResponse), + sessionState: currentUser?.token.sessionState, + ), + nonce: null, + attributes: null, + metadata: discoveryDocument, + ); } Future _listenToTokenRefreshIfSupported( @@ -741,29 +747,38 @@ class OidcUserManager { forgetUser(); } - /// This function validates that a user claims - Future _validateAndSaveUser({ + List _validateUser({ required OidcUser user, required OidcProviderMetadata metadata, - }) async { - var actualUser = user; + }) { final errors = [ - ...actualUser.parsedIdToken.claims.validate( + ...user.parsedIdToken.claims.validate( clientId: clientCredentials.clientId, issuer: metadata.issuer, expiryTolerance: settings.expiryTolerance, ), ]; - if (actualUser.parsedIdToken.claims.subject == null) { + if (user.parsedIdToken.claims.subject == null) { errors.add( JoseException('id token is missing a `sub` claim.'), ); } - if (actualUser.parsedIdToken.claims.issuedAt == null) { + if (user.parsedIdToken.claims.issuedAt == null) { errors.add( JoseException('id token is missing an `iat` claim.'), ); } + + return errors; + } + + /// This function validates that a user claims + Future _validateAndSaveUser({ + required OidcUser user, + required OidcProviderMetadata metadata, + }) async { + var actualUser = user; + final errors = _validateUser(user: actualUser, metadata: metadata); OidcUserInfoResponse? userInfoResp; if (errors.isEmpty) { @@ -804,7 +819,7 @@ class OidcUserManager { } else { for (final element in errors) { _logger.warning( - 'found a JWT, but failed the validation test: $element', + 'Found a JWT, but failed the validation test: $element', element, StackTrace.current, ); @@ -931,19 +946,47 @@ class OidcUserManager { if (rawToken == null) { return; } + try { final decodedAttributes = rawAttributes == null ? null : jsonDecode(rawAttributes) as Map; final decodedToken = jsonDecode(rawToken) as Map; final token = OidcToken.fromJson(decodedToken); - await _createUserFromToken( + final metadata = discoveryDocument; + var loadedUser = await _createUserFromToken( token: token, // nonce is only checked for new tokens. nonce: null, attributes: decodedAttributes, - metadata: discoveryDocument, + metadata: metadata, + validateAndSave: false, ); + if (loadedUser != null) { + final validationErrors = _validateUser( + user: loadedUser, + metadata: metadata, + ); + final idTokenNeedsRefresh = validationErrors + .whereType() + .any((element) => element.message.startsWith('JWT expired')); + if (token.refreshToken != null && + (idTokenNeedsRefresh || token.isAccessTokenExpired())) { + loadedUser = + await refreshToken(overrideRefreshToken: token.refreshToken); + } + if (loadedUser != null) { + loadedUser = await _validateAndSaveUser( + user: loadedUser, + metadata: metadata, + ); + } + } + + if (loadedUser == null) { + _logAndThrow( + 'Found a cached token, but the user could not be created or validated'); + } } catch (e) { // remove invalid tokens, so that they don't get used again. await store.removeMany( diff --git a/packages/oidc/lib/src/models/event.dart b/packages/oidc/lib/src/models/event.dart index 32aa8f4..87733df 100644 --- a/packages/oidc/lib/src/models/event.dart +++ b/packages/oidc/lib/src/models/event.dart @@ -1,3 +1,4 @@ +import 'package:clock/clock.dart'; import 'package:oidc_core/oidc_core.dart'; /// Represents an arbitrary event. @@ -8,7 +9,7 @@ abstract class OidcEvent { }); /// Creates an event whose [at] is now. - OidcEvent.now() : at = DateTime.now(); + OidcEvent.now() : at = clock.now(); /// when the event occurred. final DateTime at; diff --git a/packages/oidc/pubspec.yaml b/packages/oidc/pubspec.yaml index 0f9aed5..602d37a 100644 --- a/packages/oidc/pubspec.yaml +++ b/packages/oidc/pubspec.yaml @@ -37,13 +37,14 @@ dependencies: oidc_windows: ^0.3.1+1 # ==================== http: ^1.1.0 - jose_plus: ^0.4.3 + jose_plus: ^0.4.4 retry: ^3.1.2 rxdart: ^0.27.7 logging: ^1.2.0 json_annotation: ^4.8.1 nonce: ^1.2.0 uuid: ^4.0.0 + clock: ^1.1.1 dev_dependencies: build_runner: ^2.4.6 diff --git a/packages/oidc/test/mock_client.dart b/packages/oidc/test/mock_client.dart index 4258506..d130823 100644 --- a/packages/oidc/test/mock_client.dart +++ b/packages/oidc/test/mock_client.dart @@ -1,103 +1,150 @@ +// ignore_for_file: prefer_single_quotes + +import 'dart:convert'; + +import 'package:clock/clock.dart'; import 'package:collection/collection.dart'; import 'package:http/src/request.dart'; import 'package:http/src/response.dart'; import 'package:http/testing.dart'; +import 'package:oidc/oidc.dart'; +// import 'package:oidc_core/oidc_core.dart'; + +Map createMockTokenResponse({ + required Map claimsJson, + Duration expiresIn = const Duration(hours: 1), +}) { + return { + "access_token": clock.now().microsecondsSinceEpoch.toString(), + "token_type": "Bearer", + "refresh_token": "8xLOxBtZp8", + "expires_in": expiresIn.inSeconds, + "id_token": createIdToken(claimsJson: claimsJson), + }; +} + +Map defaultIdTokenClaimsJson({DateTime? iat, DateTime? exp}) => + { + "iss": "http://server.example.com", + //id token expires after 1 hour + "iat": (iat ?? clock.now()).secondsSinceEpoch, + "exp": + (exp ?? clock.now().add(const Duration(hours: 1))).secondsSinceEpoch, + "aud": "my_client_id", + "sub": "248289761001", + "nonce": "n-0S6_WzA2Mj" + }; +String createIdToken({ + required Map claimsJson, +}) { + final claims = JsonWebTokenClaims.fromJson(claimsJson); + + final builder = JsonWebSignatureBuilder() + ..jsonContent = claims.toJson() + ..addRecipient( + JsonWebKey.fromJson({ + "kty": "oct", + "k": + "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" + }), + algorithm: "HS256", + ); + final res = builder.build(); + return res.toCompactSerialization(); +} -MockClient createMockOidcClient() { - return MockClient(_handleRequest); +MockClient createMockOidcClient({ + MockClientHandler? beforeDefault, + MockClientHandler? afterDefault, +}) { + assert( + (beforeDefault == null && afterDefault == null) || + (beforeDefault != null) ^ (afterDefault != null), + 'Either supply beforeDefault or afterDefault', + ); + return MockClient( + beforeDefault ?? + (request) => _handleRequest(request, afterDefault: afterDefault), + ); } const _eq = DeepCollectionEquality(); -Future _handleRequest(Request request) async { +Future _handleRequest( + Request request, { + MockClientHandler? afterDefault, +}) async { final url = request.url; if (_eq.equals(url.pathSegments, ['.well-known', 'openid-configuration'])) { - return Response(_googleProviderMetadata, 304); + return Response(jsonEncode(mockProviderMetadata), 304); } if (url.pathSegments.first == 'token') { - return Response(_tokenResponse, 200); + final tokenResp = createMockTokenResponse( + claimsJson: defaultIdTokenClaimsJson(), + ); + return Response(jsonEncode(tokenResp), 200); + } + if (afterDefault != null) { + return afterDefault(request); } throw UnimplementedError( "Don't know how to handle the request ${request.url}", ); } -// cSpell: disable -const _tokenResponse = ''' -{ - "access_token": "SlAV32hkKG", - "token_type": "Bearer", - "refresh_token": "8xLOxBtZp8", - "expires_in": 3600, - "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc - yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5 - NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ - fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz - AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q - Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ - NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd - QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS - K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4 - XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg" -} -'''; - -const _googleProviderMetadata = ''' -{ - "issuer": "https://accounts.google.com", - "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", - "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code", - "token_endpoint": "https://oauth2.googleapis.com/token", - "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo", - "revocation_endpoint": "https://oauth2.googleapis.com/revoke", - "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", - "response_types_supported": [ - "code", - "token", - "id_token", - "code token", - "code id_token", - "token id_token", - "code token id_token", - "none" - ], - "subject_types_supported": [ - "public" - ], - "id_token_signing_alg_values_supported": [ - "RS256" - ], - "scopes_supported": [ - "openid", - "email", - "profile" - ], - "token_endpoint_auth_methods_supported": [ - "client_secret_post", - "client_secret_basic" - ], - "claims_supported": [ - "aud", - "email", - "email_verified", - "exp", - "family_name", - "given_name", - "iat", - "iss", - "locale", - "name", - "picture", - "sub" - ], - "code_challenge_methods_supported": [ - "plain", - "S256" - ], - "grant_types_supported": [ - "authorization_code", - "refresh_token", - "urn:ietf:params:oauth:grant-type:device_code", - "urn:ietf:params:oauth:grant-type:jwt-bearer" - ] -}'''; +const mockProviderMetadata = { + 'issuer': 'http://server.example.com', + 'authorization_endpoint': 'http://server.example.com/authorize', + 'token_endpoint': 'http://server.example.com/token', + 'grant_types_supported': [ + OidcConstants_GrantType.authorizationCode, + OidcConstants_GrantType.refreshToken, + ] +}; +const googleProviderMetadata = { + "issuer": "https://accounts.google.com", + "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", + "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code", + "token_endpoint": "https://oauth2.googleapis.com/token", + "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo", + "revocation_endpoint": "https://oauth2.googleapis.com/revoke", + "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", + "response_types_supported": [ + "code", + "token", + "id_token", + "code token", + "code id_token", + "token id_token", + "code token id_token", + "none" + ], + "subject_types_supported": ["public"], + "id_token_signing_alg_values_supported": ["RS256"], + "scopes_supported": ["openid", "email", "profile"], + "token_endpoint_auth_methods_supported": [ + "client_secret_post", + "client_secret_basic" + ], + "claims_supported": [ + "aud", + "email", + "email_verified", + "exp", + "family_name", + "given_name", + "iat", + "iss", + "locale", + "name", + "picture", + "sub" + ], + "code_challenge_methods_supported": ["plain", "S256"], + "grant_types_supported": [ + "authorization_code", + "refresh_token", + "urn:ietf:params:oauth:grant-type:device_code", + "urn:ietf:params:oauth:grant-type:jwt-bearer" + ] +}; diff --git a/packages/oidc/test/oidc_test.dart b/packages/oidc/test/oidc_test.dart index 8176e16..6b4d323 100644 --- a/packages/oidc/test/oidc_test.dart +++ b/packages/oidc/test/oidc_test.dart @@ -1,4 +1,11 @@ +// ignore_for_file: prefer_single_quotes + +import 'dart:convert'; + +import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart'; +import 'package:http/testing.dart'; import 'package:mocktail/mocktail.dart'; import 'package:oidc/oidc.dart'; // import 'package:oidc/oidc.dart'; @@ -13,30 +20,34 @@ class MockOidcPlatform extends Mock void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('Oidc', () { - late OidcPlatform oidcPlatform; - final client = createMockOidcClient(); - final store = OidcMemoryStore(); - const clientCredentials = OidcClientAuthentication.none( - clientId: 'my_client_id', - ); - final settings = OidcUserManagerSettings( - redirectUri: Uri.parse('http://example.com/redirect.html'), - ); - setUp(() { - oidcPlatform = MockOidcPlatform(); - OidcPlatform.instance = oidcPlatform; - }); + late OidcPlatform oidcPlatform; + + const clientCredentials = OidcClientAuthentication.none( + clientId: 'my_client_id', + ); + final settings = OidcUserManagerSettings( + redirectUri: Uri.parse('http://example.com/redirect.html'), + ); + + setUp(() { + oidcPlatform = MockOidcPlatform(); + OidcPlatform.instance = oidcPlatform; + }); - group('OidcUserManager', () { + group('OidcUserManager', () { + group('init', () { + late OidcProviderMetadata doc; + late OidcStore store; + late Client client; + late OidcUserManager manager; + setUp(() { + doc = OidcProviderMetadata.fromJson(mockProviderMetadata); + store = OidcMemoryStore(); + client = createMockOidcClient(); + }); test('with document', () async { - final doc = OidcProviderMetadata.fromJson({ - 'issuer': 'https://server.example.com', - 'authorization_endpoint': - 'https://server.example.com/connect/authorize', - 'token_endpoint': 'https://server.example.com/connect/token', - }); - final manager = OidcUserManager( + client = createMockOidcClient(); + manager = OidcUserManager( discoveryDocument: doc, clientCredentials: clientCredentials, store: store, @@ -50,7 +61,7 @@ void main() { expect(manager.discoveryDocumentUri, isNull); }); test('lazy', () async { - final manager = OidcUserManager.lazy( + manager = OidcUserManager.lazy( discoveryDocumentUri: OidcUtils.getOpenIdConfigWellKnownUri( Uri.parse('https://example.com'), ), @@ -62,9 +73,125 @@ void main() { expect(manager.didInit, isFalse); await manager.init(); expect(manager.didInit, isTrue); - expect(manager.discoveryDocument, isNotNull); + expect(manager.discoveryDocument.src, mockProviderMetadata); expect(manager.discoveryDocumentUri, isNotNull); }); + group('loadCachedToken', () { + final tokenCreatedAt = DateTime.utc(2024, 03, 01); + final tokenCreatedAtClock = Clock.fixed(tokenCreatedAt); + late Clock nowClock; + // The idToken is created + late Map cachedTokenJson; + setUp(() async { + cachedTokenJson = withClock(tokenCreatedAtClock, () { + return { + "scope": OidcInternalUtilities.joinSpaceDelimitedList([ + OidcConstants_Scopes.openid, + OidcConstants_Scopes.profile, + OidcConstants_Scopes.email, + "offline_access" + ]), + "access_token": "SlAV32hkKG", + "token_type": "Bearer", + "refresh_token": "8xLOxBtZp8", + "expires_in": const Duration(hours: 1).inSeconds, + "id_token": createIdToken(claimsJson: defaultIdTokenClaimsJson()), + "expiresInReferenceDate": clock.now().toIso8601String(), + "session_state": + "YaSjXERcv7iG5F9euVQ_F4smjyt0jD3sYxARlJdBMVE.9A5536CDE44A8BE6D4F2A9E2ABD73ECF" + }; + }); + await store.set( + OidcStoreNamespace.secureTokens, + key: OidcConstants_Store.currentToken, + value: jsonEncode(cachedTokenJson), + ); + manager = OidcUserManager( + discoveryDocument: doc, + clientCredentials: clientCredentials, + store: store, + settings: settings, + httpClient: client, + ); + }); + + test('with non-expired token should load the user normally', () async { + final nowMock = tokenCreatedAt.add(const Duration(minutes: 1)); + nowClock = Clock.fixed(nowMock); + + await withClock(nowClock, () async { + await manager.init(); + }); + expect(manager.didInit, isTrue); + expect(manager.currentUser, isNotNull); + + final newToken = manager.currentUser?.token; + expect(newToken, isNotNull); + //no refresh is needed, so old token remains + expect(newToken!.creationTime, tokenCreatedAt); + final storedToken = await store.get( + OidcStoreNamespace.secureTokens, + key: OidcConstants_Store.currentToken, + ); + //since no refresh happened AND token is valid, it should remain in the store. + expect(storedToken, isNotNull); + expect(jsonDecode(storedToken!), cachedTokenJson); + }); + test('With expired token should refresh the token', () async { + // + final nowMock = tokenCreatedAt.add(const Duration(hours: 2)); + nowClock = Clock.fixed(nowMock); + + await withClock(nowClock, () async { + await manager.init(); + }); + expect(manager.didInit, isTrue); + expect(manager.currentUser, isNotNull); + + final newToken = manager.currentUser?.token; + expect(newToken, isNotNull); + // A refresh is needed, so creation time should reflect that. + expect(newToken!.creationTime, nowMock); + final storedToken = await store.get( + OidcStoreNamespace.secureTokens, + key: OidcConstants_Store.currentToken, + ); + // a refresh happened, but new token should remain in the store. + expect(storedToken, isNotNull); + final decodedStoredToken = + jsonDecode(storedToken!) as Map; + + expect( + decodedStoredToken[OidcConstants_Store.expiresInReferenceDate], + nowMock.toIso8601String(), + ); + }); + test( + 'With expired token and no refresh token available, should remove the token', + () async { + cachedTokenJson.remove('refresh_token'); + await store.set( + OidcStoreNamespace.secureTokens, + key: OidcConstants_Store.currentToken, + value: jsonEncode(cachedTokenJson), + ); + // + final nowMock = tokenCreatedAt.add(const Duration(hours: 2)); + nowClock = Clock.fixed(nowMock); + + await withClock(nowClock, () async { + await manager.init(); + }); + expect(manager.didInit, isTrue); + expect(manager.currentUser, isNull); + + final storedToken = await store.get( + OidcStoreNamespace.secureTokens, + key: OidcConstants_Store.currentToken, + ); + expect(storedToken, isNull); + }); + }); }); }); } diff --git a/packages/oidc_core/lib/src/utils.dart b/packages/oidc_core/lib/src/utils.dart index be7a2c4..1dd6cfd 100644 --- a/packages/oidc_core/lib/src/utils.dart +++ b/packages/oidc_core/lib/src/utils.dart @@ -141,6 +141,13 @@ class OidcInternalUtilities { } } +extension OidcDateTime on DateTime { + int get secondsSinceEpoch => millisecondsSinceEpoch ~/ 1000; + static DateTime fromSecondsSinceEpoch(int seconds) { + return DateTime.fromMillisecondsSinceEpoch(seconds * 1000); + } +} + /// Utilities for the Oidc spec class OidcUtils { /// Takes a base Url and adds /.well-known/openid-configuration to it diff --git a/packages/oidc_core/pubspec.yaml b/packages/oidc_core/pubspec.yaml index 3f81b7e..b65f38f 100644 --- a/packages/oidc_core/pubspec.yaml +++ b/packages/oidc_core/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: crypto: ^3.0.3 - jose_plus: ^0.4.3 + jose_plus: ^0.4.4 http: ^1.1.0 json_annotation: ^4.8.1 uuid: "^4.0.0" From b8d442e25076bdeb1d73e3dfdb49f822919f93f1 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Wed, 20 Mar 2024 04:24:26 +0200 Subject: [PATCH 2/2] formatting --- packages/oidc/example/lib/access_token_usage_example.dart | 4 +++- packages/oidc/test/oidc_test.dart | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/oidc/example/lib/access_token_usage_example.dart b/packages/oidc/example/lib/access_token_usage_example.dart index d7219fe..cb02ed6 100644 --- a/packages/oidc/example/lib/access_token_usage_example.dart +++ b/packages/oidc/example/lib/access_token_usage_example.dart @@ -4,7 +4,9 @@ import 'package:oidc/oidc.dart'; const kAuthorizationHeader = 'Authorization'; void _tryAppendAccessToken( - OidcUserManager userManager, Map headers) { + OidcUserManager userManager, + Map headers, +) { if (headers.containsKey(kAuthorizationHeader)) { // do nothing if header already exists. return; diff --git a/packages/oidc/test/oidc_test.dart b/packages/oidc/test/oidc_test.dart index 6b4d323..7859190 100644 --- a/packages/oidc/test/oidc_test.dart +++ b/packages/oidc/test/oidc_test.dart @@ -5,10 +5,8 @@ import 'dart:convert'; import 'package:clock/clock.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart'; -import 'package:http/testing.dart'; import 'package:mocktail/mocktail.dart'; import 'package:oidc/oidc.dart'; -// import 'package:oidc/oidc.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'mock_client.dart'; @@ -77,7 +75,7 @@ void main() { expect(manager.discoveryDocumentUri, isNotNull); }); group('loadCachedToken', () { - final tokenCreatedAt = DateTime.utc(2024, 03, 01); + final tokenCreatedAt = DateTime.utc(2024, 03); final tokenCreatedAtClock = Clock.fixed(tokenCreatedAt); late Clock nowClock; // The idToken is created