@auth-craft/aws-cf-stack 1.1.1 → 1.3.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 +24 -0
- package/README.md +129 -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 +96 -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,48 @@ custom_domain_for() {
|
|
|
30
30
|
echo "${!var:-}"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
# Resolve whether to disable the public workers.dev URL for a scope (Pages-proxy /
|
|
34
|
+
# Service-Binding deploys reach the worker in-network and don't need a public URL).
|
|
35
|
+
# Precedence: CF_WORKER_DISABLE_PUBLIC_URL_<SCOPE> > CF_WORKER_DISABLE_PUBLIC_URL > '' (off).
|
|
36
|
+
# Truthy values: 1 / true / yes (case-insensitive). Ignored when a custom domain is set.
|
|
37
|
+
disable_public_url_for() {
|
|
38
|
+
local scope="$1" upper var val
|
|
39
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
40
|
+
var="CF_WORKER_DISABLE_PUBLIC_URL_${upper}"
|
|
41
|
+
val="${!var:-${CF_WORKER_DISABLE_PUBLIC_URL:-}}"
|
|
42
|
+
case "$(echo "$val" | tr '[:upper:]' '[:lower:]')" in
|
|
43
|
+
1 | true | yes) echo 1 ;;
|
|
44
|
+
*) echo '' ;;
|
|
45
|
+
esac
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Resolve per-scope static CORS origins. Precedence:
|
|
49
|
+
# CF_ALLOWED_ORIGINS_<SCOPE> > CF_ALLOWED_ORIGINS > LAMBDA_CORS_ORIGINS > '*'.
|
|
50
|
+
allowed_origins_for() {
|
|
51
|
+
local scope="$1" upper var
|
|
52
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
53
|
+
var="CF_ALLOWED_ORIGINS_${upper}"
|
|
54
|
+
echo "${!var:-${CF_ALLOWED_ORIGINS:-${LAMBDA_CORS_ORIGINS:-*}}}"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Resolve per-scope dynamic origin SUFFIXES (e.g. '.myplatform.com'). Precedence:
|
|
58
|
+
# CF_ALLOWED_ORIGIN_SUFFIXES_<SCOPE> > CF_ALLOWED_ORIGIN_SUFFIXES > '' (disabled).
|
|
59
|
+
origin_suffixes_for() {
|
|
60
|
+
local scope="$1" upper var
|
|
61
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
62
|
+
var="CF_ALLOWED_ORIGIN_SUFFIXES_${upper}"
|
|
63
|
+
echo "${!var:-${CF_ALLOWED_ORIGIN_SUFFIXES:-}}"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Resolve a per-scope CORS KV namespace id (CF_CORS_KV_ID_CUSTOMER, …) — empty = no
|
|
67
|
+
# dynamic KV allowlist for that scope. Typically only the customer scope sets this.
|
|
68
|
+
cors_kv_id_for() {
|
|
69
|
+
local scope="$1" upper var
|
|
70
|
+
upper="$(echo "$scope" | tr '[:lower:]' '[:upper:]')"
|
|
71
|
+
var="CF_CORS_KV_ID_${upper}"
|
|
72
|
+
echo "${!var:-}"
|
|
73
|
+
}
|
|
74
|
+
|
|
33
75
|
# Enable the workers.dev subdomain for a worker with no custom domain (so it's reachable).
|
|
34
76
|
enable_workers_dev() {
|
|
35
77
|
local worker_name="$1"
|
|
@@ -60,11 +102,10 @@ deploy_gateway() {
|
|
|
60
102
|
# Anti-drift: BACKEND_URL the worker forwards onto = Function URL + API base path.
|
|
61
103
|
local backend_url="${LAMBDA_FUNCTION_URL%/}${API_BASE_PATH}"
|
|
62
104
|
|
|
63
|
-
# Shared config across the 3 workers.
|
|
64
|
-
local environment jwt_issuer
|
|
105
|
+
# Shared config across the 3 workers (CORS is now resolved PER SCOPE below).
|
|
106
|
+
local environment jwt_issuer
|
|
65
107
|
environment="$RUNTIME_NODE_ENV"
|
|
66
108
|
jwt_issuer="${LAMBDA_JWT_ISSUER:-${AUTH_SYSTEM_NAME:-$RESOURCE_NAME}}"
|
|
67
|
-
allowed_origins="${CF_ALLOWED_ORIGINS:-${LAMBDA_CORS_ORIGINS:-*}}"
|
|
68
109
|
|
|
69
110
|
local jwt_alg="${LAMBDA_JWT_ALGORITHM:-EdDSA}"
|
|
70
111
|
local svc_route="${LAMBDA_SERVICE_ROUTE_PATH:-/internal}"
|
|
@@ -74,22 +115,66 @@ deploy_gateway() {
|
|
|
74
115
|
|
|
75
116
|
GATEWAY_URLS=()
|
|
76
117
|
local scope worker_name route_prefix custom_domain url
|
|
118
|
+
local allowed_origins origin_suffixes cors_kv_id config_file disable_public_url
|
|
77
119
|
for scope in system tenant customer; do
|
|
78
120
|
worker_name="${APP_NAME}-${STAGE}-${scope}"
|
|
79
121
|
route_prefix="$(route_prefix_for "$scope")"
|
|
80
122
|
custom_domain="$(custom_domain_for "$scope")"
|
|
123
|
+
allowed_origins="$(allowed_origins_for "$scope")"
|
|
124
|
+
origin_suffixes="$(origin_suffixes_for "$scope")"
|
|
125
|
+
cors_kv_id="$(cors_kv_id_for "$scope")"
|
|
126
|
+
# A custom domain always needs the worker reachable, so the disable flag is ignored there.
|
|
127
|
+
disable_public_url=""
|
|
128
|
+
[[ -z "$custom_domain" ]] && disable_public_url="$(disable_public_url_for "$scope")"
|
|
81
129
|
|
|
82
130
|
info "Deploying $worker_name (scope=$scope route_prefix=$route_prefix)…"
|
|
83
131
|
|
|
132
|
+
# A temp config is needed when either (a) a CORS KV id is set (a binding can't be
|
|
133
|
+
# set via --var) or (b) the public URL must be disabled (workers_dev/preview_urls are
|
|
134
|
+
# top-level config keys, not vars, and CF won't disable preview URLs just because
|
|
135
|
+
# workers.dev is off — both must be set explicitly). Otherwise use the shipped config.
|
|
136
|
+
config_file="$WRANGLER_DIR/wrangler.toml"
|
|
137
|
+
if [[ -n "$cors_kv_id" || -n "$disable_public_url" ]]; then
|
|
138
|
+
config_file="$(mktemp "${TMPDIR:-/tmp}/wrangler-${scope}.XXXXXX.toml")"
|
|
139
|
+
if [[ -n "$disable_public_url" ]]; then
|
|
140
|
+
# Turn off the workers.dev URL *and* the per-version preview URLs in a single
|
|
141
|
+
# deploy (no REST API round-trip; CF won't drop preview URLs just for workers_dev
|
|
142
|
+
# being off, so both are set explicitly). These are ROOT keys: they must precede
|
|
143
|
+
# any [table] header or TOML scopes them under it (e.g. [build]). We drop the
|
|
144
|
+
# shipped `workers_dev = true` (TOML forbids duplicate keys) and inject both flags
|
|
145
|
+
# immediately after the first root key (`name = …`), before [build].
|
|
146
|
+
awk '
|
|
147
|
+
/^[[:space:]]*workers_dev[[:space:]]*=/ { next } # drop original (anywhere)
|
|
148
|
+
{ print }
|
|
149
|
+
!injected && /^[[:space:]]*name[[:space:]]*=/ {
|
|
150
|
+
print "workers_dev = false"; print "preview_urls = false"; injected = 1
|
|
151
|
+
}
|
|
152
|
+
' "$WRANGLER_DIR/wrangler.toml" >"$config_file"
|
|
153
|
+
info " public URL disabled: workers.dev + preview URLs off (Pages-proxy / Service Binding only)"
|
|
154
|
+
else
|
|
155
|
+
cat "$WRANGLER_DIR/wrangler.toml" >"$config_file"
|
|
156
|
+
fi
|
|
157
|
+
if [[ -n "$cors_kv_id" ]]; then
|
|
158
|
+
{
|
|
159
|
+
echo ''
|
|
160
|
+
echo '[[kv_namespaces]]'
|
|
161
|
+
echo 'binding = "CORS_KV"'
|
|
162
|
+
echo "id = \"$cors_kv_id\""
|
|
163
|
+
} >>"$config_file"
|
|
164
|
+
info " dynamic CORS: KV binding CORS_KV → $cors_kv_id"
|
|
165
|
+
fi
|
|
166
|
+
fi
|
|
167
|
+
|
|
84
168
|
# Deploy the prebuilt bundle with per-scope vars (flags override the toml).
|
|
85
169
|
wrangler deploy \
|
|
86
170
|
--name "$worker_name" \
|
|
87
|
-
--config "$
|
|
171
|
+
--config "$config_file" \
|
|
88
172
|
--no-bundle \
|
|
89
173
|
--var "ENVIRONMENT:$environment" \
|
|
90
174
|
--var "AUTH_SCOPE:$scope" \
|
|
91
175
|
--var "ROUTE_PREFIX:$route_prefix" \
|
|
92
176
|
--var "ALLOWED_ORIGINS:$allowed_origins" \
|
|
177
|
+
--var "ALLOWED_ORIGIN_SUFFIXES:$origin_suffixes" \
|
|
93
178
|
--var "JWT_ISSUER:$jwt_issuer" \
|
|
94
179
|
--var "JWT_ALLOWED_ALGORITHMS:$jwt_alg" \
|
|
95
180
|
--var "SERVICE_ROUTE_PREFIX:$svc_route" \
|
|
@@ -97,6 +182,9 @@ deploy_gateway() {
|
|
|
97
182
|
--var "SERVICE_JWT_ISSUER:$svc_iss" \
|
|
98
183
|
--var "SERVICE_JWT_ALLOWED_ALGORITHMS:$svc_alg" >/dev/null
|
|
99
184
|
|
|
185
|
+
# Clean up the temp config (only created when KV was bound or public URL disabled).
|
|
186
|
+
[[ "$config_file" != "$WRANGLER_DIR/wrangler.toml" ]] && rm -f "$config_file"
|
|
187
|
+
|
|
100
188
|
# Secrets (piped via stdin).
|
|
101
189
|
put_secret "$worker_name" BACKEND_URL "$backend_url"
|
|
102
190
|
put_secret "$worker_name" BACKEND_GATEWAY_SECRET "${LAMBDA_GATEWAY_SECRET:-}"
|
|
@@ -108,6 +196,10 @@ deploy_gateway() {
|
|
|
108
196
|
if [[ -n "$custom_domain" ]]; then
|
|
109
197
|
url="https://$custom_domain"
|
|
110
198
|
info " custom domain: $custom_domain (configure DNS/route separately)"
|
|
199
|
+
elif [[ -n "$disable_public_url" ]]; then
|
|
200
|
+
# workers.dev + preview URLs were turned off in the deploy config above; the worker
|
|
201
|
+
# is reachable only via its Service Binding (Pages proxy) — no public URL to print.
|
|
202
|
+
url="(no public URL — reached via Pages proxy / Service Binding)"
|
|
111
203
|
else
|
|
112
204
|
enable_workers_dev "$worker_name"
|
|
113
205
|
if [[ -n "${CF_WORKERS_SUBDOMAIN:-}" ]]; then
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@auth-craft/aws-cf-stack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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"
|