@auth-craft/aws-cf-stack 1.6.4 → 1.7.1

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 CHANGED
@@ -34,8 +34,11 @@ LAMBDA_JWT_PRIVATE_KEY= # required — Ed25519 private key (hex or app's f
34
34
  LAMBDA_JWT_PUBLIC_KEY= # required — Ed25519 public key (same pair); fed to workers too
35
35
  LAMBDA_JWT_ALGORITHM=EdDSA # optional, default EdDSA
36
36
  LAMBDA_JWT_ISSUER= # optional — overrides AUTH_SYSTEM_NAME as iss (must match worker verify)
37
- LAMBDA_JWT_ACCESS_TOKEN_AUDIENCE=access # optional, default access
38
- LAMBDA_JWT_REFRESH_TOKEN_AUDIENCE=refresh # optional, default refresh
37
+ LAMBDA_JWT_ACCESS_TOKEN_AUDIENCE=access # optional, default access — legacy single-aud fallback
38
+ LAMBDA_JWT_REFRESH_TOKEN_AUDIENCE=refresh # optional, default refresh — legacy single-aud fallback
39
+ # NOTE: tokens now carry a CONTEXT-DEPENDENT aud (customer/tenant/system); see
40
+ # LAMBDA_TOKEN_AUD_* below. Verification accepts all three, so the two values
41
+ # above are only the fallback when the per-scope audiences are unset.
39
42
 
40
43
  # ─── Gateway secret (Worker → Lambda; the backend's only direct-access guard) ─
41
44
  LAMBDA_GATEWAY_SECRET= # required for staging/prod (flag --gateway-secret)
@@ -132,6 +135,16 @@ LAMBDA_LOCKOUT_DURATION_MINUTES=30
132
135
  LAMBDA_REQUIRE_VERIFIED_IDENTITY=false
133
136
  LAMBDA_VERIFY_SESSION_ON_AUTH=false
134
137
  LAMBDA_SYSTEM_TENANT_ID=__system__
138
+ # Context-dependent JWT audience. Values MUST equal the gateway AUTH_SCOPE names
139
+ # (customer/tenant/system) so the gateway can enforce scope-binding (token aud must
140
+ # match the route's scope). Change these only if you also change AUTH_SCOPE.
141
+ LAMBDA_TOKEN_AUD_CUSTOMER=customer
142
+ LAMBDA_TOKEN_AUD_TENANT=tenant
143
+ LAMBDA_TOKEN_AUD_SYSTEM=system
144
+ # Per-scope refresh-token cookie names (browser isolation across scopes).
145
+ LAMBDA_REFRESH_COOKIE_CUSTOMER=refresh_token
146
+ LAMBDA_REFRESH_COOKIE_TENANT=refresh_token_tenant
147
+ LAMBDA_REFRESH_COOKIE_SYSTEM=refresh_token_system
135
148
  LAMBDA_AUDIT_ENABLED=false
136
149
  LAMBDA_DEVICE_TRACKING=true
137
150
  LAMBDA_APP_URL=
@@ -189,7 +189,7 @@ Expecting one of '${n.join("', '")}'`);let i=`${e}Help`;return this.on(i,s=>{let
189
189
  `)+`
190
190
  -----END PRIVATE KEY-----`;return fm(s,"EdDSA")}async function rye(t){let e=t.replace(/^0x/,""),r=j5(e),n=iye(r);return In({kty:"OKP",crv:"Ed25519",x:n},"EdDSA")}async function Kv(t,e){if(e==="EdDSA"){let r=t.trim();if(r.startsWith("{"))return In(JSON.parse(r),e);if(!r.includes(`
191
191
  `)&&!r.startsWith("-----")){try{let n=atob(r);if(n.trim().startsWith("{"))return In(JSON.parse(n),e)}catch{}if(/^[0-9a-fA-F]{64}$/.test(r))return tye(r)}if(r.startsWith("-----"))return fm(r,e)}throw new Error(`Unsupported key format or algorithm: ${e}`)}async function W5(t,e){if(e==="EdDSA"){let r=t.trim();if(r.startsWith("{"))return In(JSON.parse(r),e);if(!r.includes(`
192
- `)&&!r.startsWith("-----")){try{let n=atob(r);if(n.trim().startsWith("{"))return In(JSON.parse(n),e)}catch{}if(/^[0-9a-fA-F]{64}$/.test(r))return rye(r)}if(r.startsWith("-----")){let{importSPKI:n}=await Promise.resolve().then(()=>(Bv(),V5));return n(r,e)}}throw new Error(`Unsupported key format or algorithm: ${e}`)}async function nye(t){return new TextEncoder().encode(t)}function j5(t){let e=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)e[r/2]=parseInt(t.slice(r,r+2),16);return e}function iye(t){return btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}var TA=class{name="jwt-token-plugin";version="1.0.0";config;keys=null;keysPromise=null;constructor(e){this.config={...e}}async ensureKeys(){return this.keys?this.keys:this.keysPromise?this.keysPromise:(this.keysPromise=this.resolveKeys(),this.keys=await this.keysPromise,this.keys)}async resolveKeys(){let e=this.config.algorithm;if(e==="HS256"||e==="HS384"||e==="HS512"){if(!this.config.secret)throw new Error("Secret is required for HMAC algorithms");let a=await nye(this.config.secret);return{privateKey:a,publicKey:a,servicePrivateKey:a,servicePublicKey:a}}if(!this.config.privateKey)throw new Error("Private key is required for EdDSA algorithm");if(!this.config.publicKey)throw new Error("Public key is required for EdDSA algorithm (cannot be derived via Web Crypto)");let n=await Kv(this.config.privateKey,e),i=await W5(this.config.publicKey,e),s,o;return this.config.servicePrivateKey&&this.config.servicePublicKey?(s=await Kv(this.config.servicePrivateKey,e),o=await W5(this.config.servicePublicKey,e)):this.config.servicePrivateKey?(s=await Kv(this.config.servicePrivateKey,e),o=i):(s=n,o=i),{privateKey:n,publicKey:i,servicePrivateKey:s,servicePublicKey:o}}buildBasePayload(e,r,n){let i=Math.floor(Date.now()/1e3),s=i+r*60,o=e.aud??(n?this.config.refreshTokenAudience??this.config.audience:this.config.accessTokenAudience??this.config.audience),a={sub:e.sub,aud:o??"",iss:this.config.issuer??"",jti:e.jti,sid:e.sid,typ:n?H5:Fv,iat:i,exp:s};return e.tid!==void 0&&(a.tid=e.tid),e.tty!==void 0&&(a.tty=e.tty),a}async signJwt(e,r){let n=this.config.algorithm;return new Ul(e).setProtectedHeader({alg:n,typ:"JWT"}).sign(r)}async verifyJwt(e,r,n){try{let i=this.config.algorithm,s=this.config.clockTolerance??0,o={algorithms:[i],clockTolerance:s};this.config.issuer&&(o.issuer=this.config.issuer),n&&(o.audience=n);let{payload:a}=await gA(e,r,o);return a}catch{return null}}async sign(e,r){let n=await this.ensureKeys(),i=r??this.config.accessTokenExpiresIn,s=this.buildBasePayload(e,i,!1);return this.signJwt(s,n.privateKey)}async verify(e){let r=await this.ensureKeys(),n=this.config.accessTokenAudience??this.config.audience,i=await this.verifyJwt(e,r.publicKey,n);return!i||i.typ!==Fv?null:i}async signAccessToken(e,r){let n=await this.ensureKeys(),i=r??this.config.accessTokenExpiresIn,s=this.buildBasePayload(e,i,!1),o={...s};return e.roles!==void 0&&(o.roles=e.roles),e.perms!==void 0&&(o.perms=e.perms),{token:await this.signJwt(o,n.privateKey),expiresAt:s.exp}}async verifyAccessToken(e){let r=await this.ensureKeys(),n=this.config.accessTokenAudience??this.config.audience,i=await this.verifyJwt(e,r.publicKey,n);return!i||i.typ!==Fv||!("jti"in i)?null:i}async signRefreshToken(e,r){let n=await this.ensureKeys(),i=r??this.config.refreshTokenExpiresIn,s=this.buildBasePayload(e,i,!0);return{token:await this.signJwt(s,n.privateKey),expiresAt:s.exp}}async verifyRefreshToken(e){let r=await this.ensureKeys(),n=this.config.refreshTokenAudience??this.config.audience,i=await this.verifyJwt(e,r.publicKey,n);return!i||i.typ!==H5||!("sid"in i)||!("jti"in i)?null:i}async signServiceToken(e){if(this.config.algorithm!=="EdDSA")throw new Error("signServiceToken requires EdDSA algorithm");let r=await this.ensureKeys(),n=Math.floor(Date.now()/1e3),i=this.config.serviceTokenExpiresIn??10,s=e.aud??this.config.serviceTokenAudience??q5,o={sub:e.sub,aud:s,iss:this.config.issuer??"",jti:e.jti,typ:G5,iat:n,exp:n+i*60};return e.roles!==void 0&&(o.roles=e.roles),this.signJwt(o,r.servicePrivateKey)}async verifyServiceToken(e){if(this.config.algorithm!=="EdDSA")return null;let r=await this.ensureKeys(),n=this.config.serviceTokenAudience??q5,i=await this.verifyJwt(e,r.servicePublicKey,n);return!i||i.typ!==G5?null:i}async initialize(){}async cleanup(){}};function Vv(t){return new TA(t)}var rr={INVALID_EMAIL:{code:"INVALID_EMAIL",message:"Invalid email address format",status:400},WEAK_PASSWORD:{code:"WEAK_PASSWORD",message:"Password must be at least 8 characters",status:400},SUPER_ADMIN_EXISTS:{code:"SUPER_ADMIN_EXISTS",message:"Super admin already exists in the system tenant. Use --force to override (not recommended).",status:409},EXISTENCE_CHECK_FAILED:t=>({code:"EXISTENCE_CHECK_FAILED",message:t?`Could not verify whether a super admin already exists: ${t}. Aborting to avoid creating a duplicate.`:"Could not verify whether a super admin already exists. Aborting to avoid creating a duplicate.",status:503}),MISSING_DATABASE_CONFIG:t=>({code:"MISSING_DATABASE_CONFIG",message:`Missing required configuration for ${t} database`,status:400}),REGISTRATION_FAILED:t=>({code:"REGISTRATION_FAILED",message:t?`Registration failed: ${t}`:"Failed to register user",status:500}),VERIFICATION_FAILED:{code:"VERIFICATION_FAILED",message:"Failed to mark email as verified",status:500},TENANT_CREATE_FAILED:{code:"TENANT_CREATE_FAILED",message:"Failed to create system tenant",status:500},TENANT_MEMBER_FAILED:{code:"TENANT_MEMBER_FAILED",message:"Failed to create tenant member",status:500},TENANT_PROFILE_FAILED:{code:"TENANT_PROFILE_FAILED",message:"Failed to create tenant profile",status:500},SDK_INIT_FAILED:t=>({code:"SDK_INIT_FAILED",message:t?`SDK initialization failed: ${t}`:"Failed to initialize SDK",status:500})},z5=["user_profiles","user_auths","identities","credentials","tenants","tenant_members","user_tenant_profiles"],sye={user_profiles:["id","contact_email","display_name","status","created_at","updated_at"],user_auths:["user_id","user_status","mfa_required","created_at","updated_at"],identities:["type","value","user_id","login_enabled","verified_at","guards","created_at"],credentials:["id","user_id","type","value","data","created_at"],tenants:["id","type","owner_id","created_at"],tenant_members:["tenant_id","user_id","status","roles","auth_perm_mask","is_owner","created_at"],user_tenant_profiles:["tenant_id","user_id","display_name","display_name_source","contact_email","created_at"]};function oye(t){try{let e=new URL(t);return e.password&&(e.password="****"),e.toString()}catch{return t.replace(/:([^@/]+)@/,":****@")}}async function aye(t){console.log("[DB Diagnostics] Starting database health checks..."),console.log(`[DB Diagnostics] URL: ${oye(t)}`);let e=new mh({connectionString:t,max:1,connectionTimeoutMillis:5e3});try{console.log("[DB Diagnostics] Testing connection...");let r=await e.connect(),i=(await r.query("SELECT version()")).rows[0];console.log(`[DB Diagnostics] Connected! ${i?.version?.split(",")[0]??"unknown"}`);let o=(await r.query("SELECT current_database(), current_schema()")).rows[0];console.log(`[DB Diagnostics] Database: ${o?.current_database??"unknown"}, Schema: ${o?.current_schema??"unknown"}`),console.log("[DB Diagnostics] Checking required tables...");let a=await r.query(`SELECT table_name FROM information_schema.tables
192
+ `)&&!r.startsWith("-----")){try{let n=atob(r);if(n.trim().startsWith("{"))return In(JSON.parse(n),e)}catch{}if(/^[0-9a-fA-F]{64}$/.test(r))return rye(r)}if(r.startsWith("-----")){let{importSPKI:n}=await Promise.resolve().then(()=>(Bv(),V5));return n(r,e)}}throw new Error(`Unsupported key format or algorithm: ${e}`)}async function nye(t){return new TextEncoder().encode(t)}function j5(t){let e=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)e[r/2]=parseInt(t.slice(r,r+2),16);return e}function iye(t){return btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}var TA=class{name="jwt-token-plugin";version="1.0.0";config;keys=null;keysPromise=null;constructor(e){this.config={...e}}async ensureKeys(){return this.keys?this.keys:this.keysPromise?this.keysPromise:(this.keysPromise=this.resolveKeys(),this.keys=await this.keysPromise,this.keys)}async resolveKeys(){let e=this.config.algorithm;if(e==="HS256"||e==="HS384"||e==="HS512"){if(!this.config.secret)throw new Error("Secret is required for HMAC algorithms");let a=await nye(this.config.secret);return{privateKey:a,publicKey:a,servicePrivateKey:a,servicePublicKey:a}}if(!this.config.privateKey)throw new Error("Private key is required for EdDSA algorithm");if(!this.config.publicKey)throw new Error("Public key is required for EdDSA algorithm (cannot be derived via Web Crypto)");let n=await Kv(this.config.privateKey,e),i=await W5(this.config.publicKey,e),s,o;return this.config.servicePrivateKey&&this.config.servicePublicKey?(s=await Kv(this.config.servicePrivateKey,e),o=await W5(this.config.servicePublicKey,e)):this.config.servicePrivateKey?(s=await Kv(this.config.servicePrivateKey,e),o=i):(s=n,o=i),{privateKey:n,publicKey:i,servicePrivateKey:s,servicePublicKey:o}}buildBasePayload(e,r,n){let i=Math.floor(Date.now()/1e3),s=i+r*60,o=e.aud??(n?this.config.refreshTokenAudience??this.config.audience:this.config.accessTokenAudience??this.config.audience),a={sub:e.sub,aud:o??"",iss:this.config.issuer??"",jti:e.jti,sid:e.sid,typ:n?H5:Fv,iat:i,exp:s};return e.tid!==void 0&&(a.tid=e.tid),e.tty!==void 0&&(a.tty=e.tty),a}async signJwt(e,r){let n=this.config.algorithm;return new Ul(e).setProtectedHeader({alg:n,typ:"JWT"}).sign(r)}async verifyJwt(e,r,n){try{let i=this.config.algorithm,s=this.config.clockTolerance??0,o={algorithms:[i],clockTolerance:s};this.config.issuer&&(o.issuer=this.config.issuer),n&&(typeof n=="string"||n.length>0)&&(o.audience=n);let{payload:a}=await gA(e,r,o);return a}catch{return null}}async sign(e,r){let n=await this.ensureKeys(),i=r??this.config.accessTokenExpiresIn,s=this.buildBasePayload(e,i,!1);return this.signJwt(s,n.privateKey)}accessVerifyAudience(){return this.config.accessTokenAudiences&&this.config.accessTokenAudiences.length>0?this.config.accessTokenAudiences:this.config.accessTokenAudience??this.config.audience}refreshVerifyAudience(){return this.config.refreshTokenAudiences&&this.config.refreshTokenAudiences.length>0?this.config.refreshTokenAudiences:this.config.refreshTokenAudience??this.config.audience}async verify(e){let r=await this.ensureKeys(),n=await this.verifyJwt(e,r.publicKey,this.accessVerifyAudience());return!n||n.typ!==Fv?null:n}async signAccessToken(e,r){let n=await this.ensureKeys(),i=r??this.config.accessTokenExpiresIn,s=this.buildBasePayload(e,i,!1),o={...s};return e.roles!==void 0&&(o.roles=e.roles),e.perms!==void 0&&(o.perms=e.perms),{token:await this.signJwt(o,n.privateKey),expiresAt:s.exp}}async verifyAccessToken(e){let r=await this.ensureKeys(),n=await this.verifyJwt(e,r.publicKey,this.accessVerifyAudience());return!n||n.typ!==Fv||!("jti"in n)?null:n}async signRefreshToken(e,r){let n=await this.ensureKeys(),i=r??this.config.refreshTokenExpiresIn,s=this.buildBasePayload(e,i,!0);return{token:await this.signJwt(s,n.privateKey),expiresAt:s.exp}}async verifyRefreshToken(e){let r=await this.ensureKeys(),n=await this.verifyJwt(e,r.publicKey,this.refreshVerifyAudience());return!n||n.typ!==H5||!("sid"in n)||!("jti"in n)?null:n}async signServiceToken(e){if(this.config.algorithm!=="EdDSA")throw new Error("signServiceToken requires EdDSA algorithm");let r=await this.ensureKeys(),n=Math.floor(Date.now()/1e3),i=this.config.serviceTokenExpiresIn??10,s=e.aud??this.config.serviceTokenAudience??q5,o={sub:e.sub,aud:s,iss:this.config.issuer??"",jti:e.jti,typ:G5,iat:n,exp:n+i*60};return e.roles!==void 0&&(o.roles=e.roles),this.signJwt(o,r.servicePrivateKey)}async verifyServiceToken(e){if(this.config.algorithm!=="EdDSA")return null;let r=await this.ensureKeys(),n=this.config.serviceTokenAudience??q5,i=await this.verifyJwt(e,r.servicePublicKey,n);return!i||i.typ!==G5?null:i}async initialize(){}async cleanup(){}};function Vv(t){return new TA(t)}var rr={INVALID_EMAIL:{code:"INVALID_EMAIL",message:"Invalid email address format",status:400},WEAK_PASSWORD:{code:"WEAK_PASSWORD",message:"Password must be at least 8 characters",status:400},SUPER_ADMIN_EXISTS:{code:"SUPER_ADMIN_EXISTS",message:"Super admin already exists in the system tenant. Use --force to override (not recommended).",status:409},EXISTENCE_CHECK_FAILED:t=>({code:"EXISTENCE_CHECK_FAILED",message:t?`Could not verify whether a super admin already exists: ${t}. Aborting to avoid creating a duplicate.`:"Could not verify whether a super admin already exists. Aborting to avoid creating a duplicate.",status:503}),MISSING_DATABASE_CONFIG:t=>({code:"MISSING_DATABASE_CONFIG",message:`Missing required configuration for ${t} database`,status:400}),REGISTRATION_FAILED:t=>({code:"REGISTRATION_FAILED",message:t?`Registration failed: ${t}`:"Failed to register user",status:500}),VERIFICATION_FAILED:{code:"VERIFICATION_FAILED",message:"Failed to mark email as verified",status:500},TENANT_CREATE_FAILED:{code:"TENANT_CREATE_FAILED",message:"Failed to create system tenant",status:500},TENANT_MEMBER_FAILED:{code:"TENANT_MEMBER_FAILED",message:"Failed to create tenant member",status:500},TENANT_PROFILE_FAILED:{code:"TENANT_PROFILE_FAILED",message:"Failed to create tenant profile",status:500},SDK_INIT_FAILED:t=>({code:"SDK_INIT_FAILED",message:t?`SDK initialization failed: ${t}`:"Failed to initialize SDK",status:500})},z5=["user_profiles","user_auths","identities","credentials","tenants","tenant_members","user_tenant_profiles"],sye={user_profiles:["id","contact_email","display_name","status","created_at","updated_at"],user_auths:["user_id","user_status","mfa_required","created_at","updated_at"],identities:["type","value","user_id","login_enabled","verified_at","guards","created_at"],credentials:["id","user_id","type","value","data","created_at"],tenants:["id","type","owner_id","created_at"],tenant_members:["tenant_id","user_id","status","roles","auth_perm_mask","is_owner","created_at"],user_tenant_profiles:["tenant_id","user_id","display_name","display_name_source","contact_email","created_at"]};function oye(t){try{let e=new URL(t);return e.password&&(e.password="****"),e.toString()}catch{return t.replace(/:([^@/]+)@/,":****@")}}async function aye(t){console.log("[DB Diagnostics] Starting database health checks..."),console.log(`[DB Diagnostics] URL: ${oye(t)}`);let e=new mh({connectionString:t,max:1,connectionTimeoutMillis:5e3});try{console.log("[DB Diagnostics] Testing connection...");let r=await e.connect(),i=(await r.query("SELECT version()")).rows[0];console.log(`[DB Diagnostics] Connected! ${i?.version?.split(",")[0]??"unknown"}`);let o=(await r.query("SELECT current_database(), current_schema()")).rows[0];console.log(`[DB Diagnostics] Database: ${o?.current_database??"unknown"}, Schema: ${o?.current_schema??"unknown"}`),console.log("[DB Diagnostics] Checking required tables...");let a=await r.query(`SELECT table_name FROM information_schema.tables
193
193
  WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
194
194
  ORDER BY table_name`),c=new Set(a.rows.map(p=>p.table_name)),u=!0;for(let p of z5)c.has(p)?console.log(`[DB Diagnostics] \u2713 ${p}`):(console.error(`[DB Diagnostics] \u2717 ${p} \u2014 MISSING`),u=!1);if(!u)return console.error("[DB Diagnostics] Some tables are missing! Run schema.sql first."),r.release(),!1;console.log("[DB Diagnostics] Checking table columns...");for(let[p,d]of Object.entries(sye)){let h=await r.query(`SELECT column_name FROM information_schema.columns
195
195
  WHERE table_schema = 'public' AND table_name = $1
@@ -1 +1 @@
1
- var Et=typeof process<"u"&&!1;function y(t){return Et?Object.freeze(t):t}function m(t,e){if(arguments.length===0)return y({ok:!0,data:void 0});let n={ok:!0,data:t};return e!==void 0&&(n.meta=e),y(n)}function a(t,e){let r={ok:!1,errors:y(Array.isArray(t)?[...t]:[t])};return e!==void 0&&(r.meta=e),y(r)}function E(t,e){return n=>{let r={code:t,message:n?.message??e};return n?.field&&(r.field=n.field),r}}function N(t){return t.ok?t.data===void 0?t.meta?{ok:!0,meta:t.meta}:{ok:!0}:t.meta?{ok:!0,data:t.data,meta:t.meta}:{ok:!0,data:t.data}:t.meta?{ok:!1,errors:t.errors,meta:t.meta}:{ok:!1,errors:t.errors}}function v(t){let e=["BACKEND_URL","BACKEND_GATEWAY_SECRET","JWT_PUBLIC_KEY","SERVICE_JWT_PUBLIC_KEY"];U(t)||e.push("EDGE_GATE_SECRET");let n=e.filter(r=>!t[r]);if(n.length>0)throw new Error(`Missing required environment variables: ${n.join(", ")}`)}function U(t){return t.SKIP_EDGE_GATE==="true"}function pt(t){return t.ENVIRONMENT||"development"}function S(t){return pt(t)==="development"}function W(t){return t.ALLOWED_ORIGINS?t.ALLOWED_ORIGINS.split(",").map(e=>e.trim()).filter(Boolean):["*"]}function gt(t){return t.ALLOWED_ORIGIN_SUFFIXES?t.ALLOWED_ORIGIN_SUFFIXES.split(",").map(e=>e.trim().toLowerCase()).filter(Boolean):[]}function P(t){return`origin:${t}`}async function H(t,e){if(!t)return!1;if(W(e).includes(t))return!0;let r=gt(e);if(r.length>0){let o;try{o=new URL(t).host.toLowerCase()}catch{return!1}if(r.some(i=>o===i.replace(/^\./,"")||o.endsWith(i)))return!0}return!!(e.CORS_KV&&await e.CORS_KV.get(P(t))!==null)}function O(t){let e=t.ROUTE_PREFIX||"";if(!e)return"";let n=e.startsWith("/")?e:`/${e}`;return n.endsWith("/")?n.slice(0,-1):n}function C(t,e){let n=O(e);return n?t===n?"/":t.startsWith(`${n}/`)?t.slice(n.length):t:t}var At=new Set(["system","tenant","customer"]);function L(t){return t&&At.has(t)?t:void 0}function F(t,e){let n=L(t.AUTH_SCOPE);if(n)return{scope:n,forwardPath:e};let r=L(O(t).replace(/^\//,""));if(r)return{scope:r,forwardPath:e};let o=e.split("/").filter(Boolean),i=L(o[0]);if(i){let s=e.slice(e.indexOf(o[0])+o[0].length);return{scope:i,forwardPath:s||"/"}}return{scope:void 0,forwardPath:e}}function X(t,e){let n=O(e);return n?t===n||t.startsWith(`${n}/`):!0}function $(t){if(!t)return new Set(["EdDSA"]);let e=t.split(",").map(n=>n.trim()).filter(Boolean);return e.length>0?new Set(e):new Set(["EdDSA"])}function Y(t){return $(t.JWT_ALLOWED_ALGORITHMS)}function j(t){return $(t.SERVICE_JWT_ALLOWED_ALGORITHMS)}function ht(t){let e=t.SERVICE_ROUTE_PREFIX||"/internal",n=e.startsWith("/")?e:`/${e}`;return n.endsWith("/")?n.slice(0,-1):n}function z(t){return t.SERVICE_JWT_AUDIENCE||"auth-craft:internal"}function Q(t){return t.JWT_ISSUER||void 0}function Z(t){return t.SERVICE_JWT_ISSUER||void 0}function q(t,e){let n=t.split("?")[0].replace(/\/$/,"")||"/",r=ht(e);return n===r||n.startsWith(`${r}/`)}var mt=new Set(["authenticate","login","register","refresh","logout","complete-challenge","complete-mfa-challenge","issue-mfa-challenge","forgot-password","reset-password","verify-email","resend-verification"]),M=new Set(["/verification/resend","/verification/verify","/password/forgot","/password/reset"]);function tt(t){let e=t.split("?")[0].replace(/\/$/,"")||"/";if(e==="/"||e.startsWith("/oauth/")||e==="/oauth"||M.has(e))return!0;let n=e.match(/^\/[^/]+(\/(?:verification|password)\/[^/]+)$/);if(n?.[1]&&M.has(n[1]))return!0;let r=e.match(/^(?:\/[^/]+)?\/auth\/([^/]+)/);if(r){let o=r[1];if(o&&mt.has(o)||o?.startsWith("oauth"))return!0}return!1}function et(t){return t.GATEWAY_JWT_PRIVATE_KEY||void 0}function rt(t){return t.TID_VALUE||void 0}function nt(t){let e=t.split("?")[0].replace(/\/$/,"");return e.endsWith("/auth/login")||e.endsWith("/auth/authenticate")}function Rt(){let t=Date.now(),e=new Uint8Array(16);e[0]=t/1099511627776&255,e[1]=t/4294967296&255,e[2]=t/16777216&255,e[3]=t/65536&255,e[4]=t/256&255,e[5]=t&255;let n=new Uint8Array(10);crypto.getRandomValues(n),e.set(n,6),e[6]=e[6]&15|112,e[8]=e[8]&63|128;let r="0123456789ABCDEFGHJKMNPQRSTVWXYZ",o="",i=0,s=0;for(let c=0;c<e.length;c++)for(s=s<<8|e[c],i+=8;i>=5;)o+=r[s>>>i-5&31],i-=5;return i>0&&(o+=r[s<<5-i&31]),o}var T={BAD_REQUEST:E("BAD_REQUEST","Bad request"),UNAUTHORIZED:E("UNAUTHORIZED","Authentication required"),FORBIDDEN:E("FORBIDDEN","Access denied"),NOT_FOUND:E("NOT_FOUND","Resource not found"),RATE_LIMIT_EXCEEDED:E("RATE_LIMIT_EXCEEDED","Too many requests"),INTERNAL_ERROR:E("INTERNAL_ERROR","Internal server error"),BAD_GATEWAY:E("BAD_GATEWAY","Gateway error"),SERVICE_UNAVAILABLE:E("SERVICE_UNAVAILABLE","Service temporarily unavailable"),GATEWAY_TIMEOUT:E("GATEWAY_TIMEOUT","Gateway timeout")},d={INVALID_FORMAT:E("JWT_INVALID_FORMAT","Invalid JWT format"),UNSUPPORTED_ALGORITHM:E("JWT_UNSUPPORTED_ALGORITHM","Unsupported JWT algorithm"),INVALID_TOKEN_TYPE:E("JWT_INVALID_TOKEN_TYPE","Invalid token type: expected access token"),TOKEN_EXPIRED:E("JWT_TOKEN_EXPIRED","JWT token expired"),SIGNATURE_INVALID:E("JWT_SIGNATURE_INVALID","JWT signature verification failed"),MISSING_TOKEN:E("JWT_MISSING_TOKEN","Missing access token"),INVALID_ISSUER:E("JWT_INVALID_ISSUER","Invalid JWT issuer")},ot={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,RATE_LIMIT_EXCEEDED:429,INTERNAL_ERROR:500,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504,JWT_INVALID_FORMAT:401,JWT_UNSUPPORTED_ALGORITHM:401,JWT_INVALID_TOKEN_TYPE:401,JWT_TOKEN_EXPIRED:401,JWT_SIGNATURE_INVALID:401,JWT_MISSING_TOKEN:401,JWT_INVALID_ISSUER:401};function St(t){if(t.ok)return t.data===void 0?204:200;let e=t.errors?.[0];return e?.code&&e.code in ot?ot[e.code]:500}function f(t,e){let n=new Date().toISOString(),r=e||Rt(),o={...t.meta,timestamp:n,traceId:r},i=t.ok?m(t.data,o):a(t.errors,o),s=St(i),c=N(i);return new Response(JSON.stringify(c),{status:s,headers:{"Content-Type":"application/json"}})}var wt=new Set(["EdDSA","ES256","RS256","PS256"]);function _t(t){return wt.has(t)}function yt(t){switch(t){case"EdDSA":return{name:"Ed25519"};case"ES256":return{name:"ECDSA",namedCurve:"P-256"};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"};case"PS256":return{name:"RSA-PSS",hash:"SHA-256"}}}function It(t){switch(t){case"EdDSA":return"Ed25519";case"ES256":return{name:"ECDSA",hash:"SHA-256"};case"RS256":return{name:"RSASSA-PKCS1-v1_5"};case"PS256":return{name:"RSA-PSS",saltLength:32}}}function x(t){let e=t.replace(/-/g,"+").replace(/_/g,"/"),n=e+"==".slice(0,(4-e.length%4)%4),r=atob(n),o=new Uint8Array(r.length);for(let i=0;i<r.length;i++)o[i]=r.charCodeAt(i);return o}function it(t){return atob(t)}var Ot=/^[0-9a-fA-F]{64}$/;async function Dt(t,e){if(Ot.test(t)){if(e!=="EdDSA")throw new Error(`Hex key format is only supported for EdDSA, got ${e}`);let r=new Uint8Array(32);for(let o=0;o<32;o++)r[o]=parseInt(t.slice(o*2,o*2+2),16);return crypto.subtle.importKey("raw",r,{name:"Ed25519"},!1,["verify"])}let n;try{n=JSON.parse(it(t))}catch{throw new Error(`Invalid JWK base64 for algorithm ${e}`)}return await crypto.subtle.importKey("jwk",n,yt(e),!1,["verify"])}function st(t){let e=t.split(".");if(e.length!==3)return null;let n=e[1];if(!n)return null;try{let r=x(n);return JSON.parse(new TextDecoder().decode(r))}catch{return null}}function Pt(t){let n=t.split(".")[0];if(!n)return a(d.INVALID_FORMAT());try{let r=x(n);return m(JSON.parse(new TextDecoder().decode(r)))}catch{return a(d.INVALID_FORMAT())}}function Ct(t){let n=t.split(".")[1];if(!n)return a(d.INVALID_FORMAT());try{let r=x(n);return m(JSON.parse(new TextDecoder().decode(r)))}catch{return a(d.INVALID_FORMAT())}}async function xt(t,e,n){let r=t.split("."),o=r[0],i=r[1],s=r[2];if(!o||!i||!s)return!1;let c=new TextEncoder().encode(`${o}.${i}`),u=x(s);try{return await crypto.subtle.verify(It(n),e,u,c)}catch{return!1}}async function b(t,e,n=1,r=new Set(["EdDSA"]),o){let i=Pt(t);if(!i.ok)return a(i.errors,i.meta);let s=i.data.alg;if(!r.has(s))return a(d.UNSUPPORTED_ALGORITHM());if(!_t(s))return a(d.UNSUPPORTED_ALGORITHM());let c=Ct(t);if(!c.ok)return a(c.errors,c.meta);let u=c.data;if(u.typ!==n)return a(d.INVALID_TOKEN_TYPE());if(o!==void 0&&u.iss!==o)return a(d.INVALID_ISSUER());if(u.exp&&Math.floor(Date.now()/1e3)>=u.exp)return a(d.TOKEN_EXPIRED());let l;try{l=await Dt(e,s)}catch{return a(d.INVALID_FORMAT())}return await xt(t,l,s)?m(u):a(d.SIGNATURE_INVALID())}var J=null;async function Nt(t){let e;try{e=JSON.parse(it(t))}catch{throw new Error("Invalid Gateway JWT private key: expected base64-encoded JWK")}return await crypto.subtle.importKey("jwk",e,{name:"Ed25519"},!1,["sign"])}function k(t){let e="";for(let n=0;n<t.length;n++)e+=String.fromCharCode(t[n]);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function at(t,e,n,r,o=30){J||(J=Nt(t));let i=await J,s=Math.floor(Date.now()/1e3),c={alg:"EdDSA",typ:"JWT"},u={iss:e,aud:n,sub:r,iat:s,exp:s+o,jti:crypto.randomUUID()},l=k(new TextEncoder().encode(JSON.stringify(c))),A=k(new TextEncoder().encode(JSON.stringify(u))),R=new TextEncoder().encode(`${l}.${A}`),h=await crypto.subtle.sign("Ed25519",i,R),w=k(new Uint8Array(h));return`${l}.${A}.${w}`}var Lt=["content-type","accept","accept-encoding","accept-language","user-agent","cookie"];function Ut(t){return t.getSetCookie?.()??[]}async function Wt(t,e){if(!(t.headers.get("content-type")||"").includes("application/json"))return null;let r;try{r=await t.clone().json()}catch{return null}return delete r.tid,r.tid=e,{body:JSON.stringify(r),contentType:"application/json"}}function Jt(t,e,n,r,o,i){let s=new Headers;for(let R of Lt){let h=t.headers.get(R);h&&s.set(R,h)}o&&s.set("content-type",o);let c=t.headers.get("Authorization");c&&s.set("Authorization",c),r&&s.set("X-Auth-Context",r),s.set("X-Gateway-Secret",e),i&&s.set("x-gateway-auth",`Bearer ${i}`),s.set("X-Request-Id",n);let u=t.headers.get("cf-connecting-ip");u&&(s.set("X-Forwarded-For",u),s.set("X-Real-IP",u)),s.set("X-Forwarded-Proto","https");let l=new URL(t.url);s.set("X-Forwarded-Host",l.hostname);let A=t.headers.get("cf-ipcountry");return A&&s.set("X-Cloudflare-Country",A),s}function kt(t,e,n){let r=new URL(t.url),o=e.BACKEND_URL.endsWith("/")?e.BACKEND_URL.slice(0,-1):e.BACKEND_URL;S(e)&&console.log("[Gateway] Path transform:",JSON.stringify({original:r.pathname,forwarded:n}));let i=new URL(`${o}${n}`);return i.search=r.search,i.toString()}function bt(t,e,n){let r=new Headers(t.headers);r.set("X-Request-Id",e);let o=Ut(t.headers);if(o.length>0){r.delete("Set-Cookie");for(let i of o)r.append("Set-Cookie",i);S(n)&&console.log("[Gateway] Forwarding Set-Cookie:",o.length,"cookie(s)")}return new Response(t.body,{status:t.status,statusText:t.statusText,headers:r})}async function G(t,e){let n=crypto.randomUUID();try{let r=new URL(t.url),o=C(r.pathname,e),{scope:i,forwardPath:s}=F(e,o),c=kt(t,e,s),u,l,A=rt(e);if(A&&nt(s)&&t.method==="POST"){let I=await Wt(t,A);I&&(u=I.body,l=I.contentType,S(e)&&console.log("[Gateway] Injected tid:",A,"into",s))}let R,h=et(e);h&&(R=await at(h,"bff-worker","auth-craft:backend","cloudflare-auth-gateway"));let w=Jt(t,e.BACKEND_GATEWAY_SECRET,n,i,l,R),V=u!==void 0?u:t.body?await t.clone().arrayBuffer():void 0,_=await fetch(c,{method:t.method,headers:w,body:V});return bt(_,n,e)}catch(r){console.error("[Gateway] Proxy error:",r);let o=r instanceof Error?r.message:"Unknown error";return f(a(E("BAD_GATEWAY",`Failed to reach backend: ${o}`)()))}}var D=new Map,ct=0,Gt=[{match:t=>t==="/auth/authenticate",limit:15,windowMs:6e4,prefix:"login"},{match:t=>t==="/auth/register",limit:8,windowMs:6e4,prefix:"register"},{match:t=>t==="/password/forgot",limit:8,windowMs:3e5,prefix:"forgot"},{match:t=>t.startsWith("/oauth/"),limit:30,windowMs:6e4,prefix:"oauth"},{match:t=>t.startsWith("/auth/complete-"),limit:30,windowMs:6e4,prefix:"complete"},{match:t=>t==="/auth/issue-mfa-challenge",limit:15,windowMs:6e4,prefix:"issue-mfa"}];function Kt(t){let e=Date.now();if(e-ct<3e4)return;ct=e;let n=e-t;for(let[r,o]of D){let i=o.hits.filter(s=>s>n);i.length===0?D.delete(r):o.hits=i}}function ut(t,e,n){if(n!=="POST")return{allowed:!0,retryAfterSec:0};let r=Gt.find(l=>l.match(e));if(!r)return{allowed:!0,retryAfterSec:0};Kt(3e5);let o=`${r.prefix}:${t}`,i=Date.now(),s=i-r.windowMs,c=D.get(o)??{hits:[]},u=c.hits.filter(l=>l>s);if(u.length>=r.limit){let l=u[0]??i;return c.hits=u,D.set(o,c),{allowed:!1,retryAfterSec:Math.max(1,Math.ceil((l+r.windowMs-i)/1e3))}}return u.push(i),c.hits=u,D.set(o,c),{allowed:!0,retryAfterSec:0}}function ft(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t.charCodeAt(r)^e.charCodeAt(r);return n===0}async function lt(t,e){return t&&await H(t,e)?{"Access-Control-Allow-Origin":t,"Access-Control-Allow-Credentials":"true",Vary:"Origin"}:W(e).includes("*")?{"Access-Control-Allow-Origin":"*"}:{Vary:"Origin"}}async function Vt(t,e){let n=t.headers.get("Origin")??"*",r=t.headers.get("Access-Control-Request-Headers")??"";return new Response(null,{status:204,headers:{...await lt(n,e),"Access-Control-Allow-Methods":"GET, POST, PUT, DELETE, OPTIONS, PATCH","Access-Control-Allow-Headers":r||"Content-Type, Authorization, X-Requested-With, X-Edge-Gate","Access-Control-Max-Age":"86400"}})}async function p(t,e,n){let r=e.headers.get("Origin")??"*",o=new Response(t.body,t),i=t.headers.getSetCookie?.()??[];if(i.length>0){o.headers.delete("Set-Cookie");for(let c of i)o.headers.append("Set-Cookie",c)}let s=await lt(r,n);for(let[c,u]of Object.entries(s))o.headers.set(c,u);return o.headers.set("Access-Control-Expose-Headers","Content-Length, X-Request-Id, Set-Cookie"),o}var Bt="/internal/cors-origins";function Mt(t){return t.split("?")[0].replace(/\/$/,"")===Bt}async function vt(t,e){if(!e.CORS_KV)return K();if(t.method==="GET"){let r=(await e.CORS_KV.list({prefix:"origin:"})).keys.map(o=>o.name.slice(7));return f(m({origins:r}))}if(t.method==="PUT"||t.method==="DELETE"){let n;try{n=(await t.json()).origin}catch{return f(a(T.BAD_REQUEST()))}if(typeof n!="string"||!n)return f(a(T.BAD_REQUEST()));let r;try{if(r=new URL(n).origin,r==="null"||r!==n.replace(/\/$/,""))return f(a(T.BAD_REQUEST()))}catch{return f(a(T.BAD_REQUEST()))}return t.method==="PUT"?await e.CORS_KV.put(P(r),"1"):await e.CORS_KV.delete(P(r)),f(m({origin:r}))}return f(a(T.BAD_REQUEST()))}function K(){return new Response(null,{status:404})}var Pe={async fetch(t,e,n){try{v(e);let r=new URL(t.url);if(!X(r.pathname,e))return K();if(t.method==="OPTIONS")return await Vt(t,e);if(!U(e)){let g=t.headers.get("X-Edge-Gate");if(!g||!e.EDGE_GATE_SECRET||!ft(g,e.EDGE_GATE_SECRET))return S(e)&&console.warn("[Gateway] X-Edge-Gate missing or invalid"),K()}let o=O(e),i=o?`${o}/health`:"/health";if(r.pathname===i)return await p(f(m({status:"healthy",timestamp:new Date().toISOString()})),t,e);let s=C(r.pathname,e);S(e)&&console.log("[Gateway] Request:",t.method,r.pathname,"\u2192",s);let c=t.headers.get("cf-connecting-ip")??"unknown",u=ut(c,s,t.method);if(!u.allowed){let g=f(a(T.RATE_LIMIT_EXCEEDED()));return g.headers.set("Retry-After",String(u.retryAfterSec)),await p(g,t,e)}let l=q(s,e);if(!l&&tt(s))return await p(await G(t,e),t,e);let A=t.headers.get("Authorization");if(!A)return await p(f(a(d.MISSING_TOKEN())),t,e);let R=A.match(/^Bearer\s+(.+)$/i);if(!R)return await p(f(a(d.INVALID_FORMAT())),t,e);let h=R[1];if(h.length>4096)return await p(f(a(d.INVALID_FORMAT())),t,e);let w=h.split(".");if(w.length!==3)return await p(f(a(d.INVALID_FORMAT())),t,e);if(w[1].length>2048)return await p(f(a(d.INVALID_FORMAT())),t,e);let _=st(h);if(!_)return await p(f(a(d.INVALID_FORMAT())),t,e);let I=_.typ,dt=l?3:1;if(I!==dt)return await p(f(a(d.INVALID_TOKEN_TYPE())),t,e);if(l&&_.aud!==z(e))return await p(f(a(d.INVALID_TOKEN_TYPE())),t,e);let B=_.exp;if(typeof B=="number"&&Math.floor(Date.now()/1e3)>=B)return await p(f(a(d.TOKEN_EXPIRED())),t,e);if(l){if(!e.SERVICE_JWT_PUBLIC_KEY)return await p(f(a(T.INTERNAL_ERROR())),t,e);let g=await b(h,e.SERVICE_JWT_PUBLIC_KEY,3,j(e),Z(e));if(!g.ok)return S(e)&&console.error("[Gateway] Service JWT failed:",g.errors),await p(f(a(g.errors,g.meta)),t,e)}else{let g=await b(h,e.JWT_PUBLIC_KEY,1,Y(e),Q(e));if(!g.ok)return S(e)&&console.error("[Gateway] User JWT failed:",g.errors),await p(f(a(g.errors,g.meta)),t,e)}return l&&Mt(s)?await p(await vt(t,e),t,e):await p(await G(t,e),t,e)}catch(r){return S(e)&&console.error("[Gateway] Unhandled error:",r),f(a(T.INTERNAL_ERROR()))}}};export{Pe as default};
1
+ var gt=typeof process<"u"&&!1;function y(t){return gt?Object.freeze(t):t}function m(t,e){if(arguments.length===0)return y({ok:!0,data:void 0});let r={ok:!0,data:t};return e!==void 0&&(r.meta=e),y(r)}function a(t,e){let n={ok:!1,errors:y(Array.isArray(t)?[...t]:[t])};return e!==void 0&&(n.meta=e),y(n)}function E(t,e){return r=>{let n={code:t,message:r?.message??e};return r?.field&&(n.field=r.field),n}}function U(t){return t.ok?t.data===void 0?t.meta?{ok:!0,meta:t.meta}:{ok:!0}:t.meta?{ok:!0,data:t.data,meta:t.meta}:{ok:!0,data:t.data}:t.meta?{ok:!1,errors:t.errors,meta:t.meta}:{ok:!1,errors:t.errors}}function F(t){let e=["BACKEND_URL","BACKEND_GATEWAY_SECRET","JWT_PUBLIC_KEY","SERVICE_JWT_PUBLIC_KEY"];J(t)||e.push("EDGE_GATE_SECRET");let r=e.filter(n=>!t[n]);if(r.length>0)throw new Error(`Missing required environment variables: ${r.join(", ")}`)}function J(t){return t.SKIP_EDGE_GATE==="true"}function At(t){return t.ENVIRONMENT||"development"}function S(t){return At(t)==="development"}function k(t){return t.ALLOWED_ORIGINS?t.ALLOWED_ORIGINS.split(",").map(e=>e.trim()).filter(Boolean):["*"]}function ht(t){return t.ALLOWED_ORIGIN_SUFFIXES?t.ALLOWED_ORIGIN_SUFFIXES.split(",").map(e=>e.trim().toLowerCase()).filter(Boolean):[]}function C(t){return`origin:${t}`}async function $(t,e){if(!t)return!1;if(k(e).includes(t))return!0;let n=ht(e);if(n.length>0){let o;try{o=new URL(t).host.toLowerCase()}catch{return!1}if(n.some(i=>o===i.replace(/^\./,"")||o.endsWith(i)))return!0}return!!(e.CORS_KV&&await e.CORS_KV.get(C(t))!==null)}function O(t){let e=t.ROUTE_PREFIX||"";if(!e)return"";let r=e.startsWith("/")?e:`/${e}`;return r.endsWith("/")?r.slice(0,-1):r}function D(t,e){let r=O(e);return r?t===r?"/":t.startsWith(`${r}/`)?t.slice(r.length):t:t}var mt=new Set(["system","tenant","customer"]);function W(t){return t&&mt.has(t)?t:void 0}function x(t,e){let r=W(t.AUTH_SCOPE);if(r)return{scope:r,forwardPath:e};let n=W(O(t).replace(/^\//,""));if(n)return{scope:n,forwardPath:e};let o=e.split("/").filter(Boolean),i=W(o[0]);if(i){let s=e.slice(e.indexOf(o[0])+o[0].length);return{scope:i,forwardPath:s||"/"}}return{scope:void 0,forwardPath:e}}function X(t,e){let r=O(e);return r?t===r||t.startsWith(`${r}/`):!0}function Y(t){if(!t)return new Set(["EdDSA"]);let e=t.split(",").map(r=>r.trim()).filter(Boolean);return e.length>0?new Set(e):new Set(["EdDSA"])}function j(t){return Y(t.JWT_ALLOWED_ALGORITHMS)}function z(t){return Y(t.SERVICE_JWT_ALLOWED_ALGORITHMS)}function St(t){let e=t.SERVICE_ROUTE_PREFIX||"/internal",r=e.startsWith("/")?e:`/${e}`;return r.endsWith("/")?r.slice(0,-1):r}function Q(t){return t.SERVICE_JWT_AUDIENCE||"auth-craft:internal"}function Z(t){return t.JWT_ISSUER||void 0}function q(t){return t.SERVICE_JWT_ISSUER||void 0}function tt(t,e){let r=t.split("?")[0].replace(/\/$/,"")||"/",n=St(e);return r===n||r.startsWith(`${n}/`)}var Rt=new Set(["authenticate","login","register","refresh","logout","complete-challenge","complete-mfa-challenge","issue-mfa-challenge","forgot-password","reset-password","verify-email","resend-verification"]),v=new Set(["/verification/resend","/verification/verify","/password/forgot","/password/reset"]),Tt=new Set(["select-tenant"]);function et(t){let r=(t.split("?")[0].replace(/\/$/,"")||"/").match(/^(?:\/[^/]+)?\/auth\/([^/]+)/);return!!r?.[1]&&Tt.has(r[1])}function rt(t){let e=t.split("?")[0].replace(/\/$/,"")||"/";if(e==="/"||e.startsWith("/oauth/")||e==="/oauth"||v.has(e))return!0;let r=e.match(/^\/[^/]+(\/(?:verification|password)\/[^/]+)$/);if(r?.[1]&&v.has(r[1]))return!0;let n=e.match(/^(?:\/[^/]+)?\/auth\/([^/]+)/);if(n){let o=n[1];if(o&&Rt.has(o)||o?.startsWith("oauth"))return!0}return!1}function nt(t){return t.GATEWAY_JWT_PRIVATE_KEY||void 0}function ot(t){return t.TID_VALUE||void 0}function it(t){let e=t.split("?")[0].replace(/\/$/,"");return e.endsWith("/auth/login")||e.endsWith("/auth/authenticate")}function _t(){let t=Date.now(),e=new Uint8Array(16);e[0]=t/1099511627776&255,e[1]=t/4294967296&255,e[2]=t/16777216&255,e[3]=t/65536&255,e[4]=t/256&255,e[5]=t&255;let r=new Uint8Array(10);crypto.getRandomValues(r),e.set(r,6),e[6]=e[6]&15|112,e[8]=e[8]&63|128;let n="0123456789ABCDEFGHJKMNPQRSTVWXYZ",o="",i=0,s=0;for(let c=0;c<e.length;c++)for(s=s<<8|e[c],i+=8;i>=5;)o+=n[s>>>i-5&31],i-=5;return i>0&&(o+=n[s<<5-i&31]),o}var T={BAD_REQUEST:E("BAD_REQUEST","Bad request"),UNAUTHORIZED:E("UNAUTHORIZED","Authentication required"),FORBIDDEN:E("FORBIDDEN","Access denied"),NOT_FOUND:E("NOT_FOUND","Resource not found"),RATE_LIMIT_EXCEEDED:E("RATE_LIMIT_EXCEEDED","Too many requests"),INTERNAL_ERROR:E("INTERNAL_ERROR","Internal server error"),BAD_GATEWAY:E("BAD_GATEWAY","Gateway error"),SERVICE_UNAVAILABLE:E("SERVICE_UNAVAILABLE","Service temporarily unavailable"),GATEWAY_TIMEOUT:E("GATEWAY_TIMEOUT","Gateway timeout")},f={INVALID_FORMAT:E("JWT_INVALID_FORMAT","Invalid JWT format"),UNSUPPORTED_ALGORITHM:E("JWT_UNSUPPORTED_ALGORITHM","Unsupported JWT algorithm"),INVALID_TOKEN_TYPE:E("JWT_INVALID_TOKEN_TYPE","Invalid token type: expected access token"),TOKEN_EXPIRED:E("JWT_TOKEN_EXPIRED","JWT token expired"),SIGNATURE_INVALID:E("JWT_SIGNATURE_INVALID","JWT signature verification failed"),MISSING_TOKEN:E("JWT_MISSING_TOKEN","Missing access token"),INVALID_ISSUER:E("JWT_INVALID_ISSUER","Invalid JWT issuer"),SCOPE_MISMATCH:E("JWT_SCOPE_MISMATCH","Token not valid for this scope")},st={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,RATE_LIMIT_EXCEEDED:429,INTERNAL_ERROR:500,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504,JWT_INVALID_FORMAT:401,JWT_UNSUPPORTED_ALGORITHM:401,JWT_INVALID_TOKEN_TYPE:401,JWT_TOKEN_EXPIRED:401,JWT_SIGNATURE_INVALID:401,JWT_MISSING_TOKEN:401,JWT_INVALID_ISSUER:401,JWT_SCOPE_MISMATCH:403};function wt(t){if(t.ok)return t.data===void 0?204:200;let e=t.errors?.[0];return e?.code&&e.code in st?st[e.code]:500}function l(t,e){let r=new Date().toISOString(),n=e||_t(),o={...t.meta,timestamp:r,traceId:n},i=t.ok?m(t.data,o):a(t.errors,o),s=wt(i),c=U(i);return new Response(JSON.stringify(c),{status:s,headers:{"Content-Type":"application/json"}})}var It=new Set(["EdDSA","ES256","RS256","PS256"]);function Ot(t){return It.has(t)}function Pt(t){switch(t){case"EdDSA":return{name:"Ed25519"};case"ES256":return{name:"ECDSA",namedCurve:"P-256"};case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"};case"PS256":return{name:"RSA-PSS",hash:"SHA-256"}}}function Ct(t){switch(t){case"EdDSA":return"Ed25519";case"ES256":return{name:"ECDSA",hash:"SHA-256"};case"RS256":return{name:"RSASSA-PKCS1-v1_5"};case"PS256":return{name:"RSA-PSS",saltLength:32}}}function N(t){let e=t.replace(/-/g,"+").replace(/_/g,"/"),r=e+"==".slice(0,(4-e.length%4)%4),n=atob(r),o=new Uint8Array(n.length);for(let i=0;i<n.length;i++)o[i]=n.charCodeAt(i);return o}function at(t){return atob(t)}var Dt=/^[0-9a-fA-F]{64}$/;async function xt(t,e){if(Dt.test(t)){if(e!=="EdDSA")throw new Error(`Hex key format is only supported for EdDSA, got ${e}`);let n=new Uint8Array(32);for(let o=0;o<32;o++)n[o]=parseInt(t.slice(o*2,o*2+2),16);return crypto.subtle.importKey("raw",n,{name:"Ed25519"},!1,["verify"])}let r;try{r=JSON.parse(at(t))}catch{throw new Error(`Invalid JWK base64 for algorithm ${e}`)}return await crypto.subtle.importKey("jwk",r,Pt(e),!1,["verify"])}function ct(t){let e=t.split(".");if(e.length!==3)return null;let r=e[1];if(!r)return null;try{let n=N(r);return JSON.parse(new TextDecoder().decode(n))}catch{return null}}function Nt(t){let r=t.split(".")[0];if(!r)return a(f.INVALID_FORMAT());try{let n=N(r);return m(JSON.parse(new TextDecoder().decode(n)))}catch{return a(f.INVALID_FORMAT())}}function Lt(t){let r=t.split(".")[1];if(!r)return a(f.INVALID_FORMAT());try{let n=N(r);return m(JSON.parse(new TextDecoder().decode(n)))}catch{return a(f.INVALID_FORMAT())}}async function Ut(t,e,r){let n=t.split("."),o=n[0],i=n[1],s=n[2];if(!o||!i||!s)return!1;let c=new TextEncoder().encode(`${o}.${i}`),u=N(s);try{return await crypto.subtle.verify(Ct(r),e,u,c)}catch{return!1}}async function K(t,e,r=1,n=new Set(["EdDSA"]),o){let i=Nt(t);if(!i.ok)return a(i.errors,i.meta);let s=i.data.alg;if(!n.has(s))return a(f.UNSUPPORTED_ALGORITHM());if(!Ot(s))return a(f.UNSUPPORTED_ALGORITHM());let c=Lt(t);if(!c.ok)return a(c.errors,c.meta);let u=c.data;if(u.typ!==r)return a(f.INVALID_TOKEN_TYPE());if(o!==void 0&&u.iss!==o)return a(f.INVALID_ISSUER());if(u.exp&&Math.floor(Date.now()/1e3)>=u.exp)return a(f.TOKEN_EXPIRED());let d;try{d=await xt(e,s)}catch{return a(f.INVALID_FORMAT())}return await Ut(t,d,s)?m(u):a(f.SIGNATURE_INVALID())}var b=null;async function Wt(t){let e;try{e=JSON.parse(at(t))}catch{throw new Error("Invalid Gateway JWT private key: expected base64-encoded JWK")}return await crypto.subtle.importKey("jwk",e,{name:"Ed25519"},!1,["sign"])}function G(t){let e="";for(let r=0;r<t.length;r++)e+=String.fromCharCode(t[r]);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function ut(t,e,r,n,o=30){b||(b=Wt(t));let i=await b,s=Math.floor(Date.now()/1e3),c={alg:"EdDSA",typ:"JWT"},u={iss:e,aud:r,sub:n,iat:s,exp:s+o,jti:crypto.randomUUID()},d=G(new TextEncoder().encode(JSON.stringify(c))),A=G(new TextEncoder().encode(JSON.stringify(u))),R=new TextEncoder().encode(`${d}.${A}`),h=await crypto.subtle.sign("Ed25519",i,R),_=G(new Uint8Array(h));return`${d}.${A}.${_}`}var Jt=["content-type","accept","accept-encoding","accept-language","user-agent","cookie"];function kt(t){return t.getSetCookie?.()??[]}async function bt(t,e){if(!(t.headers.get("content-type")||"").includes("application/json"))return null;let n;try{n=await t.clone().json()}catch{return null}return delete n.tid,n.tid=e,{body:JSON.stringify(n),contentType:"application/json"}}function Gt(t,e,r,n,o,i){let s=new Headers;for(let R of Jt){let h=t.headers.get(R);h&&s.set(R,h)}o&&s.set("content-type",o);let c=t.headers.get("Authorization");c&&s.set("Authorization",c),n&&s.set("X-Auth-Context",n),s.set("X-Gateway-Secret",e),i&&s.set("x-gateway-auth",`Bearer ${i}`),s.set("X-Request-Id",r);let u=t.headers.get("cf-connecting-ip");u&&(s.set("X-Forwarded-For",u),s.set("X-Real-IP",u)),s.set("X-Forwarded-Proto","https");let d=new URL(t.url);s.set("X-Forwarded-Host",d.hostname);let A=t.headers.get("cf-ipcountry");return A&&s.set("X-Cloudflare-Country",A),s}function Kt(t,e,r){let n=new URL(t.url),o=e.BACKEND_URL.endsWith("/")?e.BACKEND_URL.slice(0,-1):e.BACKEND_URL;S(e)&&console.log("[Gateway] Path transform:",JSON.stringify({original:n.pathname,forwarded:r}));let i=new URL(`${o}${r}`);return i.search=n.search,i.toString()}function Vt(t,e,r){let n=new Headers(t.headers);n.set("X-Request-Id",e);let o=kt(t.headers);if(o.length>0){n.delete("Set-Cookie");for(let i of o)n.append("Set-Cookie",i);S(r)&&console.log("[Gateway] Forwarding Set-Cookie:",o.length,"cookie(s)")}return new Response(t.body,{status:t.status,statusText:t.statusText,headers:n})}async function V(t,e){let r=crypto.randomUUID();try{let n=new URL(t.url),o=D(n.pathname,e),{scope:i,forwardPath:s}=x(e,o),c=Kt(t,e,s),u,d,A=ot(e);if(A&&it(s)&&t.method==="POST"){let I=await bt(t,A);I&&(u=I.body,d=I.contentType,S(e)&&console.log("[Gateway] Injected tid:",A,"into",s))}let R,h=nt(e);h&&(R=await ut(h,"bff-worker","auth-craft:backend","cloudflare-auth-gateway"));let _=Gt(t,e.BACKEND_GATEWAY_SECRET,r,i,d,R),B=u!==void 0?u:t.body?await t.clone().arrayBuffer():void 0,w=await fetch(c,{method:t.method,headers:_,body:B});return Vt(w,r,e)}catch(n){console.error("[Gateway] Proxy error:",n);let o=n instanceof Error?n.message:"Unknown error";return l(a(E("BAD_GATEWAY",`Failed to reach backend: ${o}`)()))}}var P=new Map,lt=0,Mt=[{match:t=>t==="/auth/authenticate",limit:15,windowMs:6e4,prefix:"login"},{match:t=>t==="/auth/register",limit:8,windowMs:6e4,prefix:"register"},{match:t=>t==="/password/forgot",limit:8,windowMs:3e5,prefix:"forgot"},{match:t=>t.startsWith("/oauth/"),limit:30,windowMs:6e4,prefix:"oauth"},{match:t=>t.startsWith("/auth/complete-"),limit:30,windowMs:6e4,prefix:"complete"},{match:t=>t==="/auth/issue-mfa-challenge",limit:15,windowMs:6e4,prefix:"issue-mfa"}];function Bt(t){let e=Date.now();if(e-lt<3e4)return;lt=e;let r=e-t;for(let[n,o]of P){let i=o.hits.filter(s=>s>r);i.length===0?P.delete(n):o.hits=i}}function ft(t,e,r){if(r!=="POST")return{allowed:!0,retryAfterSec:0};let n=Mt.find(d=>d.match(e));if(!n)return{allowed:!0,retryAfterSec:0};Bt(3e5);let o=`${n.prefix}:${t}`,i=Date.now(),s=i-n.windowMs,c=P.get(o)??{hits:[]},u=c.hits.filter(d=>d>s);if(u.length>=n.limit){let d=u[0]??i;return c.hits=u,P.set(o,c),{allowed:!1,retryAfterSec:Math.max(1,Math.ceil((d+n.windowMs-i)/1e3))}}return u.push(i),c.hits=u,P.set(o,c),{allowed:!0,retryAfterSec:0}}function dt(t,e){if(t.length!==e.length)return!1;let r=0;for(let n=0;n<t.length;n++)r|=t.charCodeAt(n)^e.charCodeAt(n);return r===0}async function Et(t,e){return t&&await $(t,e)?{"Access-Control-Allow-Origin":t,"Access-Control-Allow-Credentials":"true",Vary:"Origin"}:k(e).includes("*")?{"Access-Control-Allow-Origin":"*"}:{Vary:"Origin"}}async function Ht(t,e){let r=t.headers.get("Origin")??"*",n=t.headers.get("Access-Control-Request-Headers")??"";return new Response(null,{status:204,headers:{...await Et(r,e),"Access-Control-Allow-Methods":"GET, POST, PUT, DELETE, OPTIONS, PATCH","Access-Control-Allow-Headers":n||"Content-Type, Authorization, X-Requested-With, X-Edge-Gate","Access-Control-Max-Age":"86400"}})}async function p(t,e,r){let n=e.headers.get("Origin")??"*",o=new Response(t.body,t),i=t.headers.getSetCookie?.()??[];if(i.length>0){o.headers.delete("Set-Cookie");for(let c of i)o.headers.append("Set-Cookie",c)}let s=await Et(n,r);for(let[c,u]of Object.entries(s))o.headers.set(c,u);return o.headers.set("Access-Control-Expose-Headers","Content-Length, X-Request-Id, Set-Cookie"),o}var vt="/internal/cors-origins";function Ft(t){return t.split("?")[0].replace(/\/$/,"")===vt}async function $t(t,e){if(!e.CORS_KV)return M();if(t.method==="GET"){let n=(await e.CORS_KV.list({prefix:"origin:"})).keys.map(o=>o.name.slice(7));return l(m({origins:n}))}if(t.method==="PUT"||t.method==="DELETE"){let r;try{r=(await t.json()).origin}catch{return l(a(T.BAD_REQUEST()))}if(typeof r!="string"||!r)return l(a(T.BAD_REQUEST()));let n;try{if(n=new URL(r).origin,n==="null"||n!==r.replace(/\/$/,""))return l(a(T.BAD_REQUEST()))}catch{return l(a(T.BAD_REQUEST()))}return t.method==="PUT"?await e.CORS_KV.put(C(n),"1"):await e.CORS_KV.delete(C(n)),l(m({origin:n}))}return l(a(T.BAD_REQUEST()))}function M(){return new Response(null,{status:404})}var Ne={async fetch(t,e,r){try{F(e);let n=new URL(t.url);if(!X(n.pathname,e))return M();if(t.method==="OPTIONS")return await Ht(t,e);if(!J(e)){let g=t.headers.get("X-Edge-Gate");if(!g||!e.EDGE_GATE_SECRET||!dt(g,e.EDGE_GATE_SECRET))return S(e)&&console.warn("[Gateway] X-Edge-Gate missing or invalid"),M()}let o=O(e),i=o?`${o}/health`:"/health";if(n.pathname===i)return await p(l(m({status:"healthy",timestamp:new Date().toISOString()})),t,e);let s=D(n.pathname,e);S(e)&&console.log("[Gateway] Request:",t.method,n.pathname,"\u2192",s);let c=t.headers.get("cf-connecting-ip")??"unknown",u=ft(c,s,t.method);if(!u.allowed){let g=l(a(T.RATE_LIMIT_EXCEEDED()));return g.headers.set("Retry-After",String(u.retryAfterSec)),await p(g,t,e)}let d=tt(s,e);if(!d&&rt(s))return await p(await V(t,e),t,e);let A=t.headers.get("Authorization");if(!A)return await p(l(a(f.MISSING_TOKEN())),t,e);let R=A.match(/^Bearer\s+(.+)$/i);if(!R)return await p(l(a(f.INVALID_FORMAT())),t,e);let h=R[1];if(h.length>4096)return await p(l(a(f.INVALID_FORMAT())),t,e);let _=h.split(".");if(_.length!==3)return await p(l(a(f.INVALID_FORMAT())),t,e);if(_[1].length>2048)return await p(l(a(f.INVALID_FORMAT())),t,e);let w=ct(h);if(!w)return await p(l(a(f.INVALID_FORMAT())),t,e);let I=w.typ,pt=d?3:1;if(I!==pt)return await p(l(a(f.INVALID_TOKEN_TYPE())),t,e);if(d&&w.aud!==Q(e))return await p(l(a(f.INVALID_TOKEN_TYPE())),t,e);let H=w.exp;if(typeof H=="number"&&Math.floor(Date.now()/1e3)>=H)return await p(l(a(f.TOKEN_EXPIRED())),t,e);if(d){if(!e.SERVICE_JWT_PUBLIC_KEY)return await p(l(a(T.INTERNAL_ERROR())),t,e);let g=await K(h,e.SERVICE_JWT_PUBLIC_KEY,3,z(e),q(e));if(!g.ok)return S(e)&&console.error("[Gateway] Service JWT failed:",g.errors),await p(l(a(g.errors,g.meta)),t,e)}else{let g=await K(h,e.JWT_PUBLIC_KEY,1,j(e),Z(e));if(!g.ok)return S(e)&&console.error("[Gateway] User JWT failed:",g.errors),await p(l(a(g.errors,g.meta)),t,e);let{scope:L}=x(e,s);if(L&&!et(s)&&g.data.aud!==L)return S(e)&&console.error(`[Gateway] Scope mismatch: token aud="${g.data.aud}" scope="${L}"`),await p(l(a(f.SCOPE_MISMATCH())),t,e)}return d&&Ft(s)?await p(await $t(t,e),t,e):await p(await V(t,e),t,e)}catch(n){return S(e)&&console.error("[Gateway] Unhandled error:",n),l(a(T.INTERNAL_ERROR()))}}};export{Ne as default};