Skip to content

Commit 7dc217c

Browse files
committed
CCM-11870: refactor authorizer client id claim check
1 parent 366617a commit 7dc217c

File tree

4 files changed

+68
-65
lines changed

4 files changed

+68
-65
lines changed

lambdas/authorizer/src/__tests__/index.test.ts

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -101,58 +101,6 @@ test('returns Allow policy on valid token with clientId', async () => {
101101
);
102102
});
103103

104-
test('returns Deny policy when authorisation succeeds but clientId is missing', async () => {
105-
lambdaCognitoAuthorizer.authorize.mockResolvedValue({
106-
success: true,
107-
subject: 'sub',
108-
clientId: undefined,
109-
});
110-
111-
const res = await handler(
112-
mock<APIGatewayRequestAuthorizerEvent>({
113-
requestContext,
114-
headers: { Authorization: 'jwt' },
115-
type: 'REQUEST',
116-
}),
117-
mock<Context>(),
118-
jest.fn()
119-
);
120-
121-
expect(res).toEqual({
122-
...denyPolicy,
123-
context: { user: 'sub' },
124-
});
125-
expect(mockLogger.warn).toHaveBeenCalledWith(
126-
'Authorisation denied: missing clientId'
127-
);
128-
});
129-
130-
test('returns Deny policy when authorisation succeeds but clientId is empty/whitespace', async () => {
131-
lambdaCognitoAuthorizer.authorize.mockResolvedValue({
132-
success: true,
133-
subject: 'sub',
134-
clientId: ' ',
135-
});
136-
137-
const res = await handler(
138-
mock<APIGatewayRequestAuthorizerEvent>({
139-
requestContext,
140-
headers: { Authorization: 'jwt' },
141-
type: 'REQUEST',
142-
}),
143-
mock<Context>(),
144-
jest.fn()
145-
);
146-
147-
expect(res).toEqual({
148-
...denyPolicy,
149-
context: { user: 'sub' },
150-
});
151-
expect(mockLogger.warn).toHaveBeenCalledWith(
152-
'Authorisation denied: missing clientId'
153-
);
154-
});
155-
156104
test('returns Deny policy on lambda misconfiguration', async () => {
157105
process.env.USER_POOL_ID = '';
158106

lambdas/authorizer/src/index.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,10 @@ export const handler: APIGatewayRequestAuthorizerHandler = async (event) => {
6565
);
6666

6767
if (authResult.success) {
68-
const clientId = authResult.clientId;
69-
70-
if (!clientId || clientId.trim() === '') {
71-
logger.warn('Authorisation denied: missing clientId');
72-
return generatePolicy(methodArn, 'Deny', { user: authResult.subject });
73-
} else {
74-
return generatePolicy(methodArn, 'Allow', {
75-
user: authResult.subject,
76-
clientId,
77-
});
78-
}
68+
return generatePolicy(methodArn, 'Allow', {
69+
user: authResult.subject,
70+
clientId: authResult.clientId,
71+
});
7972
}
8073

8174
return generatePolicy(methodArn, 'Deny');

utils/utils/src/__tests__/lambda-cognito-authorizer.test.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ describe('LambdaCognitoAuthorizer', () => {
175175
);
176176
});
177177

178-
test('returns failure on token with incorrect client_id claim', async () => {
178+
test('returns failure on token with incorrect cognito client_id claim', async () => {
179179
const jwt = sign(
180180
{
181181
token_use: 'access',
@@ -282,6 +282,68 @@ describe('LambdaCognitoAuthorizer', () => {
282282
);
283283
});
284284

285+
test('returns failure when NHS Notify client ID claim is empty string', async () => {
286+
const jwt = sign(
287+
{
288+
token_use: 'access',
289+
client_id: 'user-pool-client-id',
290+
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
291+
'nhs-notify:client-id': '',
292+
},
293+
'key',
294+
{
295+
keyid: 'key-id',
296+
}
297+
);
298+
299+
const res = await authorizer.authorize(userPoolId, userPoolClientId, jwt);
300+
301+
expect(res).toEqual({ success: false });
302+
expect(mockLogger.logMessages).toContainEqual(
303+
expect.objectContaining({
304+
level: 'error',
305+
message: expect.stringContaining('Failed to authorize'),
306+
issues: expect.arrayContaining([
307+
expect.objectContaining({
308+
path: ['nhs-notify:client-id'],
309+
message: 'Too small: expected string to have >=1 characters',
310+
}),
311+
]),
312+
})
313+
);
314+
});
315+
316+
test('returns failure when NHS Notify client ID claim is whitespace', async () => {
317+
const jwt = sign(
318+
{
319+
token_use: 'access',
320+
client_id: 'user-pool-client-id',
321+
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
322+
'nhs-notify:client-id': ' ',
323+
},
324+
'key',
325+
{
326+
keyid: 'key-id',
327+
}
328+
);
329+
330+
const res = await authorizer.authorize(userPoolId, userPoolClientId, jwt);
331+
332+
expect(res).toEqual({ success: false });
333+
expect(mockLogger.logMessages).toContainEqual(
334+
expect.objectContaining({
335+
level: 'error',
336+
message: expect.stringContaining('Failed to authorize'),
337+
issues: expect.arrayContaining([
338+
expect.objectContaining({
339+
path: ['nhs-notify:client-id'],
340+
message: 'Too small: expected string to have >=1 characters',
341+
}),
342+
]),
343+
})
344+
);
345+
});
346+
285347
test('returns failure on Cognito not validating the token', async () => {
286348
const cognitoErrorUserPool = 'user-pool-id-cognito-error';
287349

utils/utils/src/lambda-cognito-authorizer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const $AccessToken = z.object({
1212
client_id: z.string(),
1313
iss: z.string(),
1414
token_use: z.string(),
15-
'nhs-notify:client-id': z.string(),
15+
'nhs-notify:client-id': z.string().trim().nonempty(),
1616
});
1717

1818
export class LambdaCognitoAuthorizer {

0 commit comments

Comments
 (0)