Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
850e2a4
CCM-1875: make client required for create
alexnuttall Aug 7, 2025
aed2586
CCM-1875: call getOwner
alexnuttall Aug 7, 2025
28b65d3
CCM-10387: wip
alexnuttall Aug 7, 2025
8d916ba
CCM-10387: delete lamdba gsi perms
alexnuttall Aug 7, 2025
800155a
CCM-10387: wip
alexnuttall Aug 8, 2025
dc6e58e
CCM-10387: coverage
alexnuttall Aug 8, 2025
1b980db
CCM-10387: fix whitespace
alexnuttall Aug 8, 2025
fab51ea
CCM-10387: api tests pass
alexnuttall Aug 8, 2025
57909cb
CCM-10387: lint fix
alexnuttall Aug 8, 2025
ce93b80
CCM-10387: pw tests
alexnuttall Aug 8, 2025
e587d7d
CCM-10387: fix template repo unit tests
alexnuttall Aug 8, 2025
2aa9844
CCM-10387: cleanup
alexnuttall Aug 8, 2025
4d9670e
CCM-10387: fix api test
alexnuttall Aug 8, 2025
dfc5268
CCM-10387: pw tests
alexnuttall Aug 11, 2025
13104fe
CCM-10387: shared ownership tests
alexnuttall Aug 11, 2025
a0f840f
CCM-10387: fix comment
alexnuttall Aug 11, 2025
90e120b
CCM-10387: cleanup
alexnuttall Aug 11, 2025
0007da0
CCM-10422: init
alexnuttall Aug 11, 2025
8a5b7c1
CCM-10422: unit tests
alexnuttall Aug 11, 2025
8efd8fa
CCM-10422: rename arg
alexnuttall Aug 11, 2025
85606f3
CCM-10422: fix test name
alexnuttall Aug 11, 2025
db3b8fa
CCM-10430: init
alexnuttall Aug 12, 2025
748ae70
CCM-10430: controller tests
alexnuttall Aug 12, 2025
bfed29d
CCM-10430: line endings
alexnuttall Aug 12, 2025
ea74dc7
CCM-10430: tfdocs
alexnuttall Aug 12, 2025
73d5959
CCM-10430: cleanup
alexnuttall Aug 12, 2025
1a0f9ab
CCM-10430: rm conditional fetch of client config
alexnuttall Aug 13, 2025
93be729
CCM-1875: make client required for create
alexnuttall Aug 7, 2025
ef0d28b
CCM-1875: call getOwner
alexnuttall Aug 7, 2025
fd8ee1f
CCM-10387: wip
alexnuttall Aug 7, 2025
282f410
CCM-10387: delete lamdba gsi perms
alexnuttall Aug 7, 2025
3a46601
CCM-10387: wip
alexnuttall Aug 8, 2025
0ad6d33
CCM-10387: coverage
alexnuttall Aug 8, 2025
292e550
CCM-10387: fix whitespace
alexnuttall Aug 8, 2025
9ae0003
CCM-10387: api tests pass
alexnuttall Aug 8, 2025
5620d21
CCM-10387: lint fix
alexnuttall Aug 8, 2025
6c0400c
CCM-10387: pw tests
alexnuttall Aug 8, 2025
d71b3b3
CCM-10387: fix template repo unit tests
alexnuttall Aug 8, 2025
a8f6db9
CCM-10387: cleanup
alexnuttall Aug 8, 2025
ce4516f
CCM-10387: fix api test
alexnuttall Aug 8, 2025
5fc2f1b
CCM-10387: pw tests
alexnuttall Aug 11, 2025
d9b2a35
CCM-10387: shared ownership tests
alexnuttall Aug 11, 2025
a2bcb73
CCM-10387: cleanup
alexnuttall Aug 11, 2025
466f0eb
CCM-1875: add more api unit tests
alexnuttall Aug 11, 2025
c2cf094
CCM-1875: allow create without client
alexnuttall Aug 11, 2025
95d798b
CCM-10387: refactor
alexnuttall Aug 12, 2025
e40c434
CCM-10387: coverage
alexnuttall Aug 12, 2025
f7f1809
CCM-10387: fmt fix
alexnuttall Aug 12, 2025
05e62dd
CCM-10387: cleanup
alexnuttall Aug 12, 2025
4abc3e0
CCM-10430: rm api test not needed
alexnuttall Aug 13, 2025
fd89a76
CCM-10387: ownership by default
alexnuttall Aug 14, 2025
f4211d9
CCM-10387: add api test for proof request shared ownership
alexnuttall Aug 14, 2025
e3bc983
CCM-10387: letter e2e proof links update
alexnuttall Aug 14, 2025
68417e6
CCM-10387: fix pw
alexnuttall Aug 14, 2025
8574ecd
CCM-10387: fix unit tests
alexnuttall Aug 14, 2025
57a485a
Merge branch 'feature/CCM-10387-multi-user-update' into feature/CCM-1…
alexnuttall Aug 14, 2025
342b54e
CCM-10422: merge
alexnuttall Aug 14, 2025
ee9c154
CCM-10422: revert
alexnuttall Aug 14, 2025
849108f
CCM-10422: revert
alexnuttall Aug 14, 2025
a86ec50
CCM-10422: request proof sftp use clientid
alexnuttall Aug 14, 2025
cad1232
CCM-10422: update dl auth
alexnuttall Aug 14, 2025
3c152d2
CCM-10422: fix assert
alexnuttall Aug 14, 2025
1f8245b
CCM-10422: cleanup
alexnuttall Aug 14, 2025
600b443
fix whitespace
alexnuttall Aug 14, 2025
e028575
pa11y fix
alexnuttall Aug 14, 2025
7f31c51
Merge branch 'feature/CCM-10387-multi-user-update' into feature/CCM-1…
alexnuttall Aug 14, 2025
702d400
gitignore test reports
alexnuttall Aug 14, 2025
4b01caa
Merge branch 'feature/CCM-10387-multi-user-update' into feature/CCM-1…
alexnuttall Aug 14, 2025
7b7f8ef
CCM-1875: make client required for create
alexnuttall Aug 7, 2025
6c44bc7
CCM-1875: call getOwner
alexnuttall Aug 7, 2025
8226491
CCM-10387: wip
alexnuttall Aug 7, 2025
7b8f178
CCM-10387: delete lamdba gsi perms
alexnuttall Aug 7, 2025
20804dd
CCM-10387: wip
alexnuttall Aug 8, 2025
e21d323
CCM-10387: coverage
alexnuttall Aug 8, 2025
3b4567d
CCM-10387: fix whitespace
alexnuttall Aug 8, 2025
565ed37
CCM-10387: api tests pass
alexnuttall Aug 8, 2025
ca71503
CCM-10387: lint fix
alexnuttall Aug 8, 2025
3c96e50
CCM-10387: pw tests
alexnuttall Aug 8, 2025
0fa9ba8
CCM-10387: fix template repo unit tests
alexnuttall Aug 8, 2025
ba0f109
CCM-10387: cleanup
alexnuttall Aug 8, 2025
a23edd1
CCM-10387: fix api test
alexnuttall Aug 8, 2025
fde84b2
CCM-10387: pw tests
alexnuttall Aug 11, 2025
557810e
CCM-10387: shared ownership tests
alexnuttall Aug 11, 2025
f2136ff
CCM-10387: cleanup
alexnuttall Aug 11, 2025
69f87af
CCM-1875: add more api unit tests
alexnuttall Aug 11, 2025
75e2d96
CCM-1875: allow create without client
alexnuttall Aug 11, 2025
153da90
CCM-10387: refactor
alexnuttall Aug 12, 2025
608d583
CCM-10387: coverage
alexnuttall Aug 12, 2025
79affd4
CCM-10387: fmt fix
alexnuttall Aug 12, 2025
783a766
CCM-10387: cleanup
alexnuttall Aug 12, 2025
15475d8
CCM-10430: rm api test not needed
alexnuttall Aug 13, 2025
1ec34c9
CCM-10387: ownership by default
alexnuttall Aug 14, 2025
f46a71e
CCM-10387: add api test for proof request shared ownership
alexnuttall Aug 14, 2025
1d27c70
CCM-10387: letter e2e proof links update
alexnuttall Aug 14, 2025
6804209
CCM-10387: fix pw
alexnuttall Aug 14, 2025
3e76ae7
CCM-10387: fix unit tests
alexnuttall Aug 14, 2025
e811195
CCM-10422: request proof sftp use clientid
alexnuttall Aug 14, 2025
1049d4e
CCM-10422: cleanup
alexnuttall Aug 14, 2025
294d951
pa11y fix
alexnuttall Aug 14, 2025
991edca
gitignore test reports
alexnuttall Aug 14, 2025
1ba9e4e
empty
alexnuttall Aug 15, 2025
d2eaf89
cleanup
alexnuttall Aug 15, 2025
0f22677
Merge branch 'feature/CCM-10387-multi-user-update' into feature/CCM-1…
alexnuttall Aug 15, 2025
8fd08a5
rm flag
alexnuttall Aug 15, 2025
1e7ec5f
api tests
alexnuttall Aug 18, 2025
7382bc8
fmt
alexnuttall Aug 18, 2025
d9be1f8
remove user ownership from letter validation pipeline
alexnuttall Aug 18, 2025
c3bd5b3
cleanup, rm extra client
alexnuttall Aug 18, 2025
94ba270
fmt
alexnuttall Aug 18, 2025
2aa4a74
fix api tests
alexnuttall Aug 18, 2025
5de049a
in validate files, dont fetch by userId
alexnuttall Aug 18, 2025
ae295ed
test for user-owned letter submission with proofing enabled
alexnuttall Aug 18, 2025
450c707
make getClientId work with user owned templates
alexnuttall Aug 18, 2025
8769507
error on multiple ownership
alexnuttall Aug 18, 2025
eb7c068
comments
alexnuttall Aug 19, 2025
b286ad9
use filter expr
alexnuttall Aug 19, 2025
d0d8235
Merge branch 'feature/CCM-10387-multi-user-update' into feature/CCM-1…
alexnuttall Aug 19, 2025
401cfe2
Merge branch 'main' into feature/CCM-10422-download-auth-client-owner
alexnuttall Aug 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 50 additions & 13 deletions utils/utils/src/__tests__/lambda-cognito-authorizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,31 @@ describe('LambdaCognitoAuthorizer', () => {
expect(mockLogger.logMessages).toEqual([]);
});

test('returns success on valid token without notify-client-id', async () => {
test('returns success on valid token when expected resource owner is specified', async () => {
const jwt = sign(
{
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
keyid: 'key-id',
}
);

const res = await authorizer.authorize(userPoolId, userPoolClientId, jwt);
const res = await authorizer.authorize(
userPoolId,
userPoolClientId,
jwt,
'nhs-notify-client-id'
);

expect(res).toEqual({
success: true,
subject: 'sub',
clientId: 'nhs-notify-client-id',
});
expect(mockLogger.logMessages).toEqual([]);
});
Expand Down Expand Up @@ -152,7 +159,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key'
);
Expand All @@ -174,7 +181,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'access',
client_id: 'user-pool-client-id-2',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand All @@ -200,7 +207,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-2',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand All @@ -226,7 +233,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'id',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand All @@ -245,6 +252,36 @@ describe('LambdaCognitoAuthorizer', () => {
);
});

test('returns failure when no NHS Notify client ID is present in the access token', async () => {
const jwt = sign(
{
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
},
'key',
{
keyid: 'key-id',
}
);

const res = await authorizer.authorize(userPoolId, userPoolClientId, jwt);

expect(res).toEqual({ success: false });
expect(mockLogger.logMessages).toContainEqual(
expect.objectContaining({
level: 'error',
message: expect.stringContaining('Failed to authorize'),
issues: expect.arrayContaining([
expect.objectContaining({
path: ['nhs-notify:client-id'],
message: 'Invalid input: expected string, received undefined',
}),
]),
})
);
});

test('returns failure on Cognito not validating the token', async () => {
const cognitoErrorUserPool = 'user-pool-id-cognito-error';

Expand All @@ -253,7 +290,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-error',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand Down Expand Up @@ -285,7 +322,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'access',
client_id: 'user-pool-client-id',
iss: `https://cognito-idp.eu-west-2.amazonaws.com/${iss}`,
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand All @@ -312,7 +349,7 @@ describe('LambdaCognitoAuthorizer', () => {
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-no-sub',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand All @@ -335,13 +372,13 @@ describe('LambdaCognitoAuthorizer', () => {
);
});

test('returns failure when sub on Cognito UserAttributes does not match expected subject', async () => {
test('returns failure when expected resource owner does not match notify client id from Cognito', async () => {
const jwt = sign(
{
token_use: 'access',
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand All @@ -360,7 +397,7 @@ describe('LambdaCognitoAuthorizer', () => {
expect(mockLogger.logMessages).toContainEqual(
expect.objectContaining({
level: 'warn',
message: 'Subject path does not match expected subject',
message: 'clientId does not match expected resource owner',
})
);
});
Expand All @@ -372,7 +409,7 @@ describe('LambdaCognitoAuthorizer', () => {
client_id: 'user-pool-client-id',
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id',
exp: 1_640_995_200,
clientId: 'nhs-notify-client-id',
'nhs-notify:client-id': 'nhs-notify-client-id',
},
'key',
{
Expand Down
19 changes: 14 additions & 5 deletions utils/utils/src/lambda-cognito-authorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const $AccessToken = z.object({
client_id: z.string(),
iss: z.string(),
token_use: z.string(),
'nhs-notify:client-id': z.string().optional(),
'nhs-notify:client-id': z.string(),
});

export class LambdaCognitoAuthorizer {
Expand All @@ -25,7 +25,7 @@ export class LambdaCognitoAuthorizer {
userPoolId: string,
userPoolClientId: string,
jwt: string,
expectedSubject?: string
expectedResourceOwner?: string
): Promise<
{ success: true; subject: string; clientId?: string } | { success: false }
> {
Expand Down Expand Up @@ -90,14 +90,23 @@ export class LambdaCognitoAuthorizer {
return { success: false };
}

if (expectedSubject !== undefined && expectedSubject !== sub) {
this.logger.warn('Subject path does not match expected subject');
if (
expectedResourceOwner !== undefined &&
expectedResourceOwner !== notifyClientId
) {
this.logger.warn('clientId does not match expected resource owner');
return { success: false };
}

return { success: true, subject: sub, clientId: notifyClientId };
} catch (error) {
this.logger.error('Failed to authorize:', error);
this.logger
.child(
error instanceof Error && 'issues' in error
? { issues: error.issues }
: {}
)
.error('Failed to authorize:', error);
return { success: false };
}
}
Expand Down