@liflig/cdk 3.0.22 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * This lambda verifies authorization header against static basic auth credentials saved in Secret
3
+ * Manager.
4
+ *
5
+ * Expects the following environment variables:
6
+ * - CREDENTIALS_SECRET_NAME
7
+ * - Secret value should follow this format: `{"username":"<username>","password":"<password>"}`
8
+ */
9
+ import type { APIGatewayRequestAuthorizerEventV2, APIGatewaySimpleAuthorizerResult } from "aws-lambda";
10
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
11
+ export declare const handler: (event: APIGatewayRequestAuthorizerEventV2) => Promise<APIGatewaySimpleAuthorizerResult>;
12
+ /** For overriding dependency creation in tests. */
13
+ export declare const dependencies: {
14
+ createSecretsManager: () => SecretsManager;
15
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * This lambda verifies authorization header against static basic auth credentials saved in Secret
3
+ * Manager.
4
+ *
5
+ * Expects the following environment variables:
6
+ * - CREDENTIALS_SECRET_NAME
7
+ * - Secret value should follow this format: `{"username":"<username>","password":"<password>"}`
8
+ */
9
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
10
+ export const handler = async (event) => {
11
+ const authHeader = event.headers?.authorization;
12
+ if (!authHeader || !authHeader.startsWith("Basic ")) {
13
+ return { isAuthorized: false };
14
+ }
15
+ const expectedAuthHeader = await getExpectedAuthHeader();
16
+ return { isAuthorized: authHeader === expectedAuthHeader };
17
+ };
18
+ /** Cache this value, so that subsequent lambda invocations don't have to refetch. */
19
+ let cachedAuthHeader = undefined;
20
+ async function getExpectedAuthHeader() {
21
+ if (cachedAuthHeader === undefined) {
22
+ const secretName = process.env["CREDENTIALS_SECRET_NAME"];
23
+ if (!secretName) {
24
+ console.error("CREDENTIALS_SECRET_NAME env variable is not defined");
25
+ throw new Error();
26
+ }
27
+ cachedAuthHeader = await getSecretAsBasicAuthHeader(secretName);
28
+ }
29
+ return cachedAuthHeader;
30
+ }
31
+ async function getSecretAsBasicAuthHeader(secretName) {
32
+ const credentials = await getSecretValue(secretName);
33
+ if (!secretHasExpectedFormat(credentials)) {
34
+ console.error(`Basic auth credentials secret did not follow expected format (secret name: '${secretName}')`);
35
+ throw new Error();
36
+ }
37
+ return ("Basic " +
38
+ Buffer.from(`${credentials.username}:${credentials.password}`).toString("base64"));
39
+ }
40
+ /** For overriding dependency creation in tests. */
41
+ export const dependencies = {
42
+ createSecretsManager: () => new SecretsManager(),
43
+ };
44
+ async function getSecretValue(secretName) {
45
+ const client = dependencies.createSecretsManager();
46
+ const secret = await client.getSecretValue({ SecretId: secretName });
47
+ if (!secret.SecretString) {
48
+ console.error(`Secret value not found for '${secretName}'`);
49
+ throw new Error();
50
+ }
51
+ return JSON.parse(secret.SecretString);
52
+ }
53
+ function secretHasExpectedFormat(value) {
54
+ return (typeof value === "object" &&
55
+ value !== null &&
56
+ "username" in value &&
57
+ typeof value.username === "string" &&
58
+ "password" in value &&
59
+ typeof value.password === "string");
60
+ }
61
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzaWMtYXV0aC1hdXRob3JpemVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS1nYXRld2F5L2F1dGhvcml6ZXItbGFtYmRhcy9iYXNpYy1hdXRoLWF1dGhvcml6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7R0FPRztBQU1ILE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQTtBQUVoRSxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsS0FBSyxFQUMxQixLQUF5QyxFQUNFLEVBQUU7SUFDN0MsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLE9BQU8sRUFBRSxhQUFhLENBQUE7SUFDL0MsSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNwRCxPQUFPLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxDQUFBO0lBQ2hDLENBQUM7SUFFRCxNQUFNLGtCQUFrQixHQUFHLE1BQU0scUJBQXFCLEVBQUUsQ0FBQTtJQUV4RCxPQUFPLEVBQUUsWUFBWSxFQUFFLFVBQVUsS0FBSyxrQkFBa0IsRUFBRSxDQUFBO0FBQzVELENBQUMsQ0FBQTtBQUVELHFGQUFxRjtBQUNyRixJQUFJLGdCQUFnQixHQUF1QixTQUFTLENBQUE7QUFFcEQsS0FBSyxVQUFVLHFCQUFxQjtJQUNsQyxJQUFJLGdCQUFnQixLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ25DLE1BQU0sVUFBVSxHQUNkLE9BQU8sQ0FBQyxHQUFHLENBQUMseUJBQXlCLENBQUMsQ0FBQTtRQUN4QyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDaEIsT0FBTyxDQUFDLEtBQUssQ0FBQyxxREFBcUQsQ0FBQyxDQUFBO1lBQ3BFLE1BQU0sSUFBSSxLQUFLLEVBQUUsQ0FBQTtRQUNuQixDQUFDO1FBRUQsZ0JBQWdCLEdBQUcsTUFBTSwwQkFBMEIsQ0FBQyxVQUFVLENBQUMsQ0FBQTtJQUNqRSxDQUFDO0lBRUQsT0FBTyxnQkFBZ0IsQ0FBQTtBQUN6QixDQUFDO0FBRUQsS0FBSyxVQUFVLDBCQUEwQixDQUFDLFVBQWtCO0lBQzFELE1BQU0sV0FBVyxHQUFHLE1BQU0sY0FBYyxDQUFDLFVBQVUsQ0FBQyxDQUFBO0lBQ3BELElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO1FBQzFDLE9BQU8sQ0FBQyxLQUFLLENBQ1gsK0VBQStFLFVBQVUsSUFBSSxDQUM5RixDQUFBO1FBQ0QsTUFBTSxJQUFJLEtBQUssRUFBRSxDQUFBO0lBQ25CLENBQUM7SUFFRCxPQUFPLENBQ0wsUUFBUTtRQUNSLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxXQUFXLENBQUMsUUFBUSxJQUFJLFdBQVcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FDckUsUUFBUSxDQUNULENBQ0YsQ0FBQTtBQUNILENBQUM7QUFFRCxtREFBbUQ7QUFDbkQsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFHO0lBQzFCLG9CQUFvQixFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksY0FBYyxFQUFFO0NBQ2pELENBQUE7QUFFRCxLQUFLLFVBQVUsY0FBYyxDQUFDLFVBQWtCO0lBQzlDLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxvQkFBb0IsRUFBRSxDQUFBO0lBQ2xELE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLGNBQWMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFBO0lBRXBFLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDekIsT0FBTyxDQUFDLEtBQUssQ0FBQywrQkFBK0IsVUFBVSxHQUFHLENBQUMsQ0FBQTtRQUMzRCxNQUFNLElBQUksS0FBSyxFQUFFLENBQUE7SUFDbkIsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUE7QUFDeEMsQ0FBQztBQUVELFNBQVMsdUJBQXVCLENBQzlCLEtBQWM7SUFFZCxPQUFPLENBQ0wsT0FBTyxLQUFLLEtBQUssUUFBUTtRQUN6QixLQUFLLEtBQUssSUFBSTtRQUNkLFVBQVUsSUFBSSxLQUFLO1FBQ25CLE9BQU8sS0FBSyxDQUFDLFFBQVEsS0FBSyxRQUFRO1FBQ2xDLFVBQVUsSUFBSSxLQUFLO1FBQ25CLE9BQU8sS0FBSyxDQUFDLFFBQVEsS0FBSyxRQUFRLENBQ25DLENBQUE7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGlzIGxhbWJkYSB2ZXJpZmllcyBhdXRob3JpemF0aW9uIGhlYWRlciBhZ2FpbnN0IHN0YXRpYyBiYXNpYyBhdXRoIGNyZWRlbnRpYWxzIHNhdmVkIGluIFNlY3JldFxuICogTWFuYWdlci5cbiAqXG4gKiBFeHBlY3RzIHRoZSBmb2xsb3dpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBDUkVERU5USUFMU19TRUNSRVRfTkFNRVxuICogICAtIFNlY3JldCB2YWx1ZSBzaG91bGQgZm9sbG93IHRoaXMgZm9ybWF0OiBge1widXNlcm5hbWVcIjpcIjx1c2VybmFtZT5cIixcInBhc3N3b3JkXCI6XCI8cGFzc3dvcmQ+XCJ9YFxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgQVBJR2F0ZXdheVJlcXVlc3RBdXRob3JpemVyRXZlbnRWMixcbiAgQVBJR2F0ZXdheVNpbXBsZUF1dGhvcml6ZXJSZXN1bHQsXG59IGZyb20gXCJhd3MtbGFtYmRhXCJcbmltcG9ydCB7IFNlY3JldHNNYW5hZ2VyIH0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXJcIlxuXG5leHBvcnQgY29uc3QgaGFuZGxlciA9IGFzeW5jIChcbiAgZXZlbnQ6IEFQSUdhdGV3YXlSZXF1ZXN0QXV0aG9yaXplckV2ZW50VjIsXG4pOiBQcm9taXNlPEFQSUdhdGV3YXlTaW1wbGVBdXRob3JpemVyUmVzdWx0PiA9PiB7XG4gIGNvbnN0IGF1dGhIZWFkZXIgPSBldmVudC5oZWFkZXJzPy5hdXRob3JpemF0aW9uXG4gIGlmICghYXV0aEhlYWRlciB8fCAhYXV0aEhlYWRlci5zdGFydHNXaXRoKFwiQmFzaWMgXCIpKSB7XG4gICAgcmV0dXJuIHsgaXNBdXRob3JpemVkOiBmYWxzZSB9XG4gIH1cblxuICBjb25zdCBleHBlY3RlZEF1dGhIZWFkZXIgPSBhd2FpdCBnZXRFeHBlY3RlZEF1dGhIZWFkZXIoKVxuXG4gIHJldHVybiB7IGlzQXV0aG9yaXplZDogYXV0aEhlYWRlciA9PT0gZXhwZWN0ZWRBdXRoSGVhZGVyIH1cbn1cblxuLyoqIENhY2hlIHRoaXMgdmFsdWUsIHNvIHRoYXQgc3Vic2VxdWVudCBsYW1iZGEgaW52b2NhdGlvbnMgZG9uJ3QgaGF2ZSB0byByZWZldGNoLiAqL1xubGV0IGNhY2hlZEF1dGhIZWFkZXI6IHN0cmluZyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZFxuXG5hc3luYyBmdW5jdGlvbiBnZXRFeHBlY3RlZEF1dGhIZWFkZXIoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgaWYgKGNhY2hlZEF1dGhIZWFkZXIgPT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IHNlY3JldE5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZCA9XG4gICAgICBwcm9jZXNzLmVudltcIkNSRURFTlRJQUxTX1NFQ1JFVF9OQU1FXCJdXG4gICAgaWYgKCFzZWNyZXROYW1lKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwiQ1JFREVOVElBTFNfU0VDUkVUX05BTUUgZW52IHZhcmlhYmxlIGlzIG5vdCBkZWZpbmVkXCIpXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoKVxuICAgIH1cblxuICAgIGNhY2hlZEF1dGhIZWFkZXIgPSBhd2FpdCBnZXRTZWNyZXRBc0Jhc2ljQXV0aEhlYWRlcihzZWNyZXROYW1lKVxuICB9XG5cbiAgcmV0dXJuIGNhY2hlZEF1dGhIZWFkZXJcbn1cblxuYXN5bmMgZnVuY3Rpb24gZ2V0U2VjcmV0QXNCYXNpY0F1dGhIZWFkZXIoc2VjcmV0TmFtZTogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgY3JlZGVudGlhbHMgPSBhd2FpdCBnZXRTZWNyZXRWYWx1ZShzZWNyZXROYW1lKVxuICBpZiAoIXNlY3JldEhhc0V4cGVjdGVkRm9ybWF0KGNyZWRlbnRpYWxzKSkge1xuICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICBgQmFzaWMgYXV0aCBjcmVkZW50aWFscyBzZWNyZXQgZGlkIG5vdCBmb2xsb3cgZXhwZWN0ZWQgZm9ybWF0IChzZWNyZXQgbmFtZTogJyR7c2VjcmV0TmFtZX0nKWAsXG4gICAgKVxuICAgIHRocm93IG5ldyBFcnJvcigpXG4gIH1cblxuICByZXR1cm4gKFxuICAgIFwiQmFzaWMgXCIgK1xuICAgIEJ1ZmZlci5mcm9tKGAke2NyZWRlbnRpYWxzLnVzZXJuYW1lfToke2NyZWRlbnRpYWxzLnBhc3N3b3JkfWApLnRvU3RyaW5nKFxuICAgICAgXCJiYXNlNjRcIixcbiAgICApXG4gIClcbn1cblxuLyoqIEZvciBvdmVycmlkaW5nIGRlcGVuZGVuY3kgY3JlYXRpb24gaW4gdGVzdHMuICovXG5leHBvcnQgY29uc3QgZGVwZW5kZW5jaWVzID0ge1xuICBjcmVhdGVTZWNyZXRzTWFuYWdlcjogKCkgPT4gbmV3IFNlY3JldHNNYW5hZ2VyKCksXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldFNlY3JldFZhbHVlKHNlY3JldE5hbWU6IHN0cmluZyk6IFByb21pc2U8dW5rbm93bj4ge1xuICBjb25zdCBjbGllbnQgPSBkZXBlbmRlbmNpZXMuY3JlYXRlU2VjcmV0c01hbmFnZXIoKVxuICBjb25zdCBzZWNyZXQgPSBhd2FpdCBjbGllbnQuZ2V0U2VjcmV0VmFsdWUoeyBTZWNyZXRJZDogc2VjcmV0TmFtZSB9KVxuXG4gIGlmICghc2VjcmV0LlNlY3JldFN0cmluZykge1xuICAgIGNvbnNvbGUuZXJyb3IoYFNlY3JldCB2YWx1ZSBub3QgZm91bmQgZm9yICcke3NlY3JldE5hbWV9J2ApXG4gICAgdGhyb3cgbmV3IEVycm9yKClcbiAgfVxuXG4gIHJldHVybiBKU09OLnBhcnNlKHNlY3JldC5TZWNyZXRTdHJpbmcpXG59XG5cbmZ1bmN0aW9uIHNlY3JldEhhc0V4cGVjdGVkRm9ybWF0KFxuICB2YWx1ZTogdW5rbm93bixcbik6IHZhbHVlIGlzIHsgdXNlcm5hbWU6IHN0cmluZzsgcGFzc3dvcmQ6IHN0cmluZyB9IHtcbiAgcmV0dXJuIChcbiAgICB0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIgJiZcbiAgICB2YWx1ZSAhPT0gbnVsbCAmJlxuICAgIFwidXNlcm5hbWVcIiBpbiB2YWx1ZSAmJlxuICAgIHR5cGVvZiB2YWx1ZS51c2VybmFtZSA9PT0gXCJzdHJpbmdcIiAmJlxuICAgIFwicGFzc3dvcmRcIiBpbiB2YWx1ZSAmJlxuICAgIHR5cGVvZiB2YWx1ZS5wYXNzd29yZCA9PT0gXCJzdHJpbmdcIlxuICApXG59XG4iXX0=
@@ -0,0 +1,48 @@
1
+ /**
2
+ * This lambda verifies bearer token in authorization header using Cognito.
3
+ *
4
+ * Expects the following environment variables:
5
+ * - USER_POOL_ID
6
+ * - REQUIRED_SCOPE (optional)
7
+ * - Set this to require that the bearer token payload contains the given scope
8
+ * - CREDENTIALS_FOR_INTERNAL_AUTHORIZATION (optional)
9
+ * - Secret name from which to get basic auth credentials that should be forwarded to backend
10
+ * integration if authentication succeeds
11
+ * - Secret value should follow this format: `{"username":"<username>","password":"<password>"}`
12
+ */
13
+ import type { APIGatewayRequestAuthorizerEventV2, APIGatewaySimpleAuthorizerResult } from "aws-lambda";
14
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
15
+ import type { CognitoAccessTokenPayload } from "aws-jwt-verify/jwt-model";
16
+ type AuthorizerResult = APIGatewaySimpleAuthorizerResult & {
17
+ /**
18
+ * Returning a context object from our authorizer allows our API Gateway to access these variables
19
+ * via `${context.authorizer.<property>}`.
20
+ * https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging-variables.html
21
+ */
22
+ context?: {
23
+ /**
24
+ * If the token is verified, we return the auth client ID from the token's claims as a context
25
+ * variable (named `authorizer.clientId`). You can then use this for parameter mapping on the
26
+ * API Gateway (see `AlbIntegrationProps.mapParameters` on the `ApiGateway` construct), if for
27
+ * example you want to forward this to the backend integration.
28
+ */
29
+ clientId: string;
30
+ /**
31
+ * If `CREDENTIALS_FOR_INTERNAL_AUTHORIZATION` is provided, we want to forward basic auth
32
+ * credentials to our backend, as an additional authentication layer. See the docstring on
33
+ * `CognitoUserPoolAuthorizerProps.basicAuthForInternalAuthorization` in the `ApiGateway`
34
+ * construct for more on this.
35
+ */
36
+ internalAuthorizationHeader?: string;
37
+ };
38
+ };
39
+ export declare const handler: (event: APIGatewayRequestAuthorizerEventV2) => Promise<AuthorizerResult>;
40
+ export type TokenVerifier = {
41
+ verify: (accessToken: string) => Promise<CognitoAccessTokenPayload>;
42
+ };
43
+ /** For overriding dependency creation in tests. */
44
+ export declare const dependencies: {
45
+ createTokenVerifier: () => TokenVerifier;
46
+ createSecretsManager: () => SecretsManager;
47
+ };
48
+ export {};
@@ -0,0 +1,129 @@
1
+ /**
2
+ * This lambda verifies bearer token in authorization header using Cognito.
3
+ *
4
+ * Expects the following environment variables:
5
+ * - USER_POOL_ID
6
+ * - REQUIRED_SCOPE (optional)
7
+ * - Set this to require that the bearer token payload contains the given scope
8
+ * - CREDENTIALS_FOR_INTERNAL_AUTHORIZATION (optional)
9
+ * - Secret name from which to get basic auth credentials that should be forwarded to backend
10
+ * integration if authentication succeeds
11
+ * - Secret value should follow this format: `{"username":"<username>","password":"<password>"}`
12
+ */
13
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
14
+ import { CognitoJwtVerifier } from "aws-jwt-verify";
15
+ export const handler = async (event) => {
16
+ const authHeader = event.headers?.authorization;
17
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
18
+ return { isAuthorized: false };
19
+ }
20
+ const result = await verifyAccessToken(authHeader.substring(7)); // substring(7) == after 'Bearer '
21
+ switch (result) {
22
+ case "INVALID":
23
+ return { isAuthorized: false };
24
+ case "EXPIRED":
25
+ // We want to return 401 Unauthorized for expired tokens, so the client knows to refresh
26
+ // their token when receiving this status code. API Gateway authorizer lambdas return
27
+ // 403 Forbidden for {isAuthorized: false}, but there is a way to return 401: throwing an
28
+ // error with this exact string. https://stackoverflow.com/a/71965890
29
+ throw new Error("Unauthorized");
30
+ default: {
31
+ return {
32
+ isAuthorized: true,
33
+ context: {
34
+ clientId: result.client_id,
35
+ internalAuthorizationHeader: await getInternalAuthorizationHeader(),
36
+ },
37
+ };
38
+ }
39
+ }
40
+ };
41
+ /** Decodes and verifies the given token against Cognito. */
42
+ async function verifyAccessToken(token) {
43
+ try {
44
+ const tokenVerifier = getTokenVerifier();
45
+ // Must await here instead of returning the promise directly, so that errors can be caught in
46
+ // this function
47
+ return await tokenVerifier.verify(token);
48
+ }
49
+ catch (e) {
50
+ // If the JWT has expired, aws-jwt-verify throws this error:
51
+ // https://github.com/awslabs/aws-jwt-verify/blob/8d8f714d7281913ecd660147f5c30311479601c1/src/jwt.ts#L197
52
+ // We can't check instanceof on that error class, since it's not exported, so this is the next
53
+ // best thing.
54
+ if (e instanceof Error && e.message?.includes("Token expired")) {
55
+ return "EXPIRED";
56
+ }
57
+ else {
58
+ return "INVALID";
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * We cache the verifier in this global variable, so that subsequent invocations of a hot lambda
64
+ * will re-use this.
65
+ */
66
+ let cachedTokenVerifier = undefined;
67
+ function getTokenVerifier() {
68
+ if (cachedTokenVerifier === undefined) {
69
+ cachedTokenVerifier = dependencies.createTokenVerifier();
70
+ }
71
+ return cachedTokenVerifier;
72
+ }
73
+ /** For overriding dependency creation in tests. */
74
+ export const dependencies = {
75
+ createTokenVerifier: () => {
76
+ const userPoolId = process.env["USER_POOL_ID"];
77
+ if (!userPoolId) {
78
+ console.error("USER_POOL_ID env variable is not defined");
79
+ throw new Error();
80
+ }
81
+ return CognitoJwtVerifier.create({
82
+ userPoolId,
83
+ tokenUse: "access",
84
+ clientId: null,
85
+ scope: process.env.REQUIRED_SCOPE || undefined, // `|| undefined` to discard empty string
86
+ });
87
+ },
88
+ createSecretsManager: () => new SecretsManager(),
89
+ };
90
+ /** Cache this value, so that subsequent lambda invocations don't have to refetch. */
91
+ let cachedInternalAuthorizationHeader = undefined;
92
+ async function getInternalAuthorizationHeader() {
93
+ if (cachedInternalAuthorizationHeader === undefined) {
94
+ const secretName = process.env["CREDENTIALS_FOR_INTERNAL_AUTHORIZATION"];
95
+ if (!secretName) {
96
+ return undefined;
97
+ }
98
+ cachedInternalAuthorizationHeader =
99
+ await getSecretAsBasicAuthHeader(secretName);
100
+ }
101
+ return cachedInternalAuthorizationHeader;
102
+ }
103
+ async function getSecretAsBasicAuthHeader(secretName) {
104
+ const credentials = await getSecretValue(secretName);
105
+ if (!secretHasExpectedFormat(credentials)) {
106
+ console.error(`Basic auth credentials secret did not follow expected format (secret name: '${secretName}')`);
107
+ throw new Error();
108
+ }
109
+ return ("Basic " +
110
+ Buffer.from(`${credentials.username}:${credentials.password}`).toString("base64"));
111
+ }
112
+ async function getSecretValue(secretName) {
113
+ const client = dependencies.createSecretsManager();
114
+ const secret = await client.getSecretValue({ SecretId: secretName });
115
+ if (!secret.SecretString) {
116
+ console.error(`Secret value not found for '${secretName}'`);
117
+ throw new Error();
118
+ }
119
+ return JSON.parse(secret.SecretString);
120
+ }
121
+ function secretHasExpectedFormat(value) {
122
+ return (typeof value === "object" &&
123
+ value !== null &&
124
+ "username" in value &&
125
+ typeof value.username === "string" &&
126
+ "password" in value &&
127
+ typeof value.password === "string");
128
+ }
129
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29nbml0by11c2VyLXBvb2wtYXV0aG9yaXplci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcGktZ2F0ZXdheS9hdXRob3JpemVyLWxhbWJkYXMvY29nbml0by11c2VyLXBvb2wtYXV0aG9yaXplci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7R0FXRztBQU1ILE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQTtBQUNoRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQTJCbkQsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLEtBQUssRUFDMUIsS0FBeUMsRUFDZCxFQUFFO0lBQzdCLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxPQUFPLEVBQUUsYUFBYSxDQUFBO0lBQy9DLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsQ0FBQTtJQUNoQyxDQUFDO0lBRUQsTUFBTSxNQUFNLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUEsQ0FBQyxrQ0FBa0M7SUFDbEcsUUFBUSxNQUFNLEVBQUUsQ0FBQztRQUNmLEtBQUssU0FBUztZQUNaLE9BQU8sRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLENBQUE7UUFDaEMsS0FBSyxTQUFTO1lBQ1osd0ZBQXdGO1lBQ3hGLHFGQUFxRjtZQUNyRix5RkFBeUY7WUFDekYscUVBQXFFO1lBQ3JFLE1BQU0sSUFBSSxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUE7UUFDakMsT0FBTyxDQUFDLENBQUMsQ0FBQztZQUNSLE9BQU87Z0JBQ0wsWUFBWSxFQUFFLElBQUk7Z0JBQ2xCLE9BQU8sRUFBRTtvQkFDUCxRQUFRLEVBQUUsTUFBTSxDQUFD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UUFBUSxJQUFJLFdBQVcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FDckUsUUFBUSxDQUNULENBQ0YsQ0FBQTtBQUNILENBQUM7QUFFRCxLQUFLLFVBQVUsY0FBYyxDQUFDLFVBQWtCO0lBQzlDLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxvQkFBb0IsRUFBRSxDQUFBO0lBQ2xELE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLGNBQWMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFBO0lBRXBFLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDekIsT0FBTyxDQUFDLEtBQUssQ0FBQywrQkFBK0IsVUFBVSxHQUFHLENBQUMsQ0FBQTtRQUMzRCxNQUFNLElBQUksS0FBSyxFQUFFLENBQUE7SUFDbkIsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUE7QUFDeEMsQ0FBQztBQUVELFNBQVMsdUJBQXVCLENBQzlCLEtBQWM7SUFFZCxPQUFPLENBQ0wsT0FBTyxLQUFLLEtBQUssUUFBUTtRQUN6QixLQUFLLEtBQUssSUFBSTtRQUNkLFVBQVUsSUFBSSxLQUFLO1FBQ25CLE9BQU8sS0FBSyxDQUFDLFFBQVEsS0FBSyxRQUFRO1FBQ2xDLFVBQVUsSUFBSSxLQUFLO1FBQ25CLE9BQU8sS0FBSyxDQUFDLFFBQVEsS0FBSyxRQUFRLENBQ25DLENBQUE7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaGlzIGxhbWJkYSB2ZXJpZmllcyBiZWFyZXIgdG9rZW4gaW4gYXV0aG9yaXphdGlvbiBoZWFkZXIgdXNpbmcgQ29nbml0by5cbiAqXG4gKiBFeHBlY3RzIHRoZSBmb2xsb3dpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBVU0VSX1BPT0xfSURcbiAqIC0gUkVRVUlSRURfU0NPUEUgKG9wdGlvbmFsKVxuICogICAtIFNldCB0aGlzIHRvIHJlcXVpcmUgdGhhdCB0aGUgYmVhcmVyIHRva2VuIHBheWxvYWQgY29udGFpbnMgdGhlIGdpdmVuIHNjb3BlXG4gKiAtIENSRURFTlRJQUxTX0ZPUl9JTlRFUk5BTF9BVVRIT1JJWkFUSU9OIChvcHRpb25hbClcbiAqICAgLSBTZWNyZXQgbmFtZSBmcm9tIHdoaWNoIHRvIGdldCBiYXNpYyBhdXRoIGNyZWRlbnRpYWxzIHRoYXQgc2hvdWxkIGJlIGZvcndhcmRlZCB0byBiYWNrZW5kXG4gKiAgICAgaW50ZWdyYXRpb24gaWYgYXV0aGVudGljYXRpb24gc3VjY2VlZHNcbiAqICAgLSBTZWNyZXQgdmFsdWUgc2hvdWxkIGZvbGxvdyB0aGlzIGZvcm1hdDogYHtcInVzZXJuYW1lXCI6XCI8dXNlcm5hbWU+XCIsXCJwYXNzd29yZFwiOlwiPHBhc3N3b3JkPlwifWBcbiAqL1xuXG5pbXBvcnQgdHlwZSB7XG4gIEFQSUdhdGV3YXlSZXF1ZXN0QXV0aG9yaXplckV2ZW50VjIsXG4gIEFQSUdhdGV3YXlTaW1wbGVBdXRob3JpemVyUmVzdWx0LFxufSBmcm9tIFwiYXdzLWxhbWJkYVwiXG5pbXBvcnQgeyBTZWNyZXRzTWFuYWdlciB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtc2VjcmV0cy1tYW5hZ2VyXCJcbmltcG9ydCB7IENvZ25pdG9Kd3RWZXJpZmllciB9IGZyb20gXCJhd3Mtand0LXZlcmlmeVwiXG5pbXBvcnQgdHlwZSB7IENvZ25pdG9BY2Nlc3NUb2tlblBheWxvYWQgfSBmcm9tIFwiYXdzLWp3dC12ZXJpZnkvand0LW1vZGVsXCJcblxudHlwZSBBdXRob3JpemVyUmVzdWx0ID0gQVBJR2F0ZXdheVNpbXBsZUF1dGhvcml6ZXJSZXN1bHQgJiB7XG4gIC8qKlxuICAgKiBSZXR1cm5pbmcgYSBjb250ZXh0IG9iamVjdCBmcm9tIG91ciBhdXRob3JpemVyIGFsbG93cyBvdXIgQVBJIEdhdGV3YXkgdG8gYWNjZXNzIHRoZXNlIHZhcmlhYmxlc1xuICAgKiB2aWEgYCR7Y29udGV4dC5hdXRob3JpemVyLjxwcm9wZXJ0eT59YC5cbiAgICogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2FwaWdhdGV3YXkvbGF0ZXN0L2RldmVsb3Blcmd1aWRlL2h0dHAtYXBpLWxvZ2dpbmctdmFyaWFibGVzLmh0bWxcbiAgICovXG4gIGNvbnRleHQ/OiB7XG4gICAgLyoqXG4gICAgICogSWYgdGhlIHRva2VuIGlzIHZlcmlmaWVkLCB3ZSByZXR1cm4gdGhlIGF1dGggY2xpZW50IElEIGZyb20gdGhlIHRva2VuJ3MgY2xhaW1zIGFzIGEgY29udGV4dFxuICAgICAqIHZhcmlhYmxlIChuYW1lZCBgYXV0aG9yaXplci5jbGllbnRJZGApLiBZb3UgY2FuIHRoZW4gdXNlIHRoaXMgZm9yIHBhcmFtZXRlciBtYXBwaW5nIG9uIHRoZVxuICAgICAqIEFQSSBHYXRld2F5IChzZWUgYEFsYkludGVncmF0aW9uUHJvcHMubWFwUGFyYW1ldGVyc2Agb24gdGhlIGBBcGlHYXRld2F5YCBjb25zdHJ1Y3QpLCBpZiBmb3JcbiAgICAgKiBleGFtcGxlIHlvdSB3YW50IHRvIGZvcndhcmQgdGhpcyB0byB0aGUgYmFja2VuZCBpbnRlZ3JhdGlvbi5cbiAgICAgKi9cbiAgICBjbGllbnRJZDogc3RyaW5nXG4gICAgLyoqXG4gICAgICogSWYgYENSRURFTlRJQUxTX0ZPUl9JTlRFUk5BTF9BVVRIT1JJWkFUSU9OYCBpcyBwcm92aWRlZCwgd2Ugd2FudCB0byBmb3J3YXJkIGJhc2ljIGF1dGhcbiAgICAgKiBjcmVkZW50aWFscyB0byBvdXIgYmFja2VuZCwgYXMgYW4gYWRkaXRpb25hbCBhdXRoZW50aWNhdGlvbiBsYXllci4gU2VlIHRoZSBkb2NzdHJpbmcgb25cbiAgICAgKiBgQ29nbml0b1VzZXJQb29sQXV0aG9yaXplclByb3BzLmJhc2ljQXV0aEZvckludGVybmFsQXV0aG9yaXphdGlvbmAgaW4gdGhlIGBBcGlHYXRld2F5YFxuICAgICAqIGNvbnN0cnVjdCBmb3IgbW9yZSBvbiB0aGlzLlxuICAgICAqL1xuICAgIGludGVybmFsQXV0aG9yaXphdGlvbkhlYWRlcj86IHN0cmluZ1xuICB9XG59XG5cbmV4cG9ydCBjb25zdCBoYW5kbGVyID0gYXN5bmMgKFxuICBldmVudDogQVBJR2F0ZXdheVJlcXVlc3RBdXRob3JpemVyRXZlbnRWMixcbik6IFByb21pc2U8QXV0aG9yaXplclJlc3VsdD4gPT4ge1xuICBjb25zdCBhdXRoSGVhZGVyID0gZXZlbnQuaGVhZGVycz8uYXV0aG9yaXphdGlvblxuICBpZiAoIWF1dGhIZWFkZXIgfHwgIWF1dGhIZWFkZXIuc3RhcnRzV2l0aChcIkJlYXJlciBcIikpIHtcbiAgICByZXR1cm4geyBpc0F1dGhvcml6ZWQ6IGZhbHNlIH1cbiAgfVxuXG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHZlcmlmeUFjY2Vzc1Rva2VuKGF1dGhIZWFkZXIuc3Vic3RyaW5nKDcpKSAvLyBzdWJzdHJpbmcoNykgPT0gYWZ0ZXIgJ0JlYXJlciAnXG4gIHN3aXRjaCAocmVzdWx0KSB7XG4gICAgY2FzZSBcIklOVkFMSURcIjpcbiAgICAgIHJldHVybiB7IGlzQXV0aG9yaXplZDogZmFsc2UgfVxuICAgIGNhc2UgXCJFWFBJUkVEXCI6XG4gICAgICAvLyBXZSB3YW50IHRvIHJldHVybiA0MDEgVW5hdXRob3JpemVkIGZvciBleHBpcmVkIHRva2Vucywgc28gdGhlIGNsaWVudCBrbm93cyB0byByZWZyZXNoXG4gICAgICAvLyB0aGVpciB0b2tlbiB3aGVuIHJlY2VpdmluZyB0aGlzIHN0YXR1cyBjb2RlLiBBUEkgR2F0ZXdheSBhdXRob3JpemVyIGxhbWJkYXMgcmV0dXJuXG4gICAgICAvLyA0MDMgRm9yYmlkZGVuIGZvciB7aXNBdXRob3JpemVkOiBmYWxzZX0sIGJ1dCB0aGVyZSBpcyBhIHdheSB0byByZXR1cm4gNDAxOiB0aHJvd2luZyBhblxuICAgICAgLy8gZXJyb3Igd2l0aCB0aGlzIGV4YWN0IHN0cmluZy4gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzcxOTY1ODkwXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJVbmF1dGhvcml6ZWRcIilcbiAgICBkZWZhdWx0OiB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBpc0F1dGhvcml6ZWQ6IHRydWUsXG4gICAgICAgIGNvbnRleHQ6IHtcbiAgICAgICAgICBjbGllbnRJZDogcmVzdWx0LmNsaWVudF9pZCxcbiAgICAgICAgICBpbnRlcm5hbEF1dGhvcml6YXRpb25IZWFkZXI6IGF3YWl0IGdldEludGVybmFsQXV0aG9yaXphdGlvbkhlYWRlcigpLFxuICAgICAgICB9LFxuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG4vKiogRGVjb2RlcyBhbmQgdmVyaWZpZXMgdGhlIGdpdmVuIHRva2VuIGFnYWluc3QgQ29nbml0by4gKi9cbmFzeW5jIGZ1bmN0aW9uIHZlcmlmeUFjY2Vzc1Rva2VuKFxuICB0b2tlbjogc3RyaW5nLFxuKTogUHJvbWlzZTxDb2duaXRvQWNjZXNzVG9rZW5QYXlsb2FkIHwgXCJFWFBJUkVEXCIgfCBcIklOVkFMSURcIj4ge1xuICB0cnkge1xuICAgIGNvbnN0IHRva2VuVmVyaWZpZXIgPSBnZXRUb2tlblZlcmlmaWVyKClcbiAgICAvLyBNdXN0IGF3YWl0IGhlcmUgaW5zdGVhZCBvZiByZXR1cm5pbmcgdGhlIHByb21pc2UgZGlyZWN0bHksIHNvIHRoYXQgZXJyb3JzIGNhbiBiZSBjYXVnaHQgaW5cbiAgICAvLyB0aGlzIGZ1bmN0aW9uXG4gICAgcmV0dXJuIGF3YWl0IHRva2VuVmVyaWZpZXIudmVyaWZ5KHRva2VuKVxuICB9IGNhdGNoIChlKSB7XG4gICAgLy8gSWYgdGhlIEpXVCBoYXMgZXhwaXJlZCwgYXdzLWp3dC12ZXJpZnkgdGhyb3dzIHRoaXMgZXJyb3I6XG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2F3c2xhYnMvYXdzLWp3dC12ZXJpZnkvYmxvYi84ZDhmNzE0ZDcyODE5MTNlY2Q2NjAxNDdmNWMzMDMxMTQ3OTYwMWMxL3NyYy9qd3QudHMjTDE5N1xuICAgIC8vIFdlIGNhbid0IGNoZWNrIGluc3RhbmNlb2Ygb24gdGhhdCBlcnJvciBjbGFzcywgc2luY2UgaXQncyBub3QgZXhwb3J0ZWQsIHNvIHRoaXMgaXMgdGhlIG5leHRcbiAgICAvLyBiZXN0IHRoaW5nLlxuICAgIGlmIChlIGluc3RhbmNlb2YgRXJyb3IgJiYgZS5tZXNzYWdlPy5pbmNsdWRlcyhcIlRva2VuIGV4cGlyZWRcIikpIHtcbiAgICAgIHJldHVybiBcIkVYUElSRURcIlxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gXCJJTlZBTElEXCJcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgVG9rZW5WZXJpZmllciA9IHtcbiAgdmVyaWZ5OiAoYWNjZXNzVG9rZW46IHN0cmluZykgPT4gUHJvbWlzZTxDb2duaXRvQWNjZXNzVG9rZW5QYXlsb2FkPlxufVxuXG4vKipcbiAqIFdlIGNhY2hlIHRoZSB2ZXJpZmllciBpbiB0aGlzIGdsb2JhbCB2YXJpYWJsZSwgc28gdGhhdCBzdWJzZXF1ZW50IGludm9jYXRpb25zIG9mIGEgaG90IGxhbWJkYVxuICogd2lsbCByZS11c2UgdGhpcy5cbiAqL1xubGV0IGNhY2hlZFRva2VuVmVyaWZpZXI6IFRva2VuVmVyaWZpZXIgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcblxuZnVuY3Rpb24gZ2V0VG9rZW5WZXJpZmllcigpOiBUb2tlblZlcmlmaWVyIHtcbiAgaWYgKGNhY2hlZFRva2VuVmVyaWZpZXIgPT09IHVuZGVmaW5lZCkge1xuICAgIGNhY2hlZFRva2VuVmVyaWZpZXIgPSBkZXBlbmRlbmNpZXMuY3JlYXRlVG9rZW5WZXJpZmllcigpXG4gIH1cbiAgcmV0dXJuIGNhY2hlZFRva2VuVmVyaWZpZXJcbn1cblxuLyoqIEZvciBvdmVycmlkaW5nIGRlcGVuZGVuY3kgY3JlYXRpb24gaW4gdGVzdHMuICovXG5leHBvcnQgY29uc3QgZGVwZW5kZW5jaWVzID0ge1xuICBjcmVhdGVUb2tlblZlcmlmaWVyOiAoKTogVG9rZW5WZXJpZmllciA9PiB7XG4gICAgY29uc3QgdXNlclBvb2xJZCA9IHByb2Nlc3MuZW52W1wiVVNFUl9QT09MX0lEXCJdXG4gICAgaWYgKCF1c2VyUG9vbElkKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwiVVNFUl9QT09MX0lEIGVudiB2YXJpYWJsZSBpcyBub3QgZGVmaW5lZFwiKVxuICAgICAgdGhyb3cgbmV3IEVycm9yKClcbiAgICB9XG5cbiAgICByZXR1cm4gQ29nbml0b0p3dFZlcmlmaWVyLmNyZWF0ZSh7XG4gICAgICB1c2VyUG9vbElkLFxuICAgICAgdG9rZW5Vc2U6IFwiYWNjZXNzXCIsXG4gICAgICBjbGllbnRJZDogbnVsbCxcbiAgICAgIHNjb3BlOiBwcm9jZXNzLmVudi5SRVFVSVJFRF9TQ09QRSB8fCB1bmRlZmluZWQsIC8vIGB8fCB1bmRlZmluZWRgIHRvIGRpc2NhcmQgZW1wdHkgc3RyaW5nXG4gICAgfSlcbiAgfSxcbiAgY3JlYXRlU2VjcmV0c01hbmFnZXI6ICgpID0+IG5ldyBTZWNyZXRzTWFuYWdlcigpLFxufVxuXG4vKiogQ2FjaGUgdGhpcyB2YWx1ZSwgc28gdGhhdCBzdWJzZXF1ZW50IGxhbWJkYSBpbnZvY2F0aW9ucyBkb24ndCBoYXZlIHRvIHJlZmV0Y2guICovXG5sZXQgY2FjaGVkSW50ZXJuYWxBdXRob3JpemF0aW9uSGVhZGVyOiBzdHJpbmcgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcblxuYXN5bmMgZnVuY3Rpb24gZ2V0SW50ZXJuYWxBdXRob3JpemF0aW9uSGVhZGVyKCk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGlmIChjYWNoZWRJbnRlcm5hbEF1dGhvcml6YXRpb25IZWFkZXIgPT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IHNlY3JldE5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZCA9XG4gICAgICBwcm9jZXNzLmVudltcIkNSRURFTlRJQUxTX0ZPUl9JTlRFUk5BTF9BVVRIT1JJWkFUSU9OXCJdXG4gICAgaWYgKCFzZWNyZXROYW1lKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkXG4gICAgfVxuXG4gICAgY2FjaGVkSW50ZXJuYWxBdXRob3JpemF0aW9uSGVhZGVyID1cbiAgICAgIGF3YWl0IGdldFNlY3JldEFzQmFzaWNBdXRoSGVhZGVyKHNlY3JldE5hbWUpXG4gIH1cblxuICByZXR1cm4gY2FjaGVkSW50ZXJuYWxBdXRob3JpemF0aW9uSGVhZGVyXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldFNlY3JldEFzQmFzaWNBdXRoSGVhZGVyKHNlY3JldE5hbWU6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IGNyZWRlbnRpYWxzID0gYXdhaXQgZ2V0U2VjcmV0VmFsdWUoc2VjcmV0TmFtZSlcbiAgaWYgKCFzZWNyZXRIYXNFeHBlY3RlZEZvcm1hdChjcmVkZW50aWFscykpIHtcbiAgICBjb25zb2xlLmVycm9yKFxuICAgICAgYEJhc2ljIGF1dGggY3JlZGVudGlhbHMgc2VjcmV0IGRpZCBub3QgZm9sbG93IGV4cGVjdGVkIGZvcm1hdCAoc2VjcmV0IG5hbWU6ICcke3NlY3JldE5hbWV9JylgLFxuICAgIClcbiAgICB0aHJvdyBuZXcgRXJyb3IoKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICBcIkJhc2ljIFwiICtcbiAgICBCdWZmZXIuZnJvbShgJHtjcmVkZW50aWFscy51c2VybmFtZX06JHtjcmVkZW50aWFscy5wYXNzd29yZH1gKS50b1N0cmluZyhcbiAgICAgIFwiYmFzZTY0XCIsXG4gICAgKVxuICApXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldFNlY3JldFZhbHVlKHNlY3JldE5hbWU6IHN0cmluZyk6IFByb21pc2U8dW5rbm93bj4ge1xuICBjb25zdCBjbGllbnQgPSBkZXBlbmRlbmNpZXMuY3JlYXRlU2VjcmV0c01hbmFnZXIoKVxuICBjb25zdCBzZWNyZXQgPSBhd2FpdCBjbGllbnQuZ2V0U2VjcmV0VmFsdWUoeyBTZWNyZXRJZDogc2VjcmV0TmFtZSB9KVxuXG4gIGlmICghc2VjcmV0LlNlY3JldFN0cmluZykge1xuICAgIGNvbnNvbGUuZXJyb3IoYFNlY3JldCB2YWx1ZSBub3QgZm91bmQgZm9yICcke3NlY3JldE5hbWV9J2ApXG4gICAgdGhyb3cgbmV3IEVycm9yKClcbiAgfVxuXG4gIHJldHVybiBKU09OLnBhcnNlKHNlY3JldC5TZWNyZXRTdHJpbmcpXG59XG5cbmZ1bmN0aW9uIHNlY3JldEhhc0V4cGVjdGVkRm9ybWF0KFxuICB2YWx1ZTogdW5rbm93bixcbik6IHZhbHVlIGlzIHsgdXNlcm5hbWU6IHN0cmluZzsgcGFzc3dvcmQ6IHN0cmluZyB9IHtcbiAgcmV0dXJuIChcbiAgICB0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIgJiZcbiAgICB2YWx1ZSAhPT0gbnVsbCAmJlxuICAgIFwidXNlcm5hbWVcIiBpbiB2YWx1ZSAmJlxuICAgIHR5cGVvZiB2YWx1ZS51c2VybmFtZSA9PT0gXCJzdHJpbmdcIiAmJlxuICAgIFwicGFzc3dvcmRcIiBpbiB2YWx1ZSAmJlxuICAgIHR5cGVvZiB2YWx1ZS5wYXNzd29yZCA9PT0gXCJzdHJpbmdcIlxuICApXG59XG4iXX0=
@@ -0,0 +1,47 @@
1
+ /**
2
+ * This lambda verifies credentials:
3
+ * - Against Cognito user pool if request uses Bearer token
4
+ * - Against credentials saved in Secret Manager if request uses basic auth (and if secret exists)
5
+ *
6
+ * Expects the following environment variables
7
+ * - USER_POOL_ID
8
+ * - BASIC_AUTH_CREDENTIALS_SECRET_NAME (optional)
9
+ * - Secret value should follow this format: `{"username":"<username>","password":"<password>"}`
10
+ * - REQUIRED_SCOPE (optional)
11
+ * - Set this to require that the bearer token payload contains the given scope
12
+ */
13
+ import type { APIGatewayRequestAuthorizerEventV2, APIGatewaySimpleAuthorizerResult } from "aws-lambda";
14
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
15
+ import type { CognitoAccessTokenPayload } from "aws-jwt-verify/jwt-model";
16
+ type AuthorizerResult = APIGatewaySimpleAuthorizerResult & {
17
+ /**
18
+ * Returning a context object from our authorizer allows our API Gateway to access these variables
19
+ * via `${context.authorizer.<property>}`.
20
+ * https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging-variables.html
21
+ */
22
+ context?: {
23
+ /**
24
+ * If the token is verified, we return the auth client ID from the token's claims as a context
25
+ * variable (named `authorizer.clientId`). You can then use this for parameter mapping on the
26
+ * API Gateway (see `AlbIntegrationProps.mapParameters` on the `ApiGateway` construct), if for
27
+ * example you want to forward this to the backend integration.
28
+ */
29
+ clientId: string;
30
+ /**
31
+ * See `CognitoUserPoolAuthorizerProps.basicAuthForInternalAuthorization` in the `ApiGateway`
32
+ * construct (we provide the same context variable here as in the Cognito User Pool authorizer,
33
+ * using the credentials from BASIC_AUTH_CREDENTIALS_SECRET_NAME).
34
+ */
35
+ internalAuthorizationHeader?: string;
36
+ };
37
+ };
38
+ export declare const handler: (event: APIGatewayRequestAuthorizerEventV2) => Promise<AuthorizerResult>;
39
+ export type TokenVerifier = {
40
+ verify: (accessToken: string) => Promise<CognitoAccessTokenPayload>;
41
+ };
42
+ /** For overriding dependency creation in tests. */
43
+ export declare const dependencies: {
44
+ createTokenVerifier: () => TokenVerifier;
45
+ createSecretsManager: () => SecretsManager;
46
+ };
47
+ export {};
@@ -0,0 +1,136 @@
1
+ /**
2
+ * This lambda verifies credentials:
3
+ * - Against Cognito user pool if request uses Bearer token
4
+ * - Against credentials saved in Secret Manager if request uses basic auth (and if secret exists)
5
+ *
6
+ * Expects the following environment variables
7
+ * - USER_POOL_ID
8
+ * - BASIC_AUTH_CREDENTIALS_SECRET_NAME (optional)
9
+ * - Secret value should follow this format: `{"username":"<username>","password":"<password>"}`
10
+ * - REQUIRED_SCOPE (optional)
11
+ * - Set this to require that the bearer token payload contains the given scope
12
+ */
13
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
14
+ import { CognitoJwtVerifier } from "aws-jwt-verify";
15
+ export const handler = async (event) => {
16
+ const authHeader = event.headers?.authorization;
17
+ if (!authHeader) {
18
+ return { isAuthorized: false };
19
+ }
20
+ const expectedBasicAuthHeader = await getExpectedBasicAuthHeader();
21
+ if (authHeader.startsWith("Bearer ")) {
22
+ const result = await verifyAccessToken(authHeader.substring(7)); // substring(7) == after 'Bearer '
23
+ switch (result) {
24
+ case "INVALID":
25
+ return { isAuthorized: false };
26
+ case "EXPIRED":
27
+ // We want to return 401 Unauthorized for expired tokens, so the client knows to refresh
28
+ // their token when receiving this status code. API Gateway authorizer lambdas return
29
+ // 403 Forbidden for {isAuthorized: false}, but there is a way to return 401: throwing an
30
+ // error with this exact string. https://stackoverflow.com/a/71965890
31
+ throw new Error("Unauthorized");
32
+ default:
33
+ return {
34
+ isAuthorized: true,
35
+ context: {
36
+ clientId: result.client_id,
37
+ internalAuthorizationHeader: expectedBasicAuthHeader,
38
+ },
39
+ };
40
+ }
41
+ }
42
+ else if (authHeader.startsWith("Basic ")) {
43
+ return { isAuthorized: authHeader === expectedBasicAuthHeader };
44
+ }
45
+ else {
46
+ return { isAuthorized: false };
47
+ }
48
+ };
49
+ /** Decodes and verifies the given token against Cognito. */
50
+ async function verifyAccessToken(token) {
51
+ try {
52
+ const tokenVerifier = getTokenVerifier();
53
+ // Must await here instead of returning the promise directly, so that errors can be caught in
54
+ // this function
55
+ return await tokenVerifier.verify(token);
56
+ }
57
+ catch (e) {
58
+ // If the JWT has expired, aws-jwt-verify throws this error:
59
+ // https://github.com/awslabs/aws-jwt-verify/blob/8d8f714d7281913ecd660147f5c30311479601c1/src/jwt.ts#L197
60
+ // We can't check instanceof on that error class, since it's not exported, so this is the next
61
+ // best thing.
62
+ if (e instanceof Error && e.message?.includes("Token expired")) {
63
+ return "EXPIRED";
64
+ }
65
+ else {
66
+ return "INVALID";
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * We cache the verifier in this global variable, so that subsequent invocations of a hot lambda
72
+ * will re-use this.
73
+ */
74
+ let cachedTokenVerifier = undefined;
75
+ function getTokenVerifier() {
76
+ if (cachedTokenVerifier === undefined) {
77
+ cachedTokenVerifier = dependencies.createTokenVerifier();
78
+ }
79
+ return cachedTokenVerifier;
80
+ }
81
+ /** For overriding dependency creation in tests. */
82
+ export const dependencies = {
83
+ createTokenVerifier: () => {
84
+ const userPoolId = process.env["USER_POOL_ID"];
85
+ if (!userPoolId) {
86
+ console.error("USER_POOL_ID env variable is not defined");
87
+ throw new Error();
88
+ }
89
+ return CognitoJwtVerifier.create({
90
+ userPoolId,
91
+ tokenUse: "access",
92
+ clientId: null,
93
+ scope: process.env.REQUIRED_SCOPE || undefined, // `|| undefined` to discard empty string
94
+ });
95
+ },
96
+ createSecretsManager: () => new SecretsManager(),
97
+ };
98
+ /** Cache this value, so that subsequent lambda invocations don't have to refetch. */
99
+ let cachedBasicAuthHeader = undefined;
100
+ async function getExpectedBasicAuthHeader() {
101
+ if (cachedBasicAuthHeader === undefined) {
102
+ const secretName = process.env["BASIC_AUTH_CREDENTIALS_SECRET_NAME"];
103
+ if (!secretName) {
104
+ return undefined;
105
+ }
106
+ cachedBasicAuthHeader = await getSecretAsBasicAuthHeader(secretName);
107
+ }
108
+ return cachedBasicAuthHeader;
109
+ }
110
+ async function getSecretAsBasicAuthHeader(secretName) {
111
+ const credentials = await getSecretValue(secretName);
112
+ if (!secretHasExpectedFormat(credentials)) {
113
+ console.error(`Basic auth credentials secret did not follow expected format (secret name: '${secretName}')`);
114
+ throw new Error();
115
+ }
116
+ return ("Basic " +
117
+ Buffer.from(`${credentials.username}:${credentials.password}`).toString("base64"));
118
+ }
119
+ async function getSecretValue(secretName) {
120
+ const client = dependencies.createSecretsManager();
121
+ const secret = await client.getSecretValue({ SecretId: secretName });
122
+ if (!secret.SecretString) {
123
+ console.error(`Secret value not found for '${secretName}'`);
124
+ throw new Error();
125
+ }
126
+ return JSON.parse(secret.SecretString);
127
+ }
128
+ function secretHasExpectedFormat(value) {
129
+ return (typeof value === "object" &&
130
+ value !== null &&
131
+ "username" in value &&
132
+ typeof value.username === "string" &&
133
+ "password" in value &&
134
+ typeof value.password === "string");
135
+ }
136
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29nbml0by11c2VyLXBvb2wtb3ItYmFzaWMtYXV0aC1hdXRob3JpemVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS1nYXRld2F5L2F1dGhvcml6ZXItbGFtYmRhcy9jb2duaXRvLXVzZXItcG9vbC1vci1iYXNpYy1hdXRoLWF1dGhvcml6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7O0dBV0c7QUFNSCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUNBQWlDLENBQUE7QUFDaEUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUEwQm5ELE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxLQUFLLEVBQzFCLEtBQXlDLEVBQ2QsRUFBRTtJQUM3QixNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQTtJQUMvQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDaEIsT0FBTyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsQ0FBQTtJQUNoQyxDQUFDO0lBRUQsTUFBTSx1QkFBdUIsR0FBRyxNQUFNLDBCQUEwQixFQUFFLENBQUE7SUFFbEUsSUFBSSxVQUFVLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDckMsTUFBTSxNQUFNLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUEsQ0FBQyxrQ0FBa0M7UUFDbEcsUUFBUSxNQUFNLEVBQUUsQ0FBQztZQUNmLEtBQUssU0FBUztnQkFDWixPQUFPLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxDQUFBO1lBQ2hDLEtBQUssU0FBUztnQkFDWix3RkFBd0Y7Z0JBQ3hGLHFGQUFxRjtnQkFDckYseUZBQXlGO2dCQUN6RixxRUFBcUU7Z0JBQ3JFLE1BQU0sSUFBSSxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUE7WUFDakM7Z0JB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b3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRoaXMgbGFtYmRhIHZlcmlmaWVzIGNyZWRlbnRpYWxzOlxuICogLSBBZ2FpbnN0IENvZ25pdG8gdXNlciBwb29sIGlmIHJlcXVlc3QgdXNlcyBCZWFyZXIgdG9rZW5cbiAqIC0gQWdhaW5zdCBjcmVkZW50aWFscyBzYXZlZCBpbiBTZWNyZXQgTWFuYWdlciBpZiByZXF1ZXN0IHVzZXMgYmFzaWMgYXV0aCAoYW5kIGlmIHNlY3JldCBleGlzdHMpXG4gKlxuICogRXhwZWN0cyB0aGUgZm9sbG93aW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICogLSBVU0VSX1BPT0xfSURcbiAqIC0gQkFTSUNfQVVUSF9DUkVERU5USUFMU19TRUNSRVRfTkFNRSAob3B0aW9uYWwpXG4gKiAgIC0gU2VjcmV0IHZhbHVlIHNob3VsZCBmb2xsb3cgdGhpcyBmb3JtYXQ6IGB7XCJ1c2VybmFtZVwiOlwiPHVzZXJuYW1lPlwiLFwicGFzc3dvcmRcIjpcIjxwYXNzd29yZD5cIn1gXG4gKiAtIFJFUVVJUkVEX1NDT1BFIChvcHRpb25hbClcbiAqICAgLSBTZXQgdGhpcyB0byByZXF1aXJlIHRoYXQgdGhlIGJlYXJlciB0b2tlbiBwYXlsb2FkIGNvbnRhaW5zIHRoZSBnaXZlbiBzY29wZVxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgQVBJR2F0ZXdheVJlcXVlc3RBdXRob3JpemVyRXZlbnRWMixcbiAgQVBJR2F0ZXdheVNpbXBsZUF1dGhvcml6ZXJSZXN1bHQsXG59IGZyb20gXCJhd3MtbGFtYmRhXCJcbmltcG9ydCB7IFNlY3JldHNNYW5hZ2VyIH0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXJcIlxuaW1wb3J0IHsgQ29nbml0b0p3dFZlcmlmaWVyIH0gZnJvbSBcImF3cy1qd3QtdmVyaWZ5XCJcbmltcG9ydCB0eXBlIHsgQ29nbml0b0FjY2Vzc1Rva2VuUGF5bG9hZCB9IGZyb20gXCJhd3Mtand0LXZlcmlmeS9qd3QtbW9kZWxcIlxuXG50eXBlIEF1dGhvcml6ZXJSZXN1bHQgPSBBUElHYXRld2F5U2ltcGxlQXV0aG9yaXplclJlc3VsdCAmIHtcbiAgLyoqXG4gICAqIFJldHVybmluZyBhIGNvbnRleHQgb2JqZWN0IGZyb20gb3VyIGF1dGhvcml6ZXIgYWxsb3dzIG91ciBBUEkgR2F0ZXdheSB0byBhY2Nlc3MgdGhlc2UgdmFyaWFibGVzXG4gICAqIHZpYSBgJHtjb250ZXh0LmF1dGhvcml6ZXIuPHByb3BlcnR5Pn1gLlxuICAgKiBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXBpZ2F0ZXdheS9sYXRlc3QvZGV2ZWxvcGVyZ3VpZGUvaHR0cC1hcGktbG9nZ2luZy12YXJpYWJsZXMuaHRtbFxuICAgKi9cbiAgY29udGV4dD86IHtcbiAgICAvKipcbiAgICAgKiBJZiB0aGUgdG9rZW4gaXMgdmVyaWZpZWQsIHdlIHJldHVybiB0aGUgYXV0aCBjbGllbnQgSUQgZnJvbSB0aGUgdG9rZW4ncyBjbGFpbXMgYXMgYSBjb250ZXh0XG4gICAgICogdmFyaWFibGUgKG5hbWVkIGBhdXRob3JpemVyLmNsaWVudElkYCkuIFlvdSBjYW4gdGhlbiB1c2UgdGhpcyBmb3IgcGFyYW1ldGVyIG1hcHBpbmcgb24gdGhlXG4gICAgICogQVBJIEdhdGV3YXkgKHNlZSBgQWxiSW50ZWdyYXRpb25Qcm9wcy5tYXBQYXJhbWV0ZXJzYCBvbiB0aGUgYEFwaUdhdGV3YXlgIGNvbnN0cnVjdCksIGlmIGZvclxuICAgICAqIGV4YW1wbGUgeW91IHdhbnQgdG8gZm9yd2FyZCB0aGlzIHRvIHRoZSBiYWNrZW5kIGludGVncmF0aW9uLlxuICAgICAqL1xuICAgIGNsaWVudElkOiBzdHJpbmdcbiAgICAvKipcbiAgICAgKiBTZWUgYENvZ25pdG9Vc2VyUG9vbEF1dGhvcml6ZXJQcm9wcy5iYXNpY0F1dGhGb3JJbnRlcm5hbEF1dGhvcml6YXRpb25gIGluIHRoZSBgQXBpR2F0ZXdheWBcbiAgICAgKiBjb25zdHJ1Y3QgKHdlIHByb3ZpZGUgdGhlIHNhbWUgY29udGV4dCB2YXJpYWJsZSBoZXJlIGFzIGluIHRoZSBDb2duaXRvIFVzZXIgUG9vbCBhdXRob3JpemVyLFxuICAgICAqIHVzaW5nIHRoZSBjcmVkZW50aWFscyBmcm9tIEJBU0lDX0FVVEhfQ1JFREVOVElBTFNfU0VDUkVUX05BTUUpLlxuICAgICAqL1xuICAgIGludGVybmFsQXV0aG9yaXphdGlvbkhlYWRlcj86IHN0cmluZ1xuICB9XG59XG5cbmV4cG9ydCBjb25zdCBoYW5kbGVyID0gYXN5bmMgKFxuICBldmVudDogQVBJR2F0ZXdheVJlcXVlc3RBdXRob3JpemVyRXZlbnRWMixcbik6IFByb21pc2U8QXV0aG9yaXplclJlc3VsdD4gPT4ge1xuICBjb25zdCBhdXRoSGVhZGVyID0gZXZlbnQuaGVhZGVycz8uYXV0aG9yaXphdGlvblxuICBpZiAoIWF1dGhIZWFkZXIpIHtcbiAgICByZXR1cm4geyBpc0F1dGhvcml6ZWQ6IGZhbHNlIH1cbiAgfVxuXG4gIGNvbnN0IGV4cGVjdGVkQmFzaWNBdXRoSGVhZGVyID0gYXdhaXQgZ2V0RXhwZWN0ZWRCYXNpY0F1dGhIZWFkZXIoKVxuXG4gIGlmIChhdXRoSGVhZGVyLnN0YXJ0c1dpdGgoXCJCZWFyZXIgXCIpKSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdmVyaWZ5QWNjZXNzVG9rZW4oYXV0aEhlYWRlci5zdWJzdHJpbmcoNykpIC8vIHN1YnN0cmluZyg3KSA9PSBhZnRlciAnQmVhcmVyICdcbiAgICBzd2l0Y2ggKHJlc3VsdCkge1xuICAgICAgY2FzZSBcIklOVkFMSURcIjpcbiAgICAgICAgcmV0dXJuIHsgaXNBdXRob3JpemVkOiBmYWxzZSB9XG4gICAgICBjYXNlIFwiRVhQSVJFRFwiOlxuICAgICAgICAvLyBXZSB3YW50IHRvIHJldHVybiA0MDEgVW5hdXRob3JpemVkIGZvciBleHBpcmVkIHRva2Vucywgc28gdGhlIGNsaWVudCBrbm93cyB0byByZWZyZXNoXG4gICAgICAgIC8vIHRoZWlyIHRva2VuIHdoZW4gcmVjZWl2aW5nIHRoaXMgc3RhdHVzIGNvZGUuIEFQSSBHYXRld2F5IGF1dGhvcml6ZXIgbGFtYmRhcyByZXR1cm5cbiAgICAgICAgLy8gNDAzIEZvcmJpZGRlbiBmb3Ige2lzQXV0aG9yaXplZDogZmFsc2V9LCBidXQgdGhlcmUgaXMgYSB3YXkgdG8gcmV0dXJuIDQwMTogdGhyb3dpbmcgYW5cbiAgICAgICAgLy8gZXJyb3Igd2l0aCB0aGlzIGV4YWN0IHN0cmluZy4gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzcxOTY1ODkwXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIlVuYXV0aG9yaXplZFwiKVxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBpc0F1dGhvcml6ZWQ6IHRydWUsXG4gICAgICAgICAgY29udGV4dDoge1xuICAgICAgICAgICAgY2xpZW50SWQ6IHJlc3VsdC5jbGllbnRfaWQsXG4gICAgICAgICAgICBpbnRlcm5hbEF1dGhvcml6YXRpb25IZWFkZXI6IGV4cGVjdGVkQmFzaWNBdXRoSGVhZGVyLFxuICAgICAgICAgIH0sXG4gICAgICAgIH1cbiAgICB9XG4gIH0gZWxzZSBpZiAoYXV0aEhlYWRlci5zdGFydHNXaXRoKFwiQmFzaWMgXCIpKSB7XG4gICAgcmV0dXJuIHsgaXNBdXRob3JpemVkOiBhdXRoSGVhZGVyID09PSBleHBlY3RlZEJhc2ljQXV0aEhlYWRlciB9XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHsgaXNBdXRob3JpemVkOiBmYWxzZSB9XG4gIH1cbn1cblxuLyoqIERlY29kZXMgYW5kIHZlcmlmaWVzIHRoZSBnaXZlbiB0b2tlbiBhZ2FpbnN0IENvZ25pdG8uICovXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlBY2Nlc3NUb2tlbihcbiAgdG9rZW46IHN0cmluZyxcbik6IFByb21pc2U8Q29nbml0b0FjY2Vzc1Rva2VuUGF5bG9hZCB8IFwiRVhQSVJFRFwiIHwgXCJJTlZBTElEXCI+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCB0b2tlblZlcmlmaWVyID0gZ2V0VG9rZW5WZXJpZmllcigpXG4gICAgLy8gTXVzdCBhd2FpdCBoZXJlIGluc3RlYWQgb2YgcmV0dXJuaW5nIHRoZSBwcm9taXNlIGRpcmVjdGx5LCBzbyB0aGF0IGVycm9ycyBjYW4gYmUgY2F1Z2h0IGluXG4gICAgLy8gdGhpcyBmdW5jdGlvblxuICAgIHJldHVybiBhd2FpdCB0b2tlblZlcmlmaWVyLnZlcmlmeSh0b2tlbilcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIElmIHRoZSBKV1QgaGFzIGV4cGlyZWQsIGF3cy1qd3QtdmVyaWZ5IHRocm93cyB0aGlzIGVycm9yOlxuICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9hd3NsYWJzL2F3cy1qd3QtdmVyaWZ5L2Jsb2IvOGQ4ZjcxNGQ3MjgxOTEzZWNkNjYwMTQ3ZjVjMzAzMTE0Nzk2MDFjMS9zcmMvand0LnRzI0wxOTdcbiAgICAvLyBXZSBjYW4ndCBjaGVjayBpbnN0YW5jZW9mIG9uIHRoYXQgZXJyb3IgY2xhc3MsIHNpbmNlIGl0J3Mgbm90IGV4cG9ydGVkLCBzbyB0aGlzIGlzIHRoZSBuZXh0XG4gICAgLy8gYmVzdCB0aGluZy5cbiAgICBpZiAoZSBpbnN0YW5jZW9mIEVycm9yICYmIGUubWVzc2FnZT8uaW5jbHVkZXMoXCJUb2tlbiBleHBpcmVkXCIpKSB7XG4gICAgICByZXR1cm4gXCJFWFBJUkVEXCJcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIFwiSU5WQUxJRFwiXG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCB0eXBlIFRva2VuVmVyaWZpZXIgPSB7XG4gIHZlcmlmeTogKGFjY2Vzc1Rva2VuOiBzdHJpbmcpID0+IFByb21pc2U8Q29nbml0b0FjY2Vzc1Rva2VuUGF5bG9hZD5cbn1cblxuLyoqXG4gKiBXZSBjYWNoZSB0aGUgdmVyaWZpZXIgaW4gdGhpcyBnbG9iYWwgdmFyaWFibGUsIHNvIHRoYXQgc3Vic2VxdWVudCBpbnZvY2F0aW9ucyBvZiBhIGhvdCBsYW1iZGFcbiAqIHdpbGwgcmUtdXNlIHRoaXMuXG4gKi9cbmxldCBjYWNoZWRUb2tlblZlcmlmaWVyOiBUb2tlblZlcmlmaWVyIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkXG5cbmZ1bmN0aW9uIGdldFRva2VuVmVyaWZpZXIoKTogVG9rZW5WZXJpZmllciB7XG4gIGlmIChjYWNoZWRUb2tlblZlcmlmaWVyID09PSB1bmRlZmluZWQpIHtcbiAgICBjYWNoZWRUb2tlblZlcmlmaWVyID0gZGVwZW5kZW5jaWVzLmNyZWF0ZVRva2VuVmVyaWZpZXIoKVxuICB9XG4gIHJldHVybiBjYWNoZWRUb2tlblZlcmlmaWVyXG59XG5cbi8qKiBGb3Igb3ZlcnJpZGluZyBkZXBlbmRlbmN5IGNyZWF0aW9uIGluIHRlc3RzLiAqL1xuZXhwb3J0IGNvbnN0IGRlcGVuZGVuY2llcyA9IHtcbiAgY3JlYXRlVG9rZW5WZXJpZmllcjogKCk6IFRva2VuVmVyaWZpZXIgPT4ge1xuICAgIGNvbnN0IHVzZXJQb29sSWQgPSBwcm9jZXNzLmVudltcIlVTRVJfUE9PTF9JRFwiXVxuICAgIGlmICghdXNlclBvb2xJZCkge1xuICAgICAgY29uc29sZS5lcnJvcihcIlVTRVJfUE9PTF9JRCBlbnYgdmFyaWFibGUgaXMgbm90IGRlZmluZWRcIilcbiAgICAgIHRocm93IG5ldyBFcnJvcigpXG4gICAgfVxuXG4gICAgcmV0dXJuIENvZ25pdG9Kd3RWZXJpZmllci5jcmVhdGUoe1xuICAgICAgdXNlclBvb2xJZCxcbiAgICAgIHRva2VuVXNlOiBcImFjY2Vzc1wiLFxuICAgICAgY2xpZW50SWQ6IG51bGwsXG4gICAgICBzY29wZTogcHJvY2Vzcy5lbnYuUkVRVUlSRURfU0NPUEUgfHwgdW5kZWZpbmVkLCAvLyBgfHwgdW5kZWZpbmVkYCB0byBkaXNjYXJkIGVtcHR5IHN0cmluZ1xuICAgIH0pXG4gIH0sXG4gIGNyZWF0ZVNlY3JldHNNYW5hZ2VyOiAoKSA9PiBuZXcgU2VjcmV0c01hbmFnZXIoKSxcbn1cblxuLyoqIENhY2hlIHRoaXMgdmFsdWUsIHNvIHRoYXQgc3Vic2VxdWVudCBsYW1iZGEgaW52b2NhdGlvbnMgZG9uJ3QgaGF2ZSB0byByZWZldGNoLiAqL1xubGV0IGNhY2hlZEJhc2ljQXV0aEhlYWRlcjogc3RyaW5nIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkXG5cbmFzeW5jIGZ1bmN0aW9uIGdldEV4cGVjdGVkQmFzaWNBdXRoSGVhZGVyKCk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIGlmIChjYWNoZWRCYXNpY0F1dGhIZWFkZXIgPT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IHNlY3JldE5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZCA9XG4gICAgICBwcm9jZXNzLmVudltcIkJBU0lDX0FVVEhfQ1JFREVOVElBTFNfU0VDUkVUX05BTUVcIl1cbiAgICBpZiAoIXNlY3JldE5hbWUpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgICB9XG5cbiAgICBjYWNoZWRCYXNpY0F1dGhIZWFkZXIgPSBhd2FpdCBnZXRTZWNyZXRBc0Jhc2ljQXV0aEhlYWRlcihzZWNyZXROYW1lKVxuICB9XG5cbiAgcmV0dXJuIGNhY2hlZEJhc2ljQXV0aEhlYWRlclxufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRTZWNyZXRBc0Jhc2ljQXV0aEhlYWRlcihzZWNyZXROYW1lOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBjcmVkZW50aWFscyA9IGF3YWl0IGdldFNlY3JldFZhbHVlKHNlY3JldE5hbWUpXG4gIGlmICghc2VjcmV0SGFzRXhwZWN0ZWRGb3JtYXQoY3JlZGVudGlhbHMpKSB7XG4gICAgY29uc29sZS5lcnJvcihcbiAgICAgIGBCYXNpYyBhdXRoIGNyZWRlbnRpYWxzIHNlY3JldCBkaWQgbm90IGZvbGxvdyBleHBlY3RlZCBmb3JtYXQgKHNlY3JldCBuYW1lOiAnJHtzZWNyZXROYW1lfScpYCxcbiAgICApXG4gICAgdGhyb3cgbmV3IEVycm9yKClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgXCJCYXNpYyBcIiArXG4gICAgQnVmZmVyLmZyb20oYCR7Y3JlZGVudGlhbHMudXNlcm5hbWV9OiR7Y3JlZGVudGlhbHMucGFzc3dvcmR9YCkudG9TdHJpbmcoXG4gICAgICBcImJhc2U2NFwiLFxuICAgIClcbiAgKVxufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRTZWNyZXRWYWx1ZShzZWNyZXROYW1lOiBzdHJpbmcpOiBQcm9taXNlPHVua25vd24+IHtcbiAgY29uc3QgY2xpZW50ID0gZGVwZW5kZW5jaWVzLmNyZWF0ZVNlY3JldHNNYW5hZ2VyKClcbiAgY29uc3Qgc2VjcmV0ID0gYXdhaXQgY2xpZW50LmdldFNlY3JldFZhbHVlKHsgU2VjcmV0SWQ6IHNlY3JldE5hbWUgfSlcblxuICBpZiAoIXNlY3JldC5TZWNyZXRTdHJpbmcpIHtcbiAgICBjb25zb2xlLmVycm9yKGBTZWNyZXQgdmFsdWUgbm90IGZvdW5kIGZvciAnJHtzZWNyZXROYW1lfSdgKVxuICAgIHRocm93IG5ldyBFcnJvcigpXG4gIH1cblxuICByZXR1cm4gSlNPTi5wYXJzZShzZWNyZXQuU2VjcmV0U3RyaW5nKVxufVxuXG5mdW5jdGlvbiBzZWNyZXRIYXNFeHBlY3RlZEZvcm1hdChcbiAgdmFsdWU6IHVua25vd24sXG4pOiB2YWx1ZSBpcyB7IHVzZXJuYW1lOiBzdHJpbmc7IHBhc3N3b3JkOiBzdHJpbmcgfSB7XG4gIHJldHVybiAoXG4gICAgdHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiICYmXG4gICAgdmFsdWUgIT09IG51bGwgJiZcbiAgICBcInVzZXJuYW1lXCIgaW4gdmFsdWUgJiZcbiAgICB0eXBlb2YgdmFsdWUudXNlcm5hbWUgPT09IFwic3RyaW5nXCIgJiZcbiAgICBcInBhc3N3b3JkXCIgaW4gdmFsdWUgJiZcbiAgICB0eXBlb2YgdmFsdWUucGFzc3dvcmQgPT09IFwic3RyaW5nXCJcbiAgKVxufVxuIl19