-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTokenHandler.ts
98 lines (86 loc) · 3.14 KB
/
TokenHandler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { Tag } from '@aws-sdk/client-iam';
import { APIGatewayEventRequestContextWithAuthorizer, APIGatewayProxyEventHeaders } from 'aws-lambda';
import fetch, { RequestInit } from "node-fetch";
import { v4 as uuidv4 } from 'uuid';
import { signJWTWithKMS } from './KMS';
import { TokenResponse } from "./TokenResponse";
import { getApiData } from './gateway';
import { getRoleArn, getTagForRole, signJWTWithSecret } from './secret';
import assert = require('assert');
export interface JWTBodyOptions {
iss: string;
sub: string;
aud: string[] | string;
jti: string;
exp: number;
nbf: number | null;
iat: number | null;
}
export async function createJWT(clientId: string, aud: string, roleTag: Tag): Promise<{
token: string, emrPath: {
customer: string;
clientAppId: string;
}
}> {
const tNow = Math.floor(Date.now() / 1000);
const tEnd = tNow + 300;
const message: JWTBodyOptions = {
iss: clientId,
sub: clientId,
aud: aud,
jti: uuidv4(),
nbf: tNow,
iat: tNow,
exp: tEnd
};
const tagKey = roleTag.Key;
if (tagKey === 'SecretAccess') {
return await signJWTWithSecret(roleTag, message);
}
if (tagKey === 'KMSAccess') {
return await signJWTWithKMS(roleTag, message);
}
throw new Error(`Tag with key ${tagKey} is unsupported`)
}
export async function fetchBackendToken(eventHeaders: APIGatewayProxyEventHeaders, eventRequestContext: APIGatewayEventRequestContextWithAuthorizer<{
[name: string]: any;
}>) {
const apiId = process.env.API_ID;
assert(apiId, 'An apiId env variable must be included in the request (for the token endpoint)')
const emrType = eventHeaders.emrType
assert(emrType, 'An emrType header must be included in the request')
const clientId = eventHeaders.clientId
assert(clientId, 'A clientId header must be included in the request')
const apiData = await getApiData(apiId, emrType)
const roleArn = getRoleArn(eventRequestContext);
const roleTag = await getTagForRole(roleArn);
const { token, emrPath } = await createJWT(clientId, apiData.aud, roleTag);
const tokenResponse = await fetchAuthToken(clientId, apiData.invokeUrl, {
grant_type: "client_credentials",
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: token,
scope: "system/Patient.read system/Group.read"
});
return { tokenResponse, emrPath };
}
export async function fetchAuthToken(clientId: string, tokenEndpoint: string, params: { grant_type: string; } & Record<string, string>, authorization?: { Authorization: string; }) {
const fetchParams: RequestInit = {
method: "POST",
headers: {
accept: "application/x-www-form-urlencoded",
...(authorization ?? {})
},
body: (new URLSearchParams(params)),
};
const tokenFetchResponse = await fetch(tokenEndpoint, fetchParams);
if (!tokenFetchResponse.ok) throw new Error(JSON.stringify(
{
status: tokenFetchResponse.status,
text: await tokenFetchResponse.text(),
statusText: tokenFetchResponse.statusText,
clientId: clientId
}
))
const tokenResponse = await (tokenFetchResponse.json() as Promise<TokenResponse>);
return tokenResponse;
}