@infoxchange/make-it-so 2.11.0-internal-testing-vdt-199-add-auth-token-verify-function.4 → 2.11.0-internal-testing-vdt-199-add-auth-token-verify-function-2.2
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.
- package/README.md +15 -29
- package/dist/cdk-constructs/IxNextjsSite.d.ts.map +1 -1
- package/dist/cdk-constructs/IxNextjsSite.js +2 -1
- package/dist/cdk-constructs/IxStaticSite.d.ts.map +1 -1
- package/dist/cdk-constructs/IxStaticSite.js +2 -1
- package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.d.ts +2 -0
- package/dist/cdk-constructs/SiteOidcAuth/auth-check-handler-body.d.ts.map +1 -0
- package/dist/cdk-constructs/{CloudFrontOidcAuth/auth-check.js → SiteOidcAuth/auth-check-handler-body.js} +39 -57
- package/dist/cdk-constructs/SiteOidcAuth/auth-route.d.ts.map +1 -0
- package/dist/cdk-constructs/SiteOidcAuth/index.d.ts +197 -0
- package/dist/cdk-constructs/SiteOidcAuth/index.d.ts.map +1 -0
- package/dist/cdk-constructs/SiteOidcAuth/index.js +184 -0
- package/dist/cdk-constructs/index.d.ts +1 -1
- package/dist/cdk-constructs/index.d.ts.map +1 -1
- package/dist/cdk-constructs/index.js +1 -1
- package/dist/lib/site/support.d.ts +15 -5
- package/dist/lib/site/support.d.ts.map +1 -1
- package/dist/lib/site/support.js +19 -0
- package/package.json +1 -1
- package/src/cdk-constructs/IxNextjsSite.ts +2 -0
- package/src/cdk-constructs/IxStaticSite.ts +2 -0
- package/src/cdk-constructs/{CloudFrontOidcAuth/auth-check.ts → SiteOidcAuth/auth-check-handler-body.ts} +44 -63
- package/src/cdk-constructs/SiteOidcAuth/index.ts +288 -0
- package/src/cdk-constructs/index.ts +1 -1
- package/src/lib/site/support.ts +80 -29
- package/dist/cdk-constructs/CloudFrontOidcAuth/auth-check.d.ts +0 -2
- package/dist/cdk-constructs/CloudFrontOidcAuth/auth-check.d.ts.map +0 -1
- package/dist/cdk-constructs/CloudFrontOidcAuth/auth-route.d.ts.map +0 -1
- package/dist/cdk-constructs/CloudFrontOidcAuth/index.d.ts +0 -27
- package/dist/cdk-constructs/CloudFrontOidcAuth/index.d.ts.map +0 -1
- package/dist/cdk-constructs/CloudFrontOidcAuth/index.js +0 -198
- package/src/cdk-constructs/CloudFrontOidcAuth/cloudfront.d.ts +0 -245
- package/src/cdk-constructs/CloudFrontOidcAuth/index.ts +0 -294
- /package/dist/cdk-constructs/{CloudFrontOidcAuth → SiteOidcAuth}/auth-route.d.ts +0 -0
- /package/dist/cdk-constructs/{CloudFrontOidcAuth → SiteOidcAuth}/auth-route.js +0 -0
- /package/src/cdk-constructs/{CloudFrontOidcAuth → SiteOidcAuth}/auth-route.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdk-constructs/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,4BAA4B,CAAC;AAC3C,cAAc
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdk-constructs/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC"}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import { NextjsSite, NextjsSiteProps, Stack, StaticSite, StaticSiteProps } from "sst/constructs";
|
|
3
3
|
import { type DistributionDomainProps } from "sst/constructs/Distribution.js";
|
|
4
|
+
import { type AddToSiteProps as SiteOidcAuthAddToSiteProps } from "../../cdk-constructs/SiteOidcAuth/index.js";
|
|
5
|
+
type SharedExtendedSiteProps = {
|
|
6
|
+
customDomain?: string | ExtendedCustomDomains;
|
|
7
|
+
auth?: {
|
|
8
|
+
oidc: {
|
|
9
|
+
issuerUrl: string;
|
|
10
|
+
clientId: string;
|
|
11
|
+
scope: string;
|
|
12
|
+
};
|
|
13
|
+
} & SiteOidcAuthAddToSiteProps;
|
|
14
|
+
};
|
|
4
15
|
export type ExtendedCustomDomains = DistributionDomainProps & {
|
|
5
16
|
isIxManagedDomain?: boolean;
|
|
6
17
|
additionalDomainAliases?: string[];
|
|
7
18
|
};
|
|
8
|
-
export type ExtendedNextjsSiteProps = Omit<NextjsSiteProps, "customDomain" | "environment"> & {
|
|
9
|
-
customDomain?: string | ExtendedCustomDomains;
|
|
19
|
+
export type ExtendedNextjsSiteProps = Omit<NextjsSiteProps, "customDomain" | "environment"> & SharedExtendedSiteProps & {
|
|
10
20
|
/**
|
|
11
21
|
* An object with the key being the environment variable name. The value can either be the environment variable value
|
|
12
22
|
* as a string or as an object with `buildtime` and/or `runtime` properties where the values of `buildtime` and
|
|
@@ -31,9 +41,7 @@ export type ExtendedNextjsSiteProps = Omit<NextjsSiteProps, "customDomain" | "en
|
|
|
31
41
|
runtime?: string;
|
|
32
42
|
}>;
|
|
33
43
|
};
|
|
34
|
-
export type ExtendedStaticSiteProps = Omit<StaticSiteProps, "customDomain"> &
|
|
35
|
-
customDomain?: string | ExtendedCustomDomains;
|
|
36
|
-
};
|
|
44
|
+
export type ExtendedStaticSiteProps = Omit<StaticSiteProps, "customDomain"> & SharedExtendedSiteProps;
|
|
37
45
|
export declare function setupCustomDomain<Props extends ExtendedStaticSiteProps | ExtendedNextjsSiteProps>(scope: Construct, id: string, props: Readonly<Props>): Props;
|
|
38
46
|
export declare function setupCertificate<Props extends ExtendedStaticSiteProps | ExtendedNextjsSiteProps>(scope: Construct, id: string, props: Readonly<Props>): Props;
|
|
39
47
|
export declare function setupDomainAliasRedirect<Props extends ExtendedStaticSiteProps | ExtendedNextjsSiteProps>(scope: Construct, id: string, props: Readonly<Props>): Props;
|
|
@@ -58,4 +66,6 @@ export declare function getPrimaryOrigin<Instance extends NextjsSite | StaticSit
|
|
|
58
66
|
export declare function getPrimaryCustomDomain<Props extends ExtendedStaticSiteProps | ExtendedNextjsSiteProps>(props: Readonly<Props>): string | null;
|
|
59
67
|
export declare function getAliasDomain<Props extends ExtendedStaticSiteProps | ExtendedNextjsSiteProps>(props: Readonly<Props>): string | null;
|
|
60
68
|
export declare function getAlternativeDomains<Props extends ExtendedStaticSiteProps | ExtendedNextjsSiteProps>(props: Readonly<Props>): string[];
|
|
69
|
+
export declare function processAuthProps<SiteType extends "StaticSite" | "SsrSite", Props extends SiteType extends "StaticSite" ? ExtendedStaticSiteProps : ExtendedNextjsSiteProps>(scope: Construct, id: string, siteType: SiteType, props: Readonly<Props>): Props;
|
|
70
|
+
export {};
|
|
61
71
|
//# sourceMappingURL=support.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"support.d.ts","sourceRoot":"","sources":["../../../src/lib/site/support.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EACL,UAAU,EACV,eAAe,EACf,KAAK,EACL,UAAU,EACV,eAAe,EAChB,MAAM,gBAAgB,CAAC;AAQxB,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"support.d.ts","sourceRoot":"","sources":["../../../src/lib/site/support.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/site/support.js
CHANGED
|
@@ -5,6 +5,7 @@ import { IxVpcDetails } from "../../cdk-constructs/IxVpcDetails.js";
|
|
|
5
5
|
import { IxDnsRecord } from "../../cdk-constructs/IxDnsRecord.js";
|
|
6
6
|
import { CloudFrontTarget } from "aws-cdk-lib/aws-route53-targets";
|
|
7
7
|
import { convertToBase62Hash } from "../utils/hash.js";
|
|
8
|
+
import { SiteOidcAuth, } from "../../cdk-constructs/SiteOidcAuth/index.js";
|
|
8
9
|
export function setupCustomDomain(scope, id, props) {
|
|
9
10
|
let updatedProps = props;
|
|
10
11
|
// Default to using domains names passed in by the pipeline as the custom domain
|
|
@@ -241,3 +242,21 @@ export function getAlternativeDomains(props) {
|
|
|
241
242
|
}
|
|
242
243
|
return [];
|
|
243
244
|
}
|
|
245
|
+
export function processAuthProps(scope, id, siteType, props) {
|
|
246
|
+
if (!props.auth)
|
|
247
|
+
return props;
|
|
248
|
+
const { oidc, ...otherAuthProps } = props.auth;
|
|
249
|
+
const auth = new SiteOidcAuth(scope, `${id}-SiteOidcAuth`, {
|
|
250
|
+
oidcIssuerUrl: oidc.issuerUrl,
|
|
251
|
+
oidcClientId: oidc.clientId,
|
|
252
|
+
oidcScope: oidc.scope,
|
|
253
|
+
});
|
|
254
|
+
if (siteType === "StaticSite") {
|
|
255
|
+
return auth.addToStaticSiteProps(scope, props, otherAuthProps);
|
|
256
|
+
}
|
|
257
|
+
else if (siteType === "SsrSite") {
|
|
258
|
+
return auth.addToSsrSiteProps(scope, props, otherAuthProps);
|
|
259
|
+
}
|
|
260
|
+
siteType;
|
|
261
|
+
throw new Error(`Unsupported site type ${siteType} when processing auth prop.`);
|
|
262
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infoxchange/make-it-so",
|
|
3
|
-
"version": "2.11.0-internal-testing-vdt-199-add-auth-token-verify-function.
|
|
3
|
+
"version": "2.11.0-internal-testing-vdt-199-add-auth-token-verify-function-2.2",
|
|
4
4
|
"description": "Makes deploying services to IX infra easy",
|
|
5
5
|
"repository": "github:infoxchange/make-it-so",
|
|
6
6
|
"type": "module",
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
setupDefaultEnvVars,
|
|
17
17
|
applyConditionalEnvironmentVariables,
|
|
18
18
|
parentCompatibleSsrProps,
|
|
19
|
+
processAuthProps,
|
|
19
20
|
} from "../lib/site/support.js";
|
|
20
21
|
|
|
21
22
|
type ConstructScope = ConstructorParameters<typeof NextjsSite>[0];
|
|
@@ -34,6 +35,7 @@ export class IxNextjsSite extends NextjsSite {
|
|
|
34
35
|
props = setupCertificate(scope, id, props);
|
|
35
36
|
props = setupDomainAliasRedirect(scope, id, props);
|
|
36
37
|
}
|
|
38
|
+
props = processAuthProps(scope, id, "SsrSite", props);
|
|
37
39
|
props = setupDefaultEnvVars(scope, id, props);
|
|
38
40
|
props = applyConditionalEnvironmentVariables(scope, id, props);
|
|
39
41
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getPrimaryCustomDomain,
|
|
9
9
|
getPrimaryDomain,
|
|
10
10
|
getPrimaryOrigin,
|
|
11
|
+
processAuthProps,
|
|
11
12
|
setupCertificate,
|
|
12
13
|
setupCustomDomain,
|
|
13
14
|
setupDnsRecords,
|
|
@@ -32,6 +33,7 @@ export class IxStaticSite extends StaticSite {
|
|
|
32
33
|
props = setupCertificate(scope, id, props);
|
|
33
34
|
props = setupDomainAliasRedirect(scope, id, props);
|
|
34
35
|
}
|
|
36
|
+
props = processAuthProps(scope, id, "StaticSite", props);
|
|
35
37
|
|
|
36
38
|
super(scope, id, props);
|
|
37
39
|
this.propsExtended = props;
|
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
// Based off: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_functions_kvs_jwt_verify_section.html
|
|
2
|
-
// Note that as a CloudFront Function, this code has limitations compared to a Lambda@Edge function.
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
// Note that as a CloudFront Function, this code has limitations compared to a Lambda@Edge function. For example, no
|
|
3
|
+
// external libraries can be used, and the runtime is more limited. Because SST v2's SsrSite construct uses JS v1.0
|
|
4
|
+
// runtime for CloudFront Functions, this code must also be compatible with that runtime. Also this is used in the body
|
|
5
|
+
// of a function where the variables event and request are already defined.
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
declare const request: AWSCloudFrontFunction.Request;
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires -- v1 runtime for CloudFront Functions do not support import statements
|
|
10
|
+
const crypto: typeof import("crypto") = require("crypto");
|
|
11
|
+
|
|
12
|
+
const jwtSecret = "__placeholder-for-jwt-secret__";
|
|
8
13
|
const authRoutePrefix = "__placeholder-for-auth-route-prefix__";
|
|
9
14
|
|
|
15
|
+
// Set to true to enable console logging
|
|
16
|
+
const loggingEnabled = false;
|
|
17
|
+
|
|
18
|
+
const log: typeof console.log = function () {
|
|
19
|
+
if (!loggingEnabled) return;
|
|
20
|
+
|
|
21
|
+
// CloudFront Function runtime only prints first argument passed to console.log so add other args to the first one if given.
|
|
22
|
+
// eslint-disable-next-line prefer-rest-params -- We can't use spread or rest parameters in CloudFront Functions
|
|
23
|
+
let message = arguments[0];
|
|
24
|
+
if (arguments.length > 1) {
|
|
25
|
+
const otherArgs = [];
|
|
26
|
+
for (let i = 1; i < arguments.length; i++) {
|
|
27
|
+
// eslint-disable-next-line prefer-rest-params
|
|
28
|
+
otherArgs[i - 1] = arguments[i];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
message += " - additional args: " + JSON.stringify(otherArgs);
|
|
32
|
+
}
|
|
33
|
+
console.log(message);
|
|
34
|
+
};
|
|
35
|
+
|
|
10
36
|
//Response when JWT is not valid.
|
|
11
37
|
const redirectResponse = {
|
|
12
38
|
statusCode: 302,
|
|
@@ -15,9 +41,6 @@ const redirectResponse = {
|
|
|
15
41
|
},
|
|
16
42
|
};
|
|
17
43
|
|
|
18
|
-
// Set to true to enable console logging
|
|
19
|
-
const loggingEnabled = false;
|
|
20
|
-
|
|
21
44
|
function jwtDecode(token: string, key: string, noVerify?: boolean) {
|
|
22
45
|
// check segments
|
|
23
46
|
const segments = token.split(".");
|
|
@@ -96,62 +119,20 @@ function _base64urlDecode(str: string) {
|
|
|
96
119
|
return Buffer.from(str, "base64url").toString();
|
|
97
120
|
}
|
|
98
121
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const secret_key = await getSecret();
|
|
122
|
+
const jwtToken =
|
|
123
|
+
request.cookies["auth-token"] && request.cookies["auth-token"].value;
|
|
102
124
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const jwtToken =
|
|
109
|
-
request.cookies["auth-token"] && request.cookies["auth-token"].value;
|
|
110
|
-
|
|
111
|
-
if (!jwtToken) {
|
|
112
|
-
log("Error: No JWT in the cookies");
|
|
113
|
-
return redirectResponse;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
jwtDecode(jwtToken, secret_key);
|
|
117
|
-
} catch (e) {
|
|
118
|
-
log(e);
|
|
119
|
-
return redirectResponse;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
log("Valid JWT token");
|
|
123
|
-
return request;
|
|
125
|
+
if (!jwtToken) {
|
|
126
|
+
log("Error: No JWT in the cookies");
|
|
127
|
+
// @ts-expect-error -- This code is added to a function body so we can use return here but typescript doesn't know that.
|
|
128
|
+
return redirectResponse;
|
|
124
129
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
} catch (err) {
|
|
132
|
-
log(`Error reading value for key: ${kvsKey}, error: ${err}`);
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
130
|
+
try {
|
|
131
|
+
jwtDecode(jwtToken, jwtSecret);
|
|
132
|
+
} catch (e) {
|
|
133
|
+
log(e);
|
|
134
|
+
// @ts-expect-error -- This code is added to a function body so we can use return here but typescript doesn't know that.
|
|
135
|
+
return redirectResponse;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
|
|
138
|
-
if (!loggingEnabled) return;
|
|
139
|
-
|
|
140
|
-
// CloudFront Function runtime only prints first argument passed to console.log so add other args to the first one if given.
|
|
141
|
-
// eslint-disable-next-line prefer-rest-params -- We can't use spread or rest parameters in CloudFront Functions
|
|
142
|
-
let message = arguments[0];
|
|
143
|
-
if (arguments.length > 1) {
|
|
144
|
-
const otherArgs = [];
|
|
145
|
-
for (let i = 1; i < arguments.length; i++) {
|
|
146
|
-
// eslint-disable-next-line prefer-rest-params
|
|
147
|
-
otherArgs[i - 1] = arguments[i];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
message += " - additional args: " + JSON.stringify(otherArgs);
|
|
151
|
-
}
|
|
152
|
-
console.log(message);
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// This serves no purpose other than to make TypeScript and eslint happy by showing that that handler is used. We can't
|
|
156
|
-
// export handler as an alterative because CloudFront Functions don't support exports.
|
|
157
|
-
handler;
|
|
138
|
+
log("Valid JWT token");
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { Construct } from "constructs";
|
|
2
|
+
import SecretsManager from "aws-cdk-lib/aws-secretsmanager";
|
|
3
|
+
import CloudFront from "aws-cdk-lib/aws-cloudfront";
|
|
4
|
+
import CDK from "aws-cdk-lib";
|
|
5
|
+
import Lambda from "aws-cdk-lib/aws-lambda";
|
|
6
|
+
import * as SST from "sst/constructs";
|
|
7
|
+
import { isCDKConstruct } from "sst/constructs/Construct.js";
|
|
8
|
+
import { Config as SSTInternalConfig } from "sst/config.js";
|
|
9
|
+
import CloudFrontOrigins from "aws-cdk-lib/aws-cloudfront-origins";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import { transformSync } from "esbuild";
|
|
13
|
+
import type {
|
|
14
|
+
ExtendedNextjsSiteProps,
|
|
15
|
+
ExtendedStaticSiteProps,
|
|
16
|
+
} from "../../lib/site/support.js";
|
|
17
|
+
|
|
18
|
+
type ConstructScope = ConstructorParameters<typeof Construct>[0];
|
|
19
|
+
type ConstructId = ConstructorParameters<typeof Construct>[1];
|
|
20
|
+
|
|
21
|
+
export type Props = {
|
|
22
|
+
oidcIssuerUrl: string;
|
|
23
|
+
oidcClientId: string;
|
|
24
|
+
oidcScope: string;
|
|
25
|
+
};
|
|
26
|
+
export type AddToSiteProps = { prefix?: string };
|
|
27
|
+
|
|
28
|
+
const defaultAuthRoutePrefix = "/auth";
|
|
29
|
+
|
|
30
|
+
export class SiteOidcAuth extends Construct {
|
|
31
|
+
readonly oidcIssuerUrl: string;
|
|
32
|
+
readonly oidcClientId: string;
|
|
33
|
+
readonly oidcScope: string;
|
|
34
|
+
readonly id: string;
|
|
35
|
+
|
|
36
|
+
constructor(scope: ConstructScope, id: ConstructId, props: Props) {
|
|
37
|
+
super(scope, id);
|
|
38
|
+
this.oidcIssuerUrl = props.oidcIssuerUrl;
|
|
39
|
+
this.oidcClientId = props.oidcClientId;
|
|
40
|
+
this.oidcScope = props.oidcScope;
|
|
41
|
+
this.id = id;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addToStaticSiteProps<SiteProps extends ExtendedStaticSiteProps>(
|
|
45
|
+
scope: ConstructScope,
|
|
46
|
+
siteProps: SiteProps,
|
|
47
|
+
{ prefix = defaultAuthRoutePrefix }: AddToSiteProps = {},
|
|
48
|
+
) {
|
|
49
|
+
prefix = prefix.replace(/\/$/, ""); // Remove trailing slash from prefix if it has one
|
|
50
|
+
const behaviourName = `${prefix.replace(/^\//g, "")}/*`;
|
|
51
|
+
const distribution = siteProps.cdk?.distribution;
|
|
52
|
+
if (isCDKConstruct(distribution)) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`CDK Construct for distribution is not supported when adding CloudFront OIDC Auth behavior for prefix ${prefix}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const updatedSiteProps = {
|
|
59
|
+
...siteProps,
|
|
60
|
+
cdk: {
|
|
61
|
+
...siteProps.cdk,
|
|
62
|
+
distribution: {
|
|
63
|
+
...siteProps.cdk?.distribution,
|
|
64
|
+
additionalBehaviors: distribution?.additionalBehaviors ?? {},
|
|
65
|
+
defaultBehavior: {
|
|
66
|
+
...distribution?.defaultBehavior,
|
|
67
|
+
functionAssociations:
|
|
68
|
+
distribution?.defaultBehavior?.functionAssociations ?? [],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (updatedSiteProps.cdk.distribution.additionalBehaviors[behaviourName]) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Behavior for prefix ${prefix} already exists in distribution definition`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const jwtSecret = this.createJwtSecret();
|
|
81
|
+
|
|
82
|
+
updatedSiteProps.cdk.distribution.defaultBehavior.functionAssociations.push(
|
|
83
|
+
this.getFunctionAssociation(scope, jwtSecret, prefix),
|
|
84
|
+
);
|
|
85
|
+
updatedSiteProps.cdk.distribution.additionalBehaviors[behaviourName] =
|
|
86
|
+
this.getAuthBehaviorOptions(scope, jwtSecret, prefix);
|
|
87
|
+
|
|
88
|
+
return updatedSiteProps;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
addToSsrSiteProps<SiteProps extends ExtendedNextjsSiteProps>(
|
|
92
|
+
scope: ConstructScope,
|
|
93
|
+
siteProps: SiteProps,
|
|
94
|
+
{ prefix = defaultAuthRoutePrefix }: AddToSiteProps = {},
|
|
95
|
+
) {
|
|
96
|
+
prefix = prefix.replace(/\/$/, ""); // Remove trailing slash from prefix if it has one
|
|
97
|
+
const behaviourName = `${prefix.replace(/^\//g, "")}/*`;
|
|
98
|
+
const updatedSiteProps = {
|
|
99
|
+
...siteProps,
|
|
100
|
+
cdk: {
|
|
101
|
+
...siteProps.cdk,
|
|
102
|
+
distribution: {
|
|
103
|
+
...siteProps.cdk?.distribution,
|
|
104
|
+
additionalBehaviors:
|
|
105
|
+
siteProps.cdk?.distribution?.additionalBehaviors ?? {},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
if (updatedSiteProps.cdk.distribution.additionalBehaviors[behaviourName]) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Behavior for prefix ${prefix} already exists in distribution definition`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const jwtSecret = this.createJwtSecret();
|
|
116
|
+
|
|
117
|
+
updatedSiteProps.cdk.transform = (plan) => {
|
|
118
|
+
siteProps?.cdk?.transform?.(plan);
|
|
119
|
+
|
|
120
|
+
plan.cloudFrontFunctions?.serverCfFunction.injections.push(
|
|
121
|
+
this.getAuthCheckHandlerBodyCode(jwtSecret, prefix),
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
updatedSiteProps.cdk.distribution.additionalBehaviors[behaviourName] =
|
|
126
|
+
this.getAuthBehaviorOptions(scope, jwtSecret, prefix);
|
|
127
|
+
|
|
128
|
+
return updatedSiteProps;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private createJwtSecret() {
|
|
132
|
+
return new SecretsManager.Secret(this, `${this.id}JwtSecret`, {
|
|
133
|
+
description: "JWT Signing Secret",
|
|
134
|
+
generateSecretString: {
|
|
135
|
+
passwordLength: 32,
|
|
136
|
+
excludePunctuation: true,
|
|
137
|
+
includeSpace: false,
|
|
138
|
+
requireEachIncludedType: true,
|
|
139
|
+
},
|
|
140
|
+
// Secret is only used for sessions so it's safe to delete on stack removal
|
|
141
|
+
removalPolicy: CDK.RemovalPolicy.DESTROY,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get the CloudFront Function Association for auth checking
|
|
146
|
+
// Roughly based off https://github.com/sst/v2/blob/4283d706f251724308b397996ff307929bf3a976/packages/sst/src/constructs/SsrSite.ts#L941
|
|
147
|
+
private getFunctionAssociation(
|
|
148
|
+
scope: ConstructScope,
|
|
149
|
+
jwtSecret: SecretsManager.Secret,
|
|
150
|
+
authRoutePrefix: string,
|
|
151
|
+
): CloudFront.FunctionAssociation {
|
|
152
|
+
const authCheckFunction = new CloudFront.Function(
|
|
153
|
+
scope,
|
|
154
|
+
`${this.id}AuthCheckFunction`,
|
|
155
|
+
{
|
|
156
|
+
code: CloudFront.FunctionCode.fromInline(
|
|
157
|
+
this.convertToCloudFrontFunctionCompatibleCode(
|
|
158
|
+
`function handler(event) {
|
|
159
|
+
var request = event.request;
|
|
160
|
+
${this.getAuthCheckHandlerBodyCode(jwtSecret, authRoutePrefix)}
|
|
161
|
+
return request;
|
|
162
|
+
}`,
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
// We could specify the JS v2.0 runtime here but for SSR sites SST does the function creation and that currently
|
|
166
|
+
// uses JS v1.0 so no point using v2.0 here as the code has to be compatible with v1.0 anyway.
|
|
167
|
+
},
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
function: authCheckFunction,
|
|
172
|
+
eventType: CloudFront.FunctionEventType.VIEWER_REQUEST,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private getAuthCheckHandlerBodyCode(
|
|
177
|
+
jwtSecret: SecretsManager.Secret,
|
|
178
|
+
authRoutePrefix: string,
|
|
179
|
+
): string {
|
|
180
|
+
return fs
|
|
181
|
+
.readFileSync(
|
|
182
|
+
path.join(import.meta.dirname, "auth-check-handler-body.js"),
|
|
183
|
+
"utf8",
|
|
184
|
+
)
|
|
185
|
+
.replace(
|
|
186
|
+
"__placeholder-for-jwt-secret__",
|
|
187
|
+
jwtSecret.secretValue.toString(),
|
|
188
|
+
)
|
|
189
|
+
.replace("__placeholder-for-auth-route-prefix__", authRoutePrefix);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private convertToCloudFrontFunctionCompatibleCode(
|
|
193
|
+
sourceCode: string,
|
|
194
|
+
): string {
|
|
195
|
+
// ESBuild doesn't currently support transforming const/let to var, which is required for CloudFront Functions
|
|
196
|
+
// JS runtime 1.0.
|
|
197
|
+
sourceCode = sourceCode
|
|
198
|
+
.replaceAll(/const /g, "var ")
|
|
199
|
+
.replaceAll(/let /g, "var ");
|
|
200
|
+
return transformSync(sourceCode, {
|
|
201
|
+
minify: true,
|
|
202
|
+
target: "es5",
|
|
203
|
+
}).code;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Get the behavior options for the auth route
|
|
207
|
+
private getAuthBehaviorOptions(
|
|
208
|
+
scope: ConstructScope,
|
|
209
|
+
jwtSecret: SecretsManager.Secret,
|
|
210
|
+
prefix: string,
|
|
211
|
+
): CloudFront.BehaviorOptions {
|
|
212
|
+
const authRouteFunction = new SST.Function(
|
|
213
|
+
scope,
|
|
214
|
+
`${this.id}AuthRouteFunction`,
|
|
215
|
+
{
|
|
216
|
+
runtime: "nodejs20.x",
|
|
217
|
+
handler: path.join(import.meta.dirname, "auth-route.handler"),
|
|
218
|
+
environment: {
|
|
219
|
+
OIDC_ISSUER_URL: this.oidcIssuerUrl,
|
|
220
|
+
OIDC_CLIENT_ID: this.oidcClientId,
|
|
221
|
+
OIDC_SCOPE: this.oidcScope,
|
|
222
|
+
JWT_SECRET: jwtSecret.secretValue.toString(),
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// authRouteFunction uses SST's AuthHandler construct which is normally run inside a lambda that's
|
|
228
|
+
// created by SST's Auth construct. AuthHandler expects certain environment variables to be set
|
|
229
|
+
// by the Auth construct so we have to set them ourselves here to keep it happy.
|
|
230
|
+
const envVarName = SSTInternalConfig.envFor({
|
|
231
|
+
type: "Auth",
|
|
232
|
+
id: "id", // It seems like the env var will still be found no matter what this value is
|
|
233
|
+
prop: "prefix",
|
|
234
|
+
});
|
|
235
|
+
authRouteFunction.addEnvironment(envVarName, prefix);
|
|
236
|
+
|
|
237
|
+
const authRouteFunctionUrl = authRouteFunction.addFunctionUrl({
|
|
238
|
+
authType: Lambda.FunctionUrlAuthType.NONE,
|
|
239
|
+
});
|
|
240
|
+
const forwardHostHeaderCfFunction = new CloudFront.Function(
|
|
241
|
+
scope,
|
|
242
|
+
`${this.id}ForwardHostHeaderFunction`,
|
|
243
|
+
{
|
|
244
|
+
code: CloudFront.FunctionCode.fromInline(
|
|
245
|
+
this.convertToCloudFrontFunctionCompatibleCode(
|
|
246
|
+
`function handler(event) {
|
|
247
|
+
const request = event.request;
|
|
248
|
+
request.headers["x-forwarded-host"] = { value: request.headers.host.value };
|
|
249
|
+
return request;
|
|
250
|
+
}`,
|
|
251
|
+
),
|
|
252
|
+
),
|
|
253
|
+
runtime: CloudFront.FunctionRuntime.JS_2_0,
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
origin: new CloudFrontOrigins.HttpOrigin(
|
|
259
|
+
CDK.Fn.parseDomainName(authRouteFunctionUrl.url),
|
|
260
|
+
),
|
|
261
|
+
allowedMethods: CloudFront.AllowedMethods.ALLOW_ALL,
|
|
262
|
+
cachePolicy: new CloudFront.CachePolicy(
|
|
263
|
+
scope,
|
|
264
|
+
`${this.id}AllowAllCookiesPolicy`,
|
|
265
|
+
{
|
|
266
|
+
cachePolicyName: `${this.id}-AllowAllCookiesPolicy`,
|
|
267
|
+
comment: "Cache policy that forwards all cookies",
|
|
268
|
+
defaultTtl: CDK.Duration.seconds(1),
|
|
269
|
+
minTtl: CDK.Duration.seconds(1),
|
|
270
|
+
maxTtl: CDK.Duration.seconds(1),
|
|
271
|
+
cookieBehavior: CloudFront.CacheCookieBehavior.all(),
|
|
272
|
+
headerBehavior:
|
|
273
|
+
CloudFront.CacheHeaderBehavior.allowList("X-Forwarded-Host"),
|
|
274
|
+
queryStringBehavior: CloudFront.CacheQueryStringBehavior.all(),
|
|
275
|
+
enableAcceptEncodingGzip: true,
|
|
276
|
+
enableAcceptEncodingBrotli: true,
|
|
277
|
+
},
|
|
278
|
+
),
|
|
279
|
+
viewerProtocolPolicy: CloudFront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
280
|
+
functionAssociations: [
|
|
281
|
+
{
|
|
282
|
+
function: forwardHostHeaderCfFunction,
|
|
283
|
+
eventType: CloudFront.FunctionEventType.VIEWER_REQUEST,
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|