-
Notifications
You must be signed in to change notification settings - Fork 880
/
index.ts
121 lines (106 loc) · 4.11 KB
/
index.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2016-2019, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as apigateway from "@pulumi/aws-apigateway";
import * as awsx from "@pulumi/awsx";
import * as pulumi from "@pulumi/pulumi";
import * as jwt from "jsonwebtoken";
import * as jwksClient from "jwks-rsa";
import * as util from "util";
const config = new pulumi.Config();
const jwksUri = config.require("jwksUri");
const audience = config.require("audience");
const issuer = config.require("issuer");
const authorizerLambda = async (event: apigateway.AuthorizerEvent) => {
try {
return await authenticate(event);
}
catch (err) {
console.log(err);
// Tells API Gateway to return a 401 Unauthorized response
throw new Error("Unauthorized");
}
};
// Create our API and reference the Lambda authorizer
const api = new awsx.apigateway.API("myapi", {
routes: [{
path: "/hello",
method: "GET",
eventHandler: async () => {
return {
statusCode: 200,
body: "<h1>Hello world!</h1>",
};
},
authorizers: apigateway.getTokenLambdaAuthorizer({
authorizerName: "jwt-rsa-custom-authorizer",
header: "Authorization",
handler: authorizerLambda,
identityValidationExpression: "^Bearer [-0-9a-zA-Z\._]*$",
authorizerResultTtlInSeconds: 3600,
}),
}],
});
// Export the URL for our API
export const url = api.url;
/**
* Below is all code that gets added to the Authorizer Lambda. The code was copied and
* converted to TypeScript from [Auth0's GitHub
* Example](https://github.com/auth0-samples/jwt-rsa-aws-custom-authorizer)
*/
// Extract and return the Bearer Token from the Lambda event parameters
function getToken(event: awsx.apigateway.AuthorizerEvent): string {
if (!event.type || event.type !== "TOKEN") {
throw new Error('Expected "event.type" parameter to have value "TOKEN"');
}
const tokenString = event.authorizationToken;
if (!tokenString) {
throw new Error('Expected "event.authorizationToken" parameter to be set');
}
const match = tokenString.match(/^Bearer (.*)$/);
if (!match) {
throw new Error(`Invalid Authorization token - ${tokenString} does not match "Bearer .*"`);
}
return match[1];
}
// Check the Token is valid with Auth0
async function authenticate(event: awsx.apigateway.AuthorizerEvent): Promise<awsx.apigateway.AuthorizerResponse> {
console.log(event);
const token = getToken(event);
const decoded = jwt.decode(token, { complete: true });
if (!decoded || typeof decoded === "string" || !decoded.header || !decoded.header.kid) {
throw new Error("invalid token");
}
const client = jwksClient({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10, // Default value
jwksUri: jwksUri,
});
const key = await util.promisify(client.getSigningKey)(decoded.header.kid);
const signingKey = key.publicKey || key.rsaPublicKey;
if (!signingKey) {
throw new Error("could not get signing key");
}
const verifiedJWT = await jwt.verify(token, signingKey, { audience, issuer });
if (!verifiedJWT || typeof verifiedJWT === "string" || !isVerifiedJWT(verifiedJWT)) {
throw new Error("could not verify JWT");
}
return awsx.apigateway.authorizerResponse(verifiedJWT.sub, "Allow", event.methodArn);
}
interface VerifiedJWT {
sub: string;
}
function isVerifiedJWT(toBeDetermined: VerifiedJWT | Object): toBeDetermined is VerifiedJWT {
return (<VerifiedJWT>toBeDetermined).sub !== undefined;
}