Skip to content

Commit dcc1870

Browse files
authored
CCM-10422: download authorizer works with client ownership (#626)
1 parent c1ad34f commit dcc1870

File tree

2 files changed

+64
-18
lines changed

2 files changed

+64
-18
lines changed

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

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,31 @@ describe('LambdaCognitoAuthorizer', () => {
107107
expect(mockLogger.logMessages).toEqual([]);
108108
});
109109

110-
test('returns success on valid token without notify-client-id', async () => {
110+
test('returns success on valid token when expected resource owner is specified', async () => {
111111
const jwt = sign(
112112
{
113113
token_use: 'access',
114114
client_id: 'user-pool-client-id',
115115
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
116+
'nhs-notify:client-id': 'nhs-notify-client-id',
116117
},
117118
'key',
118119
{
119120
keyid: 'key-id',
120121
}
121122
);
122123

123-
const res = await authorizer.authorize(userPoolId, userPoolClientId, jwt);
124+
const res = await authorizer.authorize(
125+
userPoolId,
126+
userPoolClientId,
127+
jwt,
128+
'nhs-notify-client-id'
129+
);
124130

125131
expect(res).toEqual({
126132
success: true,
127133
subject: 'sub',
134+
clientId: 'nhs-notify-client-id',
128135
});
129136
expect(mockLogger.logMessages).toEqual([]);
130137
});
@@ -152,7 +159,7 @@ describe('LambdaCognitoAuthorizer', () => {
152159
token_use: 'access',
153160
client_id: 'user-pool-client-id',
154161
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
155-
clientId: 'nhs-notify-client-id',
162+
'nhs-notify:client-id': 'nhs-notify-client-id',
156163
},
157164
'key'
158165
);
@@ -174,7 +181,7 @@ describe('LambdaCognitoAuthorizer', () => {
174181
token_use: 'access',
175182
client_id: 'user-pool-client-id-2',
176183
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
177-
clientId: 'nhs-notify-client-id',
184+
'nhs-notify:client-id': 'nhs-notify-client-id',
178185
},
179186
'key',
180187
{
@@ -200,7 +207,7 @@ describe('LambdaCognitoAuthorizer', () => {
200207
token_use: 'access',
201208
client_id: 'user-pool-client-id',
202209
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-2',
203-
clientId: 'nhs-notify-client-id',
210+
'nhs-notify:client-id': 'nhs-notify-client-id',
204211
},
205212
'key',
206213
{
@@ -226,7 +233,7 @@ describe('LambdaCognitoAuthorizer', () => {
226233
token_use: 'id',
227234
client_id: 'user-pool-client-id',
228235
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
229-
clientId: 'nhs-notify-client-id',
236+
'nhs-notify:client-id': 'nhs-notify-client-id',
230237
},
231238
'key',
232239
{
@@ -245,6 +252,36 @@ describe('LambdaCognitoAuthorizer', () => {
245252
);
246253
});
247254

255+
test('returns failure when no NHS Notify client ID is present in the access token', async () => {
256+
const jwt = sign(
257+
{
258+
token_use: 'access',
259+
client_id: 'user-pool-client-id',
260+
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
261+
},
262+
'key',
263+
{
264+
keyid: 'key-id',
265+
}
266+
);
267+
268+
const res = await authorizer.authorize(userPoolId, userPoolClientId, jwt);
269+
270+
expect(res).toEqual({ success: false });
271+
expect(mockLogger.logMessages).toContainEqual(
272+
expect.objectContaining({
273+
level: 'error',
274+
message: expect.stringContaining('Failed to authorize'),
275+
issues: expect.arrayContaining([
276+
expect.objectContaining({
277+
path: ['nhs-notify:client-id'],
278+
message: 'Invalid input: expected string, received undefined',
279+
}),
280+
]),
281+
})
282+
);
283+
});
284+
248285
test('returns failure on Cognito not validating the token', async () => {
249286
const cognitoErrorUserPool = 'user-pool-id-cognito-error';
250287

@@ -253,7 +290,7 @@ describe('LambdaCognitoAuthorizer', () => {
253290
token_use: 'access',
254291
client_id: 'user-pool-client-id',
255292
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-error',
256-
clientId: 'nhs-notify-client-id',
293+
'nhs-notify:client-id': 'nhs-notify-client-id',
257294
},
258295
'key',
259296
{
@@ -285,7 +322,7 @@ describe('LambdaCognitoAuthorizer', () => {
285322
token_use: 'access',
286323
client_id: 'user-pool-client-id',
287324
iss: `https://cognito-idp.eu-west-2.amazonaws.com/${iss}`,
288-
clientId: 'nhs-notify-client-id',
325+
'nhs-notify:client-id': 'nhs-notify-client-id',
289326
},
290327
'key',
291328
{
@@ -312,7 +349,7 @@ describe('LambdaCognitoAuthorizer', () => {
312349
token_use: 'access',
313350
client_id: 'user-pool-client-id',
314351
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-no-sub',
315-
clientId: 'nhs-notify-client-id',
352+
'nhs-notify:client-id': 'nhs-notify-client-id',
316353
},
317354
'key',
318355
{
@@ -335,13 +372,13 @@ describe('LambdaCognitoAuthorizer', () => {
335372
);
336373
});
337374

338-
test('returns failure when sub on Cognito UserAttributes does not match expected subject', async () => {
375+
test('returns failure when expected resource owner does not match notify client id from Cognito', async () => {
339376
const jwt = sign(
340377
{
341378
token_use: 'access',
342379
client_id: 'user-pool-client-id',
343380
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
344-
clientId: 'nhs-notify-client-id',
381+
'nhs-notify:client-id': 'nhs-notify-client-id',
345382
},
346383
'key',
347384
{
@@ -360,7 +397,7 @@ describe('LambdaCognitoAuthorizer', () => {
360397
expect(mockLogger.logMessages).toContainEqual(
361398
expect.objectContaining({
362399
level: 'warn',
363-
message: 'Subject path does not match expected subject',
400+
message: 'clientId does not match expected resource owner',
364401
})
365402
);
366403
});
@@ -372,7 +409,7 @@ describe('LambdaCognitoAuthorizer', () => {
372409
client_id: 'user-pool-client-id',
373410
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
374411
exp: 1_640_995_200,
375-
clientId: 'nhs-notify-client-id',
412+
'nhs-notify:client-id': 'nhs-notify-client-id',
376413
},
377414
'key',
378415
{

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

Lines changed: 14 additions & 5 deletions
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().optional(),
15+
'nhs-notify:client-id': z.string(),
1616
});
1717

1818
export class LambdaCognitoAuthorizer {
@@ -25,7 +25,7 @@ export class LambdaCognitoAuthorizer {
2525
userPoolId: string,
2626
userPoolClientId: string,
2727
jwt: string,
28-
expectedSubject?: string
28+
expectedResourceOwner?: string
2929
): Promise<
3030
{ success: true; subject: string; clientId?: string } | { success: false }
3131
> {
@@ -90,14 +90,23 @@ export class LambdaCognitoAuthorizer {
9090
return { success: false };
9191
}
9292

93-
if (expectedSubject !== undefined && expectedSubject !== sub) {
94-
this.logger.warn('Subject path does not match expected subject');
93+
if (
94+
expectedResourceOwner !== undefined &&
95+
expectedResourceOwner !== notifyClientId
96+
) {
97+
this.logger.warn('clientId does not match expected resource owner');
9598
return { success: false };
9699
}
97100

98101
return { success: true, subject: sub, clientId: notifyClientId };
99102
} catch (error) {
100-
this.logger.error('Failed to authorize:', error);
103+
this.logger
104+
.child(
105+
error instanceof Error && 'issues' in error
106+
? { issues: error.issues }
107+
: {}
108+
)
109+
.error('Failed to authorize:', error);
101110
return { success: false };
102111
}
103112
}

0 commit comments

Comments
 (0)