Skip to content

Commit 11821f2

Browse files
authored
Merge pull request #235 from NHSDigital/feature/CCM-7465_use-jwt-sub
CCM-7465 : Update APIs to use sub as the owner field in DDB PT.1
2 parents 00cb47a + e5252c3 commit 11821f2

File tree

13 files changed

+76
-61
lines changed

13 files changed

+76
-61
lines changed

infrastructure/terraform/components/sandbox/aws_cognito_user_pool_client_sandbox.tf

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ resource "aws_cognito_user_pool_client" "sandbox" {
44

55
explicit_auth_flows = [
66
"ALLOW_USER_PASSWORD_AUTH",
7-
"ALLOW_REFRESH_TOKEN_AUTH"
7+
"ALLOW_REFRESH_TOKEN_AUTH",
8+
"ALLOW_USER_SRP_AUTH"
89
]
10+
11+
access_token_validity = 1 # 1 hour
12+
id_token_validity = 1 # 1 hour
13+
refresh_token_validity = 1 # 1 day
14+
15+
token_validity_units {
16+
access_token = "hours"
17+
id_token = "hours"
18+
refresh_token = "days"
19+
}
920
}

infrastructure/terraform/components/sandbox/outputs.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ output "cognito_user_pool_id" {
99
output "cognito_user_pool_client_id" {
1010
value = aws_cognito_user_pool_client.sandbox.id
1111
}
12+
13+
output "dynamodb_table_templates" {
14+
value = module.backend_api.dynamodb_table_templates
15+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
output "api_base_url" {
22
value = aws_api_gateway_stage.main.invoke_url
33
}
4+
5+
output "dynamodb_table_templates" {
6+
value = aws_dynamodb_table.templates.name
7+
}

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jest.mock('@aws-sdk/client-cognito-identity-provider', () => {
3232
) {
3333
return {
3434
Username: undefined,
35-
UserAttributes: [{ Name: 'email', Value: 'email' }],
35+
UserAttributes: [{ Name: 'sub', Value: 'sub' }],
3636
};
3737
}
3838

@@ -48,17 +48,17 @@ jest.mock('@aws-sdk/client-cognito-identity-provider', () => {
4848

4949
if (
5050
decodedJwt.iss ===
51-
'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-no-email'
51+
'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-no-sub'
5252
) {
5353
return {
5454
Username: 'username',
55-
UserAttributes: [{ Name: 'NOT-EMAIL', Value: 'not-email' }],
55+
UserAttributes: [{ Name: 'NOT-SUB', Value: 'not-sub' }],
5656
};
5757
}
5858

5959
return {
6060
Username: 'username',
61-
UserAttributes: [{ Name: 'email', Value: 'email' }],
61+
UserAttributes: [{ Name: 'sub', Value: 'sub' }],
6262
};
6363
}
6464
}
@@ -100,8 +100,7 @@ const allowPolicy = {
100100
],
101101
},
102102
context: {
103-
username: 'username',
104-
email: 'email',
103+
user: 'sub',
105104
},
106105
};
107106

@@ -361,14 +360,14 @@ test.each([
361360
expect(warnMock).toHaveBeenCalledWith('Missing user');
362361
});
363362

364-
test('returns Deny policy, when no email on Cognito UserAttributes', async () => {
365-
process.env.USER_POOL_ID = 'user-pool-id-cognito-no-email';
363+
test('returns Deny policy, when no sub on Cognito UserAttributes', async () => {
364+
process.env.USER_POOL_ID = 'user-pool-id-cognito-no-sub';
366365

367366
const jwt = sign(
368367
{
369368
token_use: 'access',
370369
client_id: 'user-pool-client-id',
371-
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-no-email',
370+
iss: 'https://cognito-idp.eu-west-2.amazonaws.com/user-pool-id-cognito-no-sub',
372371
},
373372
'key',
374373
{
@@ -387,7 +386,7 @@ test('returns Deny policy, when no email on Cognito UserAttributes', async () =>
387386
);
388387

389388
expect(res).toEqual(denyPolicy);
390-
expect(warnMock).toHaveBeenCalledWith('Missing user email address');
389+
expect(warnMock).toHaveBeenCalledWith('Missing user subject');
391390
});
392391

393392
test('returns Allow policy on valid token', async () => {

lambdas/authorizer/src/index.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const $AccessToken = z.object({
2222
const generatePolicy = (
2323
Resource: string,
2424
Effect: 'Allow' | 'Deny',
25-
context?: { username: string; email: string }
25+
context?: { user: string }
2626
) => ({
2727
principalId: 'api-caller',
2828
policyDocument: {
@@ -110,18 +110,15 @@ export const handler: APIGatewayRequestAuthorizerHandler = async ({
110110
return generatePolicy(methodArn, 'Deny');
111111
}
112112

113-
const emailAddress = UserAttributes.find(
114-
({ Name }) => Name === 'email'
115-
)?.Value;
113+
const sub = UserAttributes.find(({ Name }) => Name === 'sub')?.Value;
116114

117-
if (!emailAddress) {
118-
logger.warn('Missing user email address');
115+
if (!sub) {
116+
logger.warn('Missing user subject');
119117
return generatePolicy(methodArn, 'Deny');
120118
}
121119

122120
return generatePolicy(methodArn, 'Allow', {
123-
username: Username,
124-
email: emailAddress,
121+
user: sub,
125122
});
126123
} catch (error) {
127124
logger.error(error);

lambdas/backend-api/src/__tests__/templates/api/create.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ const createMock = jest.spyOn(TemplateClient.prototype, 'createTemplate');
1616
describe('Template API - Create', () => {
1717
beforeEach(jest.resetAllMocks);
1818

19-
test('should return 400 - Invalid request when, no email in requestContext', async () => {
19+
test('should return 400 - Invalid request when, no user in requestContext', async () => {
2020
const event = mock<APIGatewayProxyEvent>({
21-
requestContext: { authorizer: { email: undefined } },
21+
requestContext: { authorizer: { user: undefined } },
2222
body: JSON.stringify({ id: 1 }),
2323
});
2424

@@ -48,7 +48,7 @@ describe('Template API - Create', () => {
4848
});
4949

5050
const event = mock<APIGatewayProxyEvent>({
51-
requestContext: { authorizer: { email: 'email' } },
51+
requestContext: { authorizer: { user: 'sub' } },
5252
body: undefined,
5353
});
5454

@@ -65,7 +65,7 @@ describe('Template API - Create', () => {
6565
}),
6666
});
6767

68-
expect(TemplateClient).toHaveBeenCalledWith('email');
68+
expect(TemplateClient).toHaveBeenCalledWith('sub');
6969

7070
expect(createMock).toHaveBeenCalledWith({});
7171
});
@@ -79,7 +79,7 @@ describe('Template API - Create', () => {
7979
});
8080

8181
const event = mock<APIGatewayProxyEvent>({
82-
requestContext: { authorizer: { email: 'email' } },
82+
requestContext: { authorizer: { user: 'sub' } },
8383
body: JSON.stringify({ id: 1 }),
8484
});
8585

@@ -93,7 +93,7 @@ describe('Template API - Create', () => {
9393
}),
9494
});
9595

96-
expect(TemplateClient).toHaveBeenCalledWith('email');
96+
expect(TemplateClient).toHaveBeenCalledWith('sub');
9797

9898
expect(createMock).toHaveBeenCalledWith({ id: 1 });
9999
});
@@ -117,7 +117,7 @@ describe('Template API - Create', () => {
117117
});
118118

119119
const event = mock<APIGatewayProxyEvent>({
120-
requestContext: { authorizer: { email: 'email' } },
120+
requestContext: { authorizer: { user: 'sub' } },
121121
body: JSON.stringify(create),
122122
});
123123

@@ -128,7 +128,7 @@ describe('Template API - Create', () => {
128128
body: JSON.stringify({ statusCode: 201, template: response }),
129129
});
130130

131-
expect(TemplateClient).toHaveBeenCalledWith('email');
131+
expect(TemplateClient).toHaveBeenCalledWith('sub');
132132

133133
expect(createMock).toHaveBeenCalledWith(create);
134134
});

lambdas/backend-api/src/__tests__/templates/api/get.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ const getTemplateMock = jest.spyOn(TemplateClient.prototype, 'getTemplate');
1515
describe('Template API - Get', () => {
1616
beforeEach(jest.resetAllMocks);
1717

18-
test('should return 400 - Invalid request when, no email in requestContext', async () => {
18+
test('should return 400 - Invalid request when, no user in requestContext', async () => {
1919
const event = mock<APIGatewayProxyEvent>({
20-
requestContext: { authorizer: { email: undefined } },
20+
requestContext: { authorizer: { user: undefined } },
2121
pathParameters: { templateId: '1' },
2222
});
2323

@@ -36,7 +36,7 @@ describe('Template API - Get', () => {
3636

3737
test('should return 400 - Invalid request when, no templateId', async () => {
3838
const event = mock<APIGatewayProxyEvent>({
39-
requestContext: { authorizer: { email: 'email' } },
39+
requestContext: { authorizer: { user: 'sub' } },
4040
pathParameters: { templateId: undefined },
4141
});
4242

@@ -62,7 +62,7 @@ describe('Template API - Get', () => {
6262
});
6363

6464
const event = mock<APIGatewayProxyEvent>({
65-
requestContext: { authorizer: { email: 'email' } },
65+
requestContext: { authorizer: { user: 'sub' } },
6666
pathParameters: { templateId: '1' },
6767
});
6868

@@ -76,7 +76,7 @@ describe('Template API - Get', () => {
7676
}),
7777
});
7878

79-
expect(TemplateClient).toHaveBeenCalledWith('email');
79+
expect(TemplateClient).toHaveBeenCalledWith('sub');
8080
expect(getTemplateMock).toHaveBeenCalledWith('1');
8181
});
8282

@@ -96,7 +96,7 @@ describe('Template API - Get', () => {
9696
});
9797

9898
const event = mock<APIGatewayProxyEvent>({
99-
requestContext: { authorizer: { email: 'email' } },
99+
requestContext: { authorizer: { user: 'sub' } },
100100
pathParameters: { templateId: '1' },
101101
});
102102

@@ -107,7 +107,7 @@ describe('Template API - Get', () => {
107107
body: JSON.stringify({ statusCode: 200, template }),
108108
});
109109

110-
expect(TemplateClient).toHaveBeenCalledWith('email');
110+
expect(TemplateClient).toHaveBeenCalledWith('sub');
111111
expect(getTemplateMock).toHaveBeenCalledWith('1');
112112
});
113113
});

lambdas/backend-api/src/__tests__/templates/api/list.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ const listTemplatesMock = jest.spyOn(TemplateClient.prototype, 'listTemplates');
1515
describe('Template API - List', () => {
1616
beforeEach(jest.resetAllMocks);
1717

18-
test('should return 400 - Invalid request when, no email in requestContext', async () => {
18+
test('should return 400 - Invalid request when, no user in requestContext', async () => {
1919
const event = mock<APIGatewayProxyEvent>({
20-
requestContext: { authorizer: { email: undefined } },
20+
requestContext: { authorizer: { user: undefined } },
2121
});
2222

2323
const result = await handler(event, mock<Context>(), jest.fn());
@@ -42,7 +42,7 @@ describe('Template API - List', () => {
4242
});
4343

4444
const event = mock<APIGatewayProxyEvent>({
45-
requestContext: { authorizer: { email: 'email' } },
45+
requestContext: { authorizer: { user: 'sub' } },
4646
pathParameters: { templateId: '1' },
4747
});
4848

@@ -56,7 +56,7 @@ describe('Template API - List', () => {
5656
}),
5757
});
5858

59-
expect(TemplateClient).toHaveBeenCalledWith('email');
59+
expect(TemplateClient).toHaveBeenCalledWith('sub');
6060

6161
expect(listTemplatesMock).toHaveBeenCalled();
6262
});
@@ -77,7 +77,7 @@ describe('Template API - List', () => {
7777
});
7878

7979
const event = mock<APIGatewayProxyEvent>({
80-
requestContext: { authorizer: { email: 'email' } },
80+
requestContext: { authorizer: { user: 'sub' } },
8181
});
8282

8383
const result = await handler(event, mock<Context>(), jest.fn());
@@ -87,7 +87,7 @@ describe('Template API - List', () => {
8787
body: JSON.stringify({ statusCode: 200, templates: [template] }),
8888
});
8989

90-
expect(TemplateClient).toHaveBeenCalledWith('email');
90+
expect(TemplateClient).toHaveBeenCalledWith('sub');
9191

9292
expect(listTemplatesMock).toHaveBeenCalled();
9393
});

lambdas/backend-api/src/__tests__/templates/api/update.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ const updateTemplateMock = jest.spyOn(
1919
describe('Template API - Update', () => {
2020
beforeEach(jest.resetAllMocks);
2121

22-
test('should return 400 - Invalid request when, no email in requestContext', async () => {
22+
test('should return 400 - Invalid request when, no user in requestContext', async () => {
2323
const event = mock<APIGatewayProxyEvent>({
24-
requestContext: { authorizer: { email: undefined } },
24+
requestContext: { authorizer: { user: undefined } },
2525
body: JSON.stringify({ name: 'test' }),
2626
pathParameters: { templateId: '1-2-3' },
2727
});
@@ -52,7 +52,7 @@ describe('Template API - Update', () => {
5252
});
5353

5454
const event = mock<APIGatewayProxyEvent>({
55-
requestContext: { authorizer: { email: 'email' } },
55+
requestContext: { authorizer: { user: 'sub' } },
5656
pathParameters: { templateId: '1-2-3' },
5757
body: undefined,
5858
});
@@ -70,14 +70,14 @@ describe('Template API - Update', () => {
7070
}),
7171
});
7272

73-
expect(TemplateClient).toHaveBeenCalledWith('email');
73+
expect(TemplateClient).toHaveBeenCalledWith('sub');
7474

7575
expect(updateTemplateMock).toHaveBeenCalledWith('1-2-3', {});
7676
});
7777

7878
test('should return 400 - Invalid request when, no templateId', async () => {
7979
const event = mock<APIGatewayProxyEvent>({
80-
requestContext: { authorizer: { email: 'email' } },
80+
requestContext: { authorizer: { user: 'sub' } },
8181
body: JSON.stringify({ name: 'test' }),
8282
pathParameters: { templateId: undefined },
8383
});
@@ -104,7 +104,7 @@ describe('Template API - Update', () => {
104104
});
105105

106106
const event = mock<APIGatewayProxyEvent>({
107-
requestContext: { authorizer: { email: 'email' } },
107+
requestContext: { authorizer: { user: 'sub' } },
108108
body: JSON.stringify({ name: 'name' }),
109109
pathParameters: { templateId: '1-2-3' },
110110
});
@@ -119,7 +119,7 @@ describe('Template API - Update', () => {
119119
}),
120120
});
121121

122-
expect(TemplateClient).toHaveBeenCalledWith('email');
122+
expect(TemplateClient).toHaveBeenCalledWith('sub');
123123

124124
expect(updateTemplateMock).toHaveBeenCalledWith('1-2-3', { name: 'name' });
125125
});
@@ -144,7 +144,7 @@ describe('Template API - Update', () => {
144144
});
145145

146146
const event = mock<APIGatewayProxyEvent>({
147-
requestContext: { authorizer: { email: 'email' } },
147+
requestContext: { authorizer: { user: 'sub' } },
148148
body: JSON.stringify(update),
149149
pathParameters: { templateId: '1-2-3' },
150150
});
@@ -156,7 +156,7 @@ describe('Template API - Update', () => {
156156
body: JSON.stringify({ statusCode: 200, template: response }),
157157
});
158158

159-
expect(TemplateClient).toHaveBeenCalledWith('email');
159+
expect(TemplateClient).toHaveBeenCalledWith('sub');
160160

161161
expect(updateTemplateMock).toHaveBeenCalledWith('1-2-3', update);
162162
});

lambdas/backend-api/src/templates/api/create.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { TemplateClient } from '@backend-api/templates/app/template-client';
33
import { apiFailure, apiSuccess } from './responses';
44

55
export const handler: APIGatewayProxyHandler = async (event) => {
6-
const email = event.requestContext.authorizer?.email;
6+
const user = event.requestContext.authorizer?.user;
77

88
const dto = JSON.parse(event.body || '{}');
99

10-
if (!email) {
10+
if (!user) {
1111
return apiFailure(400, 'Invalid request');
1212
}
1313

14-
const client = new TemplateClient(email);
14+
const client = new TemplateClient(user);
1515

1616
const { data, error } = await client.createTemplate(dto);
1717

0 commit comments

Comments
 (0)