From b8187536ab3633e6a390ea38b9e8434b23293380 Mon Sep 17 00:00:00 2001 From: James Jarvis Date: Mon, 23 Dec 2024 11:00:11 -0800 Subject: [PATCH] feat(auth): proactive token refresh (#14076) * update isTokenExpired to include tolerance * add unit tests * update bundle sizes * tmp enable e2e * revert tmp changes * tmp enable e2e * correct spec name * revert tmp changes * use mock Date.now * add additional test for expiration time exactly equal to tolerance * move isTokenExpired to utils * update bundle size --- .github/integ-config/integ-all.yml | 9 +++ packages/aws-amplify/package.json | 2 +- .../__tests__/utils/isTokenExpired.test.ts | 56 +++++++++++++++++++ packages/core/src/libraryUtils.ts | 2 +- packages/core/src/singleton/Auth/index.ts | 12 ---- packages/core/src/utils/index.ts | 1 + packages/core/src/utils/isTokenExpired.ts | 16 ++++++ 7 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 packages/core/__tests__/utils/isTokenExpired.test.ts create mode 100644 packages/core/src/utils/isTokenExpired.ts diff --git a/.github/integ-config/integ-all.yml b/.github/integ-config/integ-all.yml index 4fc85ee6ff4..c154aca382f 100644 --- a/.github/integ-config/integ-all.yml +++ b/.github/integ-config/integ-all.yml @@ -1011,3 +1011,12 @@ tests: browser: [chrome] env: NEXT_PUBLIC_BACKEND_CONFIG: pwl-webauthn + - test_name: integ_next_refresh_token_auth + desc: 'refresh token auth' + framework: next + category: auth + sample_name: [mfa] + spec: refresh-token-auth + browser: *minimal_browser_list + env: + NEXT_PUBLIC_BACKEND_CONFIG: misc-tokenref diff --git a/packages/aws-amplify/package.json b/packages/aws-amplify/package.json index 8c52bf30a1d..0ac8b34655e 100644 --- a/packages/aws-amplify/package.json +++ b/packages/aws-amplify/package.json @@ -383,7 +383,7 @@ "name": "[Auth] confirmSignIn (Cognito)", "path": "./dist/esm/auth/index.mjs", "import": "{ confirmSignIn }", - "limit": "28.61 kB" + "limit": "28.62 kB" }, { "name": "[Auth] updateMFAPreference (Cognito)", diff --git a/packages/core/__tests__/utils/isTokenExpired.test.ts b/packages/core/__tests__/utils/isTokenExpired.test.ts new file mode 100644 index 00000000000..04a61b297ae --- /dev/null +++ b/packages/core/__tests__/utils/isTokenExpired.test.ts @@ -0,0 +1,56 @@ +import { isTokenExpired } from '../../src/libraryUtils'; + +describe('isTokenExpired', () => { + const mockDate = Date.now(); + jest.spyOn(Date, 'now').mockImplementation(() => mockDate); + + it('should return true when token is expired', () => { + const result = isTokenExpired({ + expiresAt: Date.now() - 1, + clockDrift: 0, + tolerance: 0, + }); + + expect(result).toBe(true); + }); + + it('should return false when token is not expired', () => { + const result = isTokenExpired({ + expiresAt: Date.now() + 1, + clockDrift: 0, + tolerance: 0, + }); + + expect(result).toBe(false); + }); + + it('should return false when expiration time is within tolerance', () => { + const result = isTokenExpired({ + expiresAt: Date.now() + 5001, // more than 5 seconds remaining until expiration + clockDrift: 0, + tolerance: 5000, + }); + + expect(result).toBe(false); + }); + + it('should return true when expiration time is outside tolerance', () => { + const result = isTokenExpired({ + expiresAt: Date.now() + 4999, // less than 5 seconds remaining until expiration + clockDrift: 0, + tolerance: 5000, + }); + + expect(result).toBe(true); + }); + + it('should return false when expiration time is equal to tolerance', () => { + const result = isTokenExpired({ + expiresAt: Date.now() + 5000, // exactly 5 seconds remaining until expiration + clockDrift: 0, + tolerance: 5000, + }); + + expect(result).toBe(false); + }); +}); diff --git a/packages/core/src/libraryUtils.ts b/packages/core/src/libraryUtils.ts index 6c03928c5a4..7e073d153b8 100644 --- a/packages/core/src/libraryUtils.ts +++ b/packages/core/src/libraryUtils.ts @@ -11,6 +11,7 @@ export { generateRandomString, isBrowser, isNonRetryableError, + isTokenExpired, isWebWorker, jitteredBackoff, jitteredExponentialRetry, @@ -38,7 +39,6 @@ export { assertIdentityPoolIdConfig, assertOAuthConfig, } from './singleton/Auth/utils'; -export { isTokenExpired } from './singleton/Auth'; export { AssociationBelongsTo, AssociationHasMany, diff --git a/packages/core/src/singleton/Auth/index.ts b/packages/core/src/singleton/Auth/index.ts index 245cc704b0c..42d8cb3bddd 100644 --- a/packages/core/src/singleton/Auth/index.ts +++ b/packages/core/src/singleton/Auth/index.ts @@ -9,18 +9,6 @@ import { LibraryAuthOptions, } from './types'; -export function isTokenExpired({ - expiresAt, - clockDrift, -}: { - expiresAt: number; - clockDrift: number; -}): boolean { - const currentTime = Date.now(); - - return currentTime + clockDrift > expiresAt; -} - export class AuthClass { private authConfig?: AuthConfig; private authOptions?: LibraryAuthOptions; diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 9b713d2066e..96e981f4efe 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -16,3 +16,4 @@ export { urlSafeDecode } from './urlSafeDecode'; export { urlSafeEncode } from './urlSafeEncode'; export { deepFreeze } from './deepFreeze'; export { deDupeAsyncFunction } from './deDupeAsyncFunction'; +export { isTokenExpired } from './isTokenExpired'; diff --git a/packages/core/src/utils/isTokenExpired.ts b/packages/core/src/utils/isTokenExpired.ts new file mode 100644 index 00000000000..67b5b00f915 --- /dev/null +++ b/packages/core/src/utils/isTokenExpired.ts @@ -0,0 +1,16 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +export function isTokenExpired({ + expiresAt, + clockDrift, + tolerance = 5000, +}: { + expiresAt: number; + clockDrift: number; + tolerance?: number; +}): boolean { + const currentTime = Date.now(); + + return currentTime + clockDrift + tolerance > expiresAt; +}