-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(adapter-nextjs): validate if access and id tokens are valid cog…
…nito tokens
- Loading branch information
Ashwin Kumar
committed
May 22, 2024
1 parent
82d53fa
commit ac44fb2
Showing
14 changed files
with
314 additions
and
32 deletions.
There are no files selected for viewing
85 changes: 85 additions & 0 deletions
85
packages/adapter-nextjs/__tests__/utils/createTokenValidator.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { isValidCognitoToken } from '../../src/utils/isValidCognitoToken'; | ||
import { createTokenValidator } from '../../src/utils/createTokenValidator'; | ||
|
||
jest.mock('../../src/utils/isValidCognitoToken'); | ||
|
||
const mockIsValidCognitoToken = isValidCognitoToken as jest.Mock; | ||
|
||
const userPoolId = 'userPoolId'; | ||
const userPoolClientId = 'clientId'; | ||
const tokenValidatorInput = { | ||
userPoolId, | ||
userPoolClientId, | ||
}; | ||
const accessToken = { | ||
key: 'CognitoIdentityServiceProvider.clientId.usersub.accessToken', | ||
value: | ||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMTEiLCJpc3MiOiJodHRwc', | ||
}; | ||
const idToken = { | ||
key: 'CognitoIdentityServiceProvider.clientId.usersub.idToken', | ||
value: 'eyJzdWIiOiIxMTEiLCJpc3MiOiJodHRwc.XAiOiJKV1QiLCJhbGciOiJIUzI1NiJ', | ||
}; | ||
|
||
const tokenValidator = createTokenValidator({ | ||
userPoolId, | ||
userPoolClientId, | ||
}); | ||
|
||
describe('Validator', () => { | ||
afterEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
it('should return a validator', () => { | ||
expect(createTokenValidator(tokenValidatorInput)).toBeDefined(); | ||
}); | ||
|
||
it('should return true for non-token keys', async () => { | ||
const result = await tokenValidator.getItem?.('mockKey', 'mockValue'); | ||
expect(result).toBe(true); | ||
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('should return true for valid accessToken', async () => { | ||
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(true)); | ||
|
||
const result = await tokenValidator.getItem?.( | ||
accessToken.key, | ||
accessToken.value, | ||
); | ||
|
||
expect(result).toBe(true); | ||
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1); | ||
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({ | ||
userPoolId, | ||
clientId: userPoolClientId, | ||
token: accessToken.value, | ||
tokenType: 'access', | ||
}); | ||
}); | ||
|
||
it('should return true for valid idToken', async () => { | ||
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(true)); | ||
|
||
const result = await tokenValidator.getItem?.(idToken.key, idToken.value); | ||
expect(result).toBe(true); | ||
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1); | ||
expect(mockIsValidCognitoToken).toHaveBeenCalledWith({ | ||
userPoolId, | ||
clientId: userPoolClientId, | ||
token: idToken.value, | ||
tokenType: 'id', | ||
}); | ||
}); | ||
|
||
it('should return false if invalid tokenType is access', async () => { | ||
mockIsValidCognitoToken.mockImplementation(() => Promise.resolve(false)); | ||
|
||
const result = await tokenValidator.getItem?.(idToken.key, idToken.value); | ||
expect(result).toBe(false); | ||
expect(mockIsValidCognitoToken).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
68 changes: 68 additions & 0 deletions
68
packages/adapter-nextjs/__tests__/utils/isValidCognitoToken.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { CognitoJwtVerifier } from 'aws-jwt-verify'; | ||
|
||
import { isValidCognitoToken } from '../../src/utils/isValidCognitoToken'; | ||
|
||
jest.mock('aws-jwt-verify', () => { | ||
return { | ||
CognitoJwtVerifier: { | ||
create: jest.fn(), | ||
}, | ||
}; | ||
}); | ||
|
||
const mockedCreate = CognitoJwtVerifier.create as jest.MockedFunction< | ||
typeof CognitoJwtVerifier.create | ||
>; | ||
|
||
describe('isValidCognitoToken', () => { | ||
const token = 'mocked-token'; | ||
const userPoolId = 'us-east-1_test'; | ||
const clientId = 'client-id-test'; | ||
const tokenType = 'id'; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should return true for a valid token', async () => { | ||
const mockVerifier: any = { | ||
verify: jest.fn().mockResolvedValue({}), | ||
}; | ||
mockedCreate.mockReturnValue(mockVerifier); | ||
|
||
const isValid = await isValidCognitoToken({ | ||
token, | ||
userPoolId, | ||
clientId, | ||
tokenType, | ||
}); | ||
expect(isValid).toBe(true); | ||
expect(CognitoJwtVerifier.create).toHaveBeenCalledWith({ | ||
userPoolId, | ||
clientId, | ||
tokenUse: tokenType, | ||
}); | ||
expect(mockVerifier.verify).toHaveBeenCalledWith(token); | ||
}); | ||
|
||
it('should return false for an invalid token', async () => { | ||
const mockVerifier: any = { | ||
verify: jest.fn().mockRejectedValue(new Error('Invalid token')), | ||
}; | ||
mockedCreate.mockReturnValue(mockVerifier); | ||
|
||
const isValid = await isValidCognitoToken({ | ||
token, | ||
userPoolId, | ||
clientId, | ||
tokenType, | ||
}); | ||
expect(isValid).toBe(false); | ||
expect(CognitoJwtVerifier.create).toHaveBeenCalledWith({ | ||
userPoolId, | ||
clientId, | ||
tokenUse: tokenType, | ||
}); | ||
expect(mockVerifier.verify).toHaveBeenCalledWith(token); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { KeyValueStorageMethodValidator } from '@aws-amplify/core/internals/adapter-core'; | ||
|
||
import { isValidCognitoToken } from './isValidCognitoToken'; | ||
|
||
interface CreateTokenValidatorInput { | ||
userPoolId?: string; | ||
userPoolClientId?: string; | ||
} | ||
/** | ||
* Creates a validator object for validating methods in a KeyValueStorage. | ||
*/ | ||
export const createTokenValidator = ({ | ||
userPoolId, | ||
userPoolClientId: clientId, | ||
}: CreateTokenValidatorInput): KeyValueStorageMethodValidator => { | ||
return { | ||
// validate access, id tokens | ||
getItem: async (key: string, value: string): Promise<boolean> => { | ||
const tokenType = key.includes('.accessToken') | ||
? 'access' | ||
: key.includes('.idToken') | ||
? 'id' | ||
: null; | ||
if (!tokenType) return true; | ||
|
||
if (!userPoolId || !clientId) return false; | ||
|
||
return isValidCognitoToken({ | ||
clientId, | ||
userPoolId, | ||
tokenType, | ||
token: value, | ||
}); | ||
}, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { CognitoJwtVerifier } from 'aws-jwt-verify'; | ||
|
||
/** | ||
* Verifies a Cognito JWT token for its validity. | ||
* | ||
* @param input - An object containing: | ||
* - token: The JWT token as a string that needs to be verified. | ||
* - userPoolId: The ID of the AWS Cognito User Pool to which the token belongs. | ||
* - clientId: The Client ID associated with the Cognito User Pool. | ||
* @internal | ||
*/ | ||
export const isValidCognitoToken = async (input: { | ||
token: string; | ||
userPoolId: string; | ||
clientId: string; | ||
tokenType: 'id' | 'access'; | ||
}): Promise<boolean> => { | ||
const { userPoolId, clientId, tokenType, token } = input; | ||
|
||
try { | ||
const verifier = CognitoJwtVerifier.create({ | ||
userPoolId, | ||
tokenUse: tokenType, | ||
clientId, | ||
}); | ||
await verifier.verify(token); | ||
|
||
return true; | ||
} catch (error) { | ||
// TODO (ashwinkumar6): surface invalid cognito token error to customer | ||
// TODO: clear invalid tokens from Storage | ||
return false; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
packages/core/src/adapterCore/serverContext/types/KeyValueStorageMethodValidator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { KeyValueStorageInterface } from '../../../types/storage'; | ||
|
||
export type KeyValueStorageMethodValidator = Partial< | ||
Record<keyof KeyValueStorageInterface, ValidatorFunction> | ||
>; | ||
|
||
type ValidatorFunction = (...args: any[]) => Promise<boolean>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.