@auth-craft/aws-cf-stack 1.1.1 → 1.2.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.
- package/.env.example +15 -0
- package/README.md +117 -0
- package/assets/gateway/worker.js +1 -1
- package/assets/lambda/index.mjs +32 -32
- package/lib/common.sh +16 -1
- package/lib/deploy-gateway.sh +54 -4
- package/package.json +1 -1
- package/wrangler/wrangler.toml +5 -0
package/lib/common.sh
CHANGED
|
@@ -227,6 +227,17 @@ print_outputs_json() {
|
|
|
227
227
|
jwtAlg="$(lambda_env "$fn" JWT_ALGORITHM)"; jwtAlg="${jwtAlg:-${LAMBDA_JWT_ALGORITHM:-EdDSA}}"
|
|
228
228
|
svcPub="$(lambda_env "$fn" INTERNAL_JWT_PUBLIC_KEY)"; svcPub="${svcPub:-${LAMBDA_INTERNAL_JWT_PUBLIC_KEY:-}}"
|
|
229
229
|
|
|
230
|
+
# Worker names per scope (mirror deploy-gateway.sh: ${APP_NAME}-${STAGE}-${scope}).
|
|
231
|
+
# These are what a consumer's Cloudflare Pages project references in a Service Binding
|
|
232
|
+
# (`[[services]] service = "<worker name>"`) to front /{scope}/auth/* same-origin via
|
|
233
|
+
# @auth-craft/pages-auth-proxy. The binding lives in the consumer's Pages project, not
|
|
234
|
+
# here — `serviceBindings` is a ready-to-paste snippet they drop into their wrangler.toml.
|
|
235
|
+
local wnSystem="${APP_NAME}-${STAGE}-system"
|
|
236
|
+
local wnTenant="${APP_NAME}-${STAGE}-tenant"
|
|
237
|
+
local wnCustomer="${APP_NAME}-${STAGE}-customer"
|
|
238
|
+
local bindingSnippet
|
|
239
|
+
bindingSnippet=$(printf '[[services]]\nbinding = "AUTH_GATEWAY"\nservice = "%s"\n' "$wnCustomer")
|
|
240
|
+
|
|
230
241
|
jq -n \
|
|
231
242
|
--arg url "$url" --arg base "$base" --arg name "$name" \
|
|
232
243
|
--arg table "$table" --arg backend "$backend" \
|
|
@@ -240,13 +251,17 @@ print_outputs_json() {
|
|
|
240
251
|
--arg rpSystem "${CF_WORKER_ROUTE_PREFIX_SYSTEM:-/system}" \
|
|
241
252
|
--arg rpTenant "${CF_WORKER_ROUTE_PREFIX_TENANT:-/tenant}" \
|
|
242
253
|
--arg rpCustomer "${CF_WORKER_ROUTE_PREFIX_CUSTOMER:-/customer}" \
|
|
254
|
+
--arg wnSystem "$wnSystem" --arg wnTenant "$wnTenant" --arg wnCustomer "$wnCustomer" \
|
|
255
|
+
--arg bindingSnippet "$bindingSnippet" \
|
|
243
256
|
'{stage:$stage, project:$project, region:$region,
|
|
244
257
|
lambdaFunctionUrl:$url, apiBasePath:$base, authSystemName:$name,
|
|
245
258
|
dynamodbTable:$table, backendUrl:$backend,
|
|
246
259
|
jwtPublicKey:$jwtPub, jwtIssuer:$jwtIssuer, jwtAlgorithm:$jwtAlg,
|
|
247
260
|
serviceJwtPublicKey:$svcPub,
|
|
248
261
|
gatewayUrls:{system:$gwSystem, tenant:$gwTenant, customer:$gwCustomer},
|
|
249
|
-
|
|
262
|
+
workerNames:{system:$wnSystem, tenant:$wnTenant, customer:$wnCustomer},
|
|
263
|
+
routePrefixes:{system:$rpSystem, tenant:$rpTenant, customer:$rpCustomer},
|
|
264
|
+
pagesProxy:{serviceBindingExample:$bindingSnippet}}'
|
|
250
265
|
}
|
|
251
266
|
|
|
252
267
|
# ── Tool command resolution (#7: let a consumer pin its own binaries) ─────────
|
package/lib/deploy-gateway.sh
CHANGED
|
@@ -30,6 +30,33 @@ custom_domain_for() {
|
|
|
30
30
|
echo "${!var:-}"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
# Resolve per-scope static CORS origins. Precedence:
|
|
34
|
+
# CF_ALLOWED_ORIGINS_<SCOPE> > CF_ALLOWED_ORIGINS > LAMBDA_CORS_ORIGINS > '*'.
|
|
35
|
+
allowed_origins_for() {
|
|
36
|
+
local scope="$1" upper var
|
|
37
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
38
|
+
var="CF_ALLOWED_ORIGINS_${upper}"
|
|
39
|
+
echo "${!var:-${CF_ALLOWED_ORIGINS:-${LAMBDA_CORS_ORIGINS:-*}}}"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Resolve per-scope dynamic origin SUFFIXES (e.g. '.myplatform.com'). Precedence:
|
|
43
|
+
# CF_ALLOWED_ORIGIN_SUFFIXES_<SCOPE> > CF_ALLOWED_ORIGIN_SUFFIXES > '' (disabled).
|
|
44
|
+
origin_suffixes_for() {
|
|
45
|
+
local scope="$1" upper var
|
|
46
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
47
|
+
var="CF_ALLOWED_ORIGIN_SUFFIXES_${upper}"
|
|
48
|
+
echo "${!var:-${CF_ALLOWED_ORIGIN_SUFFIXES:-}}"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Resolve a per-scope CORS KV namespace id (CF_CORS_KV_ID_CUSTOMER, …) — empty = no
|
|
52
|
+
# dynamic KV allowlist for that scope. Typically only the customer scope sets this.
|
|
53
|
+
cors_kv_id_for() {
|
|
54
|
+
local scope="$1" upper var
|
|
55
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
56
|
+
var="CF_CORS_KV_ID_${upper}"
|
|
57
|
+
echo "${!var:-}"
|
|
58
|
+
}
|
|
59
|
+
|
|
33
60
|
# Enable the workers.dev subdomain for a worker with no custom domain (so it's reachable).
|
|
34
61
|
enable_workers_dev() {
|
|
35
62
|
local worker_name="$1"
|
|
@@ -60,11 +87,10 @@ deploy_gateway() {
|
|
|
60
87
|
# Anti-drift: BACKEND_URL the worker forwards onto = Function URL + API base path.
|
|
61
88
|
local backend_url="${LAMBDA_FUNCTION_URL%/}${API_BASE_PATH}"
|
|
62
89
|
|
|
63
|
-
# Shared config across the 3 workers.
|
|
64
|
-
local environment jwt_issuer
|
|
90
|
+
# Shared config across the 3 workers (CORS is now resolved PER SCOPE below).
|
|
91
|
+
local environment jwt_issuer
|
|
65
92
|
environment="$RUNTIME_NODE_ENV"
|
|
66
93
|
jwt_issuer="${LAMBDA_JWT_ISSUER:-${AUTH_SYSTEM_NAME:-$RESOURCE_NAME}}"
|
|
67
|
-
allowed_origins="${CF_ALLOWED_ORIGINS:-${LAMBDA_CORS_ORIGINS:-*}}"
|
|
68
94
|
|
|
69
95
|
local jwt_alg="${LAMBDA_JWT_ALGORITHM:-EdDSA}"
|
|
70
96
|
local svc_route="${LAMBDA_SERVICE_ROUTE_PATH:-/internal}"
|
|
@@ -74,22 +100,43 @@ deploy_gateway() {
|
|
|
74
100
|
|
|
75
101
|
GATEWAY_URLS=()
|
|
76
102
|
local scope worker_name route_prefix custom_domain url
|
|
103
|
+
local allowed_origins origin_suffixes cors_kv_id config_file
|
|
77
104
|
for scope in system tenant customer; do
|
|
78
105
|
worker_name="${APP_NAME}-${STAGE}-${scope}"
|
|
79
106
|
route_prefix="$(route_prefix_for "$scope")"
|
|
80
107
|
custom_domain="$(custom_domain_for "$scope")"
|
|
108
|
+
allowed_origins="$(allowed_origins_for "$scope")"
|
|
109
|
+
origin_suffixes="$(origin_suffixes_for "$scope")"
|
|
110
|
+
cors_kv_id="$(cors_kv_id_for "$scope")"
|
|
81
111
|
|
|
82
112
|
info "Deploying $worker_name (scope=$scope route_prefix=$route_prefix)…"
|
|
83
113
|
|
|
114
|
+
# When a CORS KV id is set for this scope (typically customer), deploy with a
|
|
115
|
+
# temp config that appends a [[kv_namespaces]] binding (CORS_KV) — a binding can't
|
|
116
|
+
# be set via --var. Otherwise use the shipped config unchanged (KV disabled).
|
|
117
|
+
config_file="$WRANGLER_DIR/wrangler.toml"
|
|
118
|
+
if [[ -n "$cors_kv_id" ]]; then
|
|
119
|
+
config_file="$(mktemp "${TMPDIR:-/tmp}/wrangler-${scope}.XXXXXX.toml")"
|
|
120
|
+
cat "$WRANGLER_DIR/wrangler.toml" >"$config_file"
|
|
121
|
+
{
|
|
122
|
+
echo ''
|
|
123
|
+
echo '[[kv_namespaces]]'
|
|
124
|
+
echo 'binding = "CORS_KV"'
|
|
125
|
+
echo "id = \"$cors_kv_id\""
|
|
126
|
+
} >>"$config_file"
|
|
127
|
+
info " dynamic CORS: KV binding CORS_KV → $cors_kv_id"
|
|
128
|
+
fi
|
|
129
|
+
|
|
84
130
|
# Deploy the prebuilt bundle with per-scope vars (flags override the toml).
|
|
85
131
|
wrangler deploy \
|
|
86
132
|
--name "$worker_name" \
|
|
87
|
-
--config "$
|
|
133
|
+
--config "$config_file" \
|
|
88
134
|
--no-bundle \
|
|
89
135
|
--var "ENVIRONMENT:$environment" \
|
|
90
136
|
--var "AUTH_SCOPE:$scope" \
|
|
91
137
|
--var "ROUTE_PREFIX:$route_prefix" \
|
|
92
138
|
--var "ALLOWED_ORIGINS:$allowed_origins" \
|
|
139
|
+
--var "ALLOWED_ORIGIN_SUFFIXES:$origin_suffixes" \
|
|
93
140
|
--var "JWT_ISSUER:$jwt_issuer" \
|
|
94
141
|
--var "JWT_ALLOWED_ALGORITHMS:$jwt_alg" \
|
|
95
142
|
--var "SERVICE_ROUTE_PREFIX:$svc_route" \
|
|
@@ -97,6 +144,9 @@ deploy_gateway() {
|
|
|
97
144
|
--var "SERVICE_JWT_ISSUER:$svc_iss" \
|
|
98
145
|
--var "SERVICE_JWT_ALLOWED_ALGORITHMS:$svc_alg" >/dev/null
|
|
99
146
|
|
|
147
|
+
# Clean up the temp config (only created when KV was bound).
|
|
148
|
+
[[ "$config_file" != "$WRANGLER_DIR/wrangler.toml" ]] && rm -f "$config_file"
|
|
149
|
+
|
|
100
150
|
# Secrets (piped via stdin).
|
|
101
151
|
put_secret "$worker_name" BACKEND_URL "$backend_url"
|
|
102
152
|
put_secret "$worker_name" BACKEND_GATEWAY_SECRET "${LAMBDA_GATEWAY_SECRET:-}"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@auth-craft/aws-cf-stack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Self-contained, versioned distribution of the Auth Craft AWS (DynamoDB + Lambda) + Cloudflare gateway stack. Bundles prebuilt Lambda/worker artifacts + CDK app so consumers deploy without cloning auth-craft.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/wrangler/wrangler.toml
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
# (assets/gateway/worker.js). deploy-gateway.sh overrides `name` and injects vars
|
|
5
5
|
# per scope via `wrangler deploy --name <name> --var K:V ...`, and pushes secrets via
|
|
6
6
|
# `wrangler secret put`. No source compile happens at deploy time.
|
|
7
|
+
#
|
|
8
|
+
# CORS_KV binding (dynamic CORS allowlist) is NOT declared here: a KV binding can't be
|
|
9
|
+
# set via --var, so when CF_CORS_KV_ID_<SCOPE> is set deploy-gateway.sh deploys that
|
|
10
|
+
# scope with a temp copy of this file that appends a [[kv_namespaces]] block. Leaving
|
|
11
|
+
# it out here keeps scopes without a KV id (system/tenant) binding-free.
|
|
7
12
|
name = "auth-craft-gateway"
|
|
8
13
|
main = "../assets/gateway/worker.js"
|
|
9
14
|
compatibility_date = "2024-11-27"
|