@stacknet/userutils 0.3.6 → 0.4.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/README.md +1 -1
- package/dist/components/index.cjs +1 -1
- package/dist/components/index.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.d.cts +9 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
package/dist/server/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var crypto=require('crypto');function G(e){return Buffer.from(e).toString("base64url")}function ee(e){return Buffer.from(e,"base64url").toString()}function v(e){try{let r=e.split(".");return r.length!==3?null:JSON.parse(ee(r[1]))}catch{return null}}function $(e,r){let t=G(JSON.stringify({alg:"HS256",typ:"JWT"})),n=G(JSON.stringify(e)),o=crypto.createHmac("sha256",r).update(`${t}.${n}`).digest("base64url");return `${t}.${n}.${o}`}function K(e,r){try{let t=e.split(".");if(t.length!==3)return !1;let[n,o,s]=t,i=crypto.createHmac("sha256",r).update(`${n}.${o}`).digest("base64url"),y=Buffer.from(s),m=Buffer.from(i);return y.length!==m.length?!1:crypto.timingSafeEqual(y,m)}catch{return false}}function M(e,r){if(!K(e,r))return null;let t=v(e);return !t||t.exp&&t.exp<Math.floor(Date.now()/1e3)?null:t}function D(e,r,t=900,n=300){let o=M(e,r);return !o?.exp||o.exp*1e3-Date.now()>n*1e3?null:$({...o,exp:Math.floor(Date.now()/1e3)+t},r)}function W(e=32){return crypto.randomBytes(e).toString("hex")}function O(e){return e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}var re="__csrf",se="x-csrf-token";function j(e={}){let r=e.cookieName||re,t=e.headerName||se,n=e.tokenLength||32,o=e.secure!==false;return {generateToken(s){let i=W(n),y=[`${r}=${i}`,"Path=/","SameSite=Lax"];return o&&y.push("Secure"),s.append("Set-Cookie",y.join("; ")),i},validateRequest(s){let i=s.headers.get("cookie");if(!i)return {valid:false,error:"No cookies present"};let y=i.split(";").map(d=>d.trim()).find(d=>d.startsWith(`${r}=`))?.slice(r.length+1);if(!y)return {valid:false,error:"CSRF cookie missing"};let m=s.headers.get(t);if(!m)return {valid:false,error:"CSRF header missing"};try{let d=Buffer.from(y),l=Buffer.from(m);return d.length!==l.length?{valid:!1,error:"CSRF token mismatch"}:crypto.timingSafeEqual(d,l)?{valid:!0}:{valid:!1,error:"CSRF token mismatch"}}catch{return {valid:false,error:"CSRF validation failed"}}},cookieName:r,headerName:t}}function C(e){let r=new Map,t=setInterval(()=>{let n=Date.now();for(let[o,s]of r)n>=s.resetAt&&r.delete(o);},6e4);return typeof t=="object"&&"unref"in t&&t.unref(),{async check(n){let o=Date.now(),s=r.get(n);return (!s||o>=s.resetAt)&&(s={count:0,resetAt:o+e.windowMs},r.set(n,s)),s.count++,s.count>e.maxRequests?{allowed:false,remaining:0,retryAfter:Math.ceil((s.resetAt-o)/1e3)}:{allowed:true,remaining:e.maxRequests-s.count}}}}function oe(){let e=new Map,r=setInterval(()=>{let t=Date.now();for(let[n,o]of e)t>=o&&e.delete(n);},6e4);return typeof r=="object"&&"unref"in r&&r.unref(),{async has(t){let n=e.get(t);return n?Date.now()>=n?(e.delete(t),false):true:false},async set(t,n){e.set(t,Date.now()+n*1e3);}}}function ne(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),n=j({secure:e.secureCookies!==false}),o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;e.stacknetJwtSecret||e.authSecret;return async function(m){let d=O(m),l=await t.check(`auth:${d}`);if(!l.allowed)return Response.json({error:"Too many login attempts. Please wait."},{status:429,headers:{"Retry-After":String(l.retryAfter||60)}});let a;try{a=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{chain:c,message:u,signature:p,publicKey:f,otp:h,code:g,redirectUrl:k,stackId:w}=a,x=w||e.stackId,R;if(c&&u&&p){let Y={"Content-Type":"application/json"},_=await fetch(`${e.stacknetUrl}/api/v2/stacks/${x}/auth/web3/verify`,{method:"POST",headers:Y,body:JSON.stringify({chain:c,message:u,signature:p,public_key:f}),signal:AbortSignal.timeout(1e4)});if(!_.ok){let q=await _.json().catch(()=>({})),U=q?.error?.message||q?.message||q?.error||`StackNet returned ${_.status}`;return console.error(`[auth-callback] Verify failed: ${_.status}`,U),Response.json({error:"Wallet verification failed",detail:typeof U=="string"?U:void 0},{status:401})}let N=await _.json();R=N.data?.session||N.session||N.data||N,console.log(`[auth-callback] Verify OK, sessionData keys: ${Object.keys(R||{}).join(", ")}`);}else return h||g?Response.json({error:"Use /api/auth/otp for OTP verification"},{status:400}):Response.json({error:"Provide wallet signature or OTP code"},{status:400});if(!R?.jwt)return Response.json({error:"Authentication failed \u2014 no session returned"},{status:401});let S=JSON.parse(Buffer.from(R.jwt.split(".")[1],"base64url").toString()),b=Math.floor(Date.now()/1e3),T={...S,exp:b+o,iat:b},P=$(T,e.authSecret),J={userId:S.sub||S.user_id||S.session_id||S.global_id||"",address:R.address||S.address,chain:R.chain||c,expiresAt:Date.now()+s*1e3,authMethod:c?`web3:${c}`:"otp"},H=new Headers({"Content-Type":"application/json"}),L=e.secureCookies!==false?"; Secure":"",F=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";H.append("Set-Cookie",`stackauth_jwt=${P}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${L}${F}`);let z=Buffer.from(JSON.stringify(J)).toString("base64url");return H.append("Set-Cookie",`stackauth_session=${z}; Path=/; SameSite=Lax; Max-Age=${s}${L}${F}`),n.generateToken(H),new Response(JSON.stringify({user:J}),{status:200,headers:H})}}function X(e,r){if(!r)return null;try{let t=v(e);if(!t||t.exp&&t.exp<Math.floor(Date.now()/1e3))return null;let n=Buffer.from(JSON.stringify({alg:"HS256",typ:"JWT"})).toString("base64url"),o=Buffer.from(JSON.stringify(t)).toString("base64url"),s=crypto.createHmac("sha256",r).update(`${n}.${o}`).digest("base64url");return `${n}.${o}.${s}`}catch{return null}}function E(e,r){let t=X(e,r);return t?{Cookie:`stackauth_jwt=${t}`}:{Cookie:`stackauth_jwt=${e}`}}function A(e){let r=e.headers.get("cookie");if(r){let n=r.split(";").map(o=>o.trim()).find(o=>o.startsWith("stackauth_jwt="));if(n)return n.slice(14)}let t=e.headers.get("authorization");return t?.startsWith("Bearer ")?t.slice(7):null}function ie(e){return async function(t){let n=A(t);if(n){let y=v(n),m=y?.session_id||y?.sub;if(m)try{await fetch(`${e.stacknetUrl}/api/v2/sessions/${m}`,{method:"DELETE",signal:AbortSignal.timeout(5e3)});}catch{}}let o=e.secureCookies!==false?"; Secure":"",s=e.cookieDomain?`; Domain=${e.cookieDomain}`:"",i=new Headers({"Content-Type":"application/json"});return i.append("Set-Cookie",`stackauth_jwt=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${o}${s}`),i.append("Set-Cookie",`stackauth_session=; Path=/; SameSite=Lax; Max-Age=0${o}${s}`),i.append("Set-Cookie",`__csrf=; Path=/; SameSite=Lax; Max-Age=0${o}${s}`),new Response(JSON.stringify({success:true}),{status:200,headers:i})}}function ce(e){let r=e.jwtExpiry||900,t=e.sessionMaxAge||604800;return async function(o){let s=A(o);if(!s)return Response.json({session:null},{status:200});let i=M(s,e.authSecret);if(!i)return Response.json({session:null},{status:200});let m={userId:i.sub||i.user_id||i.session_id||i.global_id||"",address:i.address,chain:i.chain,expiresAt:i.session_expires_at||(i.exp?i.exp*1e3:Date.now()+t*1e3),planId:i.plan_id,authMethod:i.auth_method},d=new Headers({"Content-Type":"application/json"}),l=D(s,e.authSecret,r,300);if(l){let a=e.secureCookies!==false?"; Secure":"",c=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";d.append("Set-Cookie",`stackauth_jwt=${l}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${t}${a}${c}`);}return new Response(JSON.stringify({session:m}),{status:200,headers:d})}}function pe(e,r){if(e.length!==r.length)return false;try{return crypto.timingSafeEqual(Buffer.from(e),Buffer.from(r))}catch{return false}}function de(e){let r=e.rateLimiter||C({maxRequests:5,windowMs:3e5}),t=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;return async function(i){let y=O(i),m=await r.check(`otp:${y}`);if(!m.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(m.retryAfter||300)}});let d;try{d=await i.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{code:l}=d;if(!l||typeof l!="string"||l.length!==6)return Response.json({error:"Invalid code format"},{status:400});if(!pe(l,e.otpSecret))return Response.json({error:"Invalid code"},{status:401});let a=Math.floor(Date.now()/1e3),u={sub:`otp:${crypto.createHash("sha256").update(`otp:${l}:${Date.now()}`).digest("hex").slice(0,32)}`,auth_method:"otp",iat:a,exp:a+n},p=$(u,e.authSecret),f={userId:u.sub,expiresAt:Date.now()+o*1e3,authMethod:"otp"},h=new Headers({"Content-Type":"application/json"}),g=e.secureCookies!==false?"; Secure":"",k=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";h.append("Set-Cookie",`stackauth_jwt=${p}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${g}${k}`);let w=Buffer.from(JSON.stringify(f)).toString("base64url");return h.append("Set-Cookie",`stackauth_session=${w}; Path=/; SameSite=Lax; Max-Age=${o}${g}${k}`),t.generateToken(h),new Response(JSON.stringify({success:true,data:{user:f}}),{status:200,headers:h})}}function fe(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),n=j({secure:e.secureCookies!==false}),o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;async function i(m){let d=new URL(m.url),l=d.searchParams.get("provider"),a=d.searchParams.get("redirectUri")||d.searchParams.get("redirect_uri"),c=d.searchParams.get("stackId")||e.stackId;if(!l)return Response.json({error:"Missing provider parameter"},{status:400});if(!a)return Response.json({error:"Missing redirectUri parameter"},{status:400});try{let u=await fetch(`${e.stacknetUrl}/api/v2/stacks/${c}/auth/oauth/${l}/initiate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({redirectUrl:a}),signal:AbortSignal.timeout(1e4)});if(!u.ok){let h=await u.json().catch(()=>({}));return Response.json({error:h.error?.message||`Failed to start OAuth flow: ${u.statusText}`},{status:u.status})}let p=await u.json(),f=p.data||p;return Response.json({redirect_url:f.url,state:f.state})}catch(u){return Response.json({error:u.message||"Failed to start OAuth flow"},{status:500})}}async function y(m){let d=O(m),l=await t.check(`oauth:${d}`);if(!l.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(l.retryAfter||60)}});let a;try{a=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{provider:c,code:u,state:p,stackId:f}=a,h=f||e.stackId;if(!c||!u||!p)return Response.json({error:"Missing provider, code, or state"},{status:400});try{let g=await fetch(`${e.stacknetUrl}/api/v2/stacks/${h}/auth/oauth/${c}/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:u,state:p}),signal:AbortSignal.timeout(1e4)});if(!g.ok){let L=await g.json().catch(()=>({}));return Response.json({error:L.error?.message||`OAuth verification failed: ${g.statusText}`},{status:401})}let k=await g.json(),w=k.data?.session||k.session||k.data||k;if(!w?.jwt)return Response.json({error:"OAuth authentication failed \u2014 no session returned"},{status:401});let x=JSON.parse(Buffer.from(w.jwt.split(".")[1],"base64url").toString()),R=Math.floor(Date.now()/1e3),S=$({...x,exp:R+o,iat:R},e.authSecret),T={userId:x.sub||x.user_id||x.session_id||x.global_id||"",address:w.address||x.address,chain:void 0,expiresAt:Date.now()+s*1e3,authMethod:`oauth:${c}`},P=new Headers({"Content-Type":"application/json"}),I=e.secureCookies!==!1?"; Secure":"",J=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";P.append("Set-Cookie",`stackauth_jwt=${S}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${I}${J}`);let H=Buffer.from(JSON.stringify(T)).toString("base64url");return P.append("Set-Cookie",`stackauth_session=${H}; Path=/; SameSite=Lax; Max-Age=${s}${I}${J}`),n.generateToken(P),new Response(JSON.stringify({user:T}),{status:200,headers:P})}catch(g){return Response.json({error:g.message||"OAuth callback failed"},{status:500})}}return {startFlow:i,handleCallback:y}}function me(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),n=j({secure:e.secureCookies!==false}),o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;return async function(y){let m=O(y),d=await t.check(`google-onetap:${m}`);if(!d.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(d.retryAfter||60)}});let l;try{l=await y.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{credential:a,stackId:c}=l,u=c||e.stackId;if(!a)return Response.json({error:"Missing credential"},{status:400});let p;try{let f=a.split(".");if(f.length!==3)throw new Error("Invalid JWT");p=JSON.parse(Buffer.from(f[1],"base64url").toString());}catch{return Response.json({error:"Invalid credential format"},{status:400})}try{let f=await fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${encodeURIComponent(a)}`,{signal:AbortSignal.timeout(1e4)});if(!f.ok)return Response.json({error:"Google credential verification failed"},{status:401});let h=await f.json();if(!h.sub||!h.email)return Response.json({error:"Invalid Google token \u2014 missing user info"},{status:401})}catch{return Response.json({error:"Failed to verify Google credential"},{status:500})}try{let f=await fetch(`${e.stacknetUrl}/api/v2/stacks/${u}/auth/oauth/google/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:a,google_id:p.sub,email:p.email,name:p.name,picture:p.picture,one_tap:!0}),signal:AbortSignal.timeout(1e4)});if(!f.ok){let k=Math.floor(Date.now()/1e3),w=p.sub,x=$({sub:w,global_id:`google:${w}`,stack_id:u,chain:"google",email:p.email,credentials:["oauth:google"],iat:k,exp:k+o,iss:"stackauth.network",signed_by:["local"]},e.authSecret),R={userId:w,address:p.email,chain:void 0,expiresAt:Date.now()+s*1e3,authMethod:"oauth:google"},S=new Headers({"Content-Type":"application/json"}),b=e.secureCookies!==!1?"; Secure":"",T=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";S.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${b}${T}`);let P=Buffer.from(JSON.stringify(R)).toString("base64url");return S.append("Set-Cookie",`stackauth_session=${P}; Path=/; SameSite=Lax; Max-Age=${s}${b}${T}`),n.generateToken(S),new Response(JSON.stringify({user:R}),{status:200,headers:S})}let h=await f.json(),g=h.data?.session||h.session||h.data||h;if(g?.jwt){let k=JSON.parse(Buffer.from(g.jwt.split(".")[1],"base64url").toString()),w=Math.floor(Date.now()/1e3),x=$({...k,exp:w+o,iat:w},e.authSecret),S={userId:k.sub||k.user_id||p.sub,address:p.email||g.address,chain:void 0,expiresAt:Date.now()+s*1e3,authMethod:"oauth:google"},b=new Headers({"Content-Type":"application/json"}),T=e.secureCookies!==!1?"; Secure":"",P=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";b.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${T}${P}`);let I=Buffer.from(JSON.stringify(S)).toString("base64url");return b.append("Set-Cookie",`stackauth_session=${I}; Path=/; SameSite=Lax; Max-Age=${s}${T}${P}`),n.generateToken(b),new Response(JSON.stringify({user:S}),{status:200,headers:b})}return Response.json({error:"No session returned"},{status:401})}catch(f){return Response.json({error:f.message||"Google One Tap authentication failed"},{status:500})}}}function ye(e){let r=j({secure:e.secureCookies!==false}),t=e.rateLimiter||C({maxRequests:20,windowMs:6e4}),n=e.stacknetJwtSecret||e.authSecret,o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;function i(a){let c=A(a);if(!c)return null;let u=M(c,e.authSecret);return u?{jwt:c,payload:u}:null}function y(a,c){let u=D(a,e.authSecret,o,300);if(u){let p=e.secureCookies!==false?"; Secure":"",f=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";c.append("Set-Cookie",`stackauth_jwt=${u}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${p}${f}`);}}async function m(a,c){let u=i(a);if(!u)return Response.json({error:"Unauthorized"},{status:401});let p=E(u.jwt,n),f=await fetch(`${e.stacknetUrl}${c}`,{headers:p,signal:AbortSignal.timeout(15e3)}),h=await f.json().catch(()=>({})),g=new Headers({"Content-Type":"application/json"});return y(u.jwt,g),new Response(JSON.stringify(h),{status:f.status,headers:g})}async function d(a,c,u){let p=i(a);if(!p)return Response.json({error:"Unauthorized"},{status:401});let f=r.validateRequest(a);if(!f.valid)return Response.json({error:f.error||"CSRF validation failed"},{status:403});let h=p.payload.sub||p.payload.user_id||"unknown";if(!(await t.check(`billing:${h}`)).allowed)return Response.json({error:"Too many requests"},{status:429});let k=await a.json().catch(()=>({})),w=E(p.jwt,n);w["Content-Type"]="application/json";let x=await fetch(`${e.stacknetUrl}${c}`,{method:"POST",headers:w,body:JSON.stringify({...k,...u}),signal:AbortSignal.timeout(15e3)}),R=await x.json().catch(()=>({})),S=new Headers({"Content-Type":"application/json"});return y(p.jwt,S),new Response(JSON.stringify(R),{status:x.status,headers:S})}let l=`/api/v2/stacks/${e.stackId}`;return {plans:{GET:async a=>{let c=await fetch(`${e.stacknetUrl}${l}/plans`,{signal:AbortSignal.timeout(1e4)}),u=await c.json().catch(()=>({}));return Response.json(u,{status:c.status})}},subscription:{GET:(a=>m(a,`${l}/subscription`))},subscribe:{POST:(a=>{let c=new URL(a.url).origin;return d(a,`${l}/subscribe`,{successUrl:`${c}/billing/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing`})})},cancel:{POST:(a=>d(a,`${l}/cancel-subscription`))},usage:{GET:(a=>m(a,"/v1/account/usage"))},history:{GET:(a=>m(a,`${l}/billing`))},prepaid:{POST:(a=>{let c=new URL(a.url).origin;return d(a,`${l}/prepaid`,{successUrl:`${c}/pricing/prepaid/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing/prepaid`})})},verifyPrepaid:{POST:(a=>d(a,`${l}/verify-prepaid`))},verifySession:{POST:(a=>d(a,`${l}/verify-session`))}}}function he(e){return async function(t){let n=t.headers.get("stripe-signature");if(!n)return Response.json({error:"Missing Stripe signature"},{status:400});try{let o=await t.text(),s=await fetch(`${e.stacknetUrl}/api/v2/stacks/${e.stackId}/webhook/stripe`,{method:"POST",headers:{"Content-Type":"application/json","stripe-signature":n},body:o,signal:AbortSignal.timeout(1e4)}),i=await s.json().catch(()=>({received:!0}));return Response.json(i,{status:s.status})}catch{return Response.json({error:"Webhook processing failed"},{status:502})}}}function B(){return {"Strict-Transport-Security":"max-age=63072000; includeSubDomains; preload","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-XSS-Protection":"0","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()"}}function ge(e){return async r=>{let t=await e(r),n=B(),o=new Headers(t.headers);for(let[s,i]of Object.entries(n))o.set(s,i);return new Response(t.body,{status:t.status,statusText:t.statusText,headers:o})}}function Se(){return Object.entries(B()).map(([e,r])=>({key:e,value:r}))}
|
|
1
|
+
'use strict';var crypto=require('crypto');function G(e){return Buffer.from(e).toString("base64url")}function ee(e){return Buffer.from(e,"base64url").toString()}function v(e){try{let r=e.split(".");return r.length!==3?null:JSON.parse(ee(r[1]))}catch{return null}}function $(e,r){let t=G(JSON.stringify({alg:"HS256",typ:"JWT"})),a=G(JSON.stringify(e)),n=crypto.createHmac("sha256",r).update(`${t}.${a}`).digest("base64url");return `${t}.${a}.${n}`}function K(e,r){try{let t=e.split(".");if(t.length!==3)return !1;let[a,n,o]=t,i=crypto.createHmac("sha256",r).update(`${a}.${n}`).digest("base64url"),y=Buffer.from(o),m=Buffer.from(i);return y.length!==m.length?!1:crypto.timingSafeEqual(y,m)}catch{return false}}function M(e,r){if(!K(e,r))return null;let t=v(e);return !t||t.exp&&t.exp<Math.floor(Date.now()/1e3)?null:t}function D(e,r,t=900,a=300){let n=M(e,r);return !n?.exp||n.exp*1e3-Date.now()>a*1e3?null:$({...n,exp:Math.floor(Date.now()/1e3)+t},r)}function W(e=32){return crypto.randomBytes(e).toString("hex")}function O(e){return e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}var re="__csrf",se="x-csrf-token";function j(e={}){let r=e.cookieName||re,t=e.headerName||se,a=e.tokenLength||32,n=e.secure!==false;return {generateToken(o){let i=W(a),y=[`${r}=${i}`,"Path=/","SameSite=Lax"];return n&&y.push("Secure"),o.append("Set-Cookie",y.join("; ")),i},validateRequest(o){let i=o.headers.get("cookie");if(!i)return {valid:false,error:"No cookies present"};let y=i.split(";").map(l=>l.trim()).find(l=>l.startsWith(`${r}=`))?.slice(r.length+1);if(!y)return {valid:false,error:"CSRF cookie missing"};let m=o.headers.get(t);if(!m)return {valid:false,error:"CSRF header missing"};try{let l=Buffer.from(y),u=Buffer.from(m);return l.length!==u.length?{valid:!1,error:"CSRF token mismatch"}:crypto.timingSafeEqual(l,u)?{valid:!0}:{valid:!1,error:"CSRF token mismatch"}}catch{return {valid:false,error:"CSRF validation failed"}}},cookieName:r,headerName:t}}function C(e){let r=new Map,t=setInterval(()=>{let a=Date.now();for(let[n,o]of r)a>=o.resetAt&&r.delete(n);},6e4);return typeof t=="object"&&"unref"in t&&t.unref(),{async check(a){let n=Date.now(),o=r.get(a);return (!o||n>=o.resetAt)&&(o={count:0,resetAt:n+e.windowMs},r.set(a,o)),o.count++,o.count>e.maxRequests?{allowed:false,remaining:0,retryAfter:Math.ceil((o.resetAt-n)/1e3)}:{allowed:true,remaining:e.maxRequests-o.count}}}}function oe(){let e=new Map,r=setInterval(()=>{let t=Date.now();for(let[a,n]of e)t>=n&&e.delete(a);},6e4);return typeof r=="object"&&"unref"in r&&r.unref(),{async has(t){let a=e.get(t);return a?Date.now()>=a?(e.delete(t),false):true:false},async set(t,a){e.set(t,Date.now()+a*1e3);}}}function ne(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),a=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;e.stacknetJwtSecret||e.authSecret;return async function(m){let l=O(m),u=await t.check(`auth:${l}`);if(!u.allowed)return Response.json({error:"Too many login attempts. Please wait."},{status:429,headers:{"Retry-After":String(u.retryAfter||60)}});let s;try{s=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{chain:c,message:p,signature:d,publicKey:f,otp:h,code:g,redirectUrl:k,stackId:w}=s,x=w||e.stackId,R;if(c&&p&&d){let Y={"Content-Type":"application/json"},_=await fetch(`${e.stacknetUrl}/api/v2/stacks/${x}/auth/web3/verify`,{method:"POST",headers:Y,body:JSON.stringify({chain:c,message:p,signature:d,public_key:f}),signal:AbortSignal.timeout(1e4)});if(!_.ok){let q=await _.json().catch(()=>({})),U=q?.error?.message||q?.message||q?.error||`StackNet returned ${_.status}`;return console.error(`[auth-callback] Verify failed: ${_.status}`,U),Response.json({error:"Wallet verification failed",detail:typeof U=="string"?U:void 0},{status:401})}let N=await _.json();R=N.data?.session||N.session||N.data||N,console.log(`[auth-callback] Verify OK, sessionData keys: ${Object.keys(R||{}).join(", ")}`);}else return h||g?Response.json({error:"Use /api/auth/otp for OTP verification"},{status:400}):Response.json({error:"Provide wallet signature or OTP code"},{status:400});if(!R?.jwt)return Response.json({error:"Authentication failed \u2014 no session returned"},{status:401});let S=JSON.parse(Buffer.from(R.jwt.split(".")[1],"base64url").toString()),b=Math.floor(Date.now()/1e3),T={...S,exp:b+n,iat:b},P=$(T,e.authSecret),J={userId:S.sub||S.user_id||S.session_id||S.global_id||"",address:R.address||S.address,chain:R.chain||c,expiresAt:Date.now()+o*1e3,authMethod:c?`web3:${c}`:"otp"},H=new Headers({"Content-Type":"application/json"}),L=e.secureCookies!==false?"; Secure":"",F=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";H.append("Set-Cookie",`stackauth_jwt=${P}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${L}${F}`);let z=Buffer.from(JSON.stringify(J)).toString("base64url");return H.append("Set-Cookie",`stackauth_session=${z}; Path=/; SameSite=Lax; Max-Age=${o}${L}${F}`),a.generateToken(H),new Response(JSON.stringify({user:J}),{status:200,headers:H})}}function X(e,r){if(!r)return null;try{let t=v(e);if(!t||t.exp&&t.exp<Math.floor(Date.now()/1e3))return null;let a=Buffer.from(JSON.stringify({alg:"HS256",typ:"JWT"})).toString("base64url"),n=Buffer.from(JSON.stringify(t)).toString("base64url"),o=crypto.createHmac("sha256",r).update(`${a}.${n}`).digest("base64url");return `${a}.${n}.${o}`}catch{return null}}function E(e,r){let t=X(e,r);return t?{Cookie:`stackauth_jwt=${t}`}:{Cookie:`stackauth_jwt=${e}`}}function A(e){let r=e.headers.get("cookie");if(r){let a=r.split(";").map(n=>n.trim()).find(n=>n.startsWith("stackauth_jwt="));if(a)return a.slice(14)}let t=e.headers.get("authorization");return t?.startsWith("Bearer ")?t.slice(7):null}function ie(e){return async function(t){let a=A(t);if(a){let y=v(a),m=y?.session_id||y?.sub;if(m)try{await fetch(`${e.stacknetUrl}/api/v2/sessions/${m}`,{method:"DELETE",signal:AbortSignal.timeout(5e3)});}catch{}}let n=e.secureCookies!==false?"; Secure":"",o=e.cookieDomain?`; Domain=${e.cookieDomain}`:"",i=new Headers({"Content-Type":"application/json"});return i.append("Set-Cookie",`stackauth_jwt=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${n}${o}`),i.append("Set-Cookie",`stackauth_session=; Path=/; SameSite=Lax; Max-Age=0${n}${o}`),i.append("Set-Cookie",`__csrf=; Path=/; SameSite=Lax; Max-Age=0${n}${o}`),new Response(JSON.stringify({success:true}),{status:200,headers:i})}}function ce(e){let r=e.jwtExpiry||900,t=e.sessionMaxAge||604800;return async function(n){let o=A(n);if(!o)return Response.json({session:null},{status:200});let i=M(o,e.authSecret);if(!i)return Response.json({session:null},{status:200});let m={userId:i.sub||i.user_id||i.session_id||i.global_id||"",address:i.address,chain:i.chain,expiresAt:i.session_expires_at||(i.exp?i.exp*1e3:Date.now()+t*1e3),planId:i.plan_id,authMethod:i.auth_method},l=new Headers({"Content-Type":"application/json"}),u=D(o,e.authSecret,r,300);if(u){let s=e.secureCookies!==false?"; Secure":"",c=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";l.append("Set-Cookie",`stackauth_jwt=${u}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${t}${s}${c}`);}return new Response(JSON.stringify({session:m}),{status:200,headers:l})}}function pe(e,r){if(e.length!==r.length)return false;try{return crypto.timingSafeEqual(Buffer.from(e),Buffer.from(r))}catch{return false}}function de(e){let r=e.rateLimiter||C({maxRequests:5,windowMs:3e5}),t=j({secure:e.secureCookies!==false}),a=e.jwtExpiry||900,n=e.sessionMaxAge||604800;return async function(i){let y=O(i),m=await r.check(`otp:${y}`);if(!m.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(m.retryAfter||300)}});let l;try{l=await i.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{code:u}=l;if(!u||typeof u!="string"||u.length!==6)return Response.json({error:"Invalid code format"},{status:400});if(!pe(u,e.otpSecret))return Response.json({error:"Invalid code"},{status:401});let s=Math.floor(Date.now()/1e3),p={sub:`otp:${crypto.createHash("sha256").update(`otp:${u}:${Date.now()}`).digest("hex").slice(0,32)}`,auth_method:"otp",iat:s,exp:s+a},d=$(p,e.authSecret),f={userId:p.sub,expiresAt:Date.now()+n*1e3,authMethod:"otp"},h=new Headers({"Content-Type":"application/json"}),g=e.secureCookies!==false?"; Secure":"",k=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";h.append("Set-Cookie",`stackauth_jwt=${d}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${n}${g}${k}`);let w=Buffer.from(JSON.stringify(f)).toString("base64url");return h.append("Set-Cookie",`stackauth_session=${w}; Path=/; SameSite=Lax; Max-Age=${n}${g}${k}`),t.generateToken(h),new Response(JSON.stringify({success:true,data:{user:f}}),{status:200,headers:h})}}function fe(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),a=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;async function i(m){let l=new URL(m.url),u=l.searchParams.get("provider"),s=l.searchParams.get("redirectUri")||l.searchParams.get("redirect_uri"),c=l.searchParams.get("stackId")||e.stackId;if(!u)return Response.json({error:"Missing provider parameter"},{status:400});if(!s)return Response.json({error:"Missing redirectUri parameter"},{status:400});try{let p=await fetch(`${e.stacknetUrl}/api/v2/stacks/${c}/auth/oauth/${u}/initiate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({redirectUrl:s}),signal:AbortSignal.timeout(1e4)});if(!p.ok){let h=await p.json().catch(()=>({}));return Response.json({error:h.error?.message||`Failed to start OAuth flow: ${p.statusText}`},{status:p.status})}let d=await p.json(),f=d.data||d;return Response.json({redirect_url:f.url,state:f.state})}catch(p){return Response.json({error:p.message||"Failed to start OAuth flow"},{status:500})}}async function y(m){let l=O(m),u=await t.check(`oauth:${l}`);if(!u.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(u.retryAfter||60)}});let s;try{s=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{provider:c,code:p,state:d,stackId:f}=s,h=f||e.stackId;if(!c||!p||!d)return Response.json({error:"Missing provider, code, or state"},{status:400});try{let g=await fetch(`${e.stacknetUrl}/api/v2/stacks/${h}/auth/oauth/${c}/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:p,state:d}),signal:AbortSignal.timeout(1e4)});if(!g.ok){let L=await g.json().catch(()=>({}));return Response.json({error:L.error?.message||`OAuth verification failed: ${g.statusText}`},{status:401})}let k=await g.json(),w=k.data?.session||k.session||k.data||k;if(!w?.jwt)return Response.json({error:"OAuth authentication failed \u2014 no session returned"},{status:401});let x=JSON.parse(Buffer.from(w.jwt.split(".")[1],"base64url").toString()),R=Math.floor(Date.now()/1e3),S=$({...x,exp:R+n,iat:R},e.authSecret),T={userId:x.sub||x.user_id||x.session_id||x.global_id||"",address:w.address||x.address,chain:void 0,expiresAt:Date.now()+o*1e3,authMethod:`oauth:${c}`},P=new Headers({"Content-Type":"application/json"}),I=e.secureCookies!==!1?"; Secure":"",J=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";P.append("Set-Cookie",`stackauth_jwt=${S}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${I}${J}`);let H=Buffer.from(JSON.stringify(T)).toString("base64url");return P.append("Set-Cookie",`stackauth_session=${H}; Path=/; SameSite=Lax; Max-Age=${o}${I}${J}`),a.generateToken(P),new Response(JSON.stringify({user:T}),{status:200,headers:P})}catch(g){return Response.json({error:g.message||"OAuth callback failed"},{status:500})}}return {startFlow:i,handleCallback:y}}function me(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),a=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;return async function(y){let m=O(y),l=await t.check(`google-onetap:${m}`);if(!l.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(l.retryAfter||60)}});let u;try{u=await y.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{credential:s,stackId:c}=u,p=c||e.stackId;if(!s)return Response.json({error:"Missing credential"},{status:400});let d;try{let f=s.split(".");if(f.length!==3)throw new Error("Invalid JWT");d=JSON.parse(Buffer.from(f[1],"base64url").toString());}catch{return Response.json({error:"Invalid credential format"},{status:400})}try{let f=await fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(1e4)});if(!f.ok)return Response.json({error:"Google credential verification failed"},{status:401});let h=await f.json();if(!h.sub||!h.email)return Response.json({error:"Invalid Google token \u2014 missing user info"},{status:401})}catch{return Response.json({error:"Failed to verify Google credential"},{status:500})}try{let f=await fetch(`${e.stacknetUrl}/api/v2/stacks/${p}/auth/oauth/google/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:s,google_id:d.sub,email:d.email,name:d.name,picture:d.picture,one_tap:!0}),signal:AbortSignal.timeout(1e4)});if(!f.ok){let k=Math.floor(Date.now()/1e3),w=d.sub,x=$({sub:w,global_id:`google:${w}`,stack_id:p,chain:"google",email:d.email,credentials:["oauth:google"],iat:k,exp:k+n,iss:"stackauth.network",signed_by:["local"]},e.authSecret),R={userId:w,address:d.email,chain:void 0,expiresAt:Date.now()+o*1e3,authMethod:"oauth:google"},S=new Headers({"Content-Type":"application/json"}),b=e.secureCookies!==!1?"; Secure":"",T=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";S.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${b}${T}`);let P=Buffer.from(JSON.stringify(R)).toString("base64url");return S.append("Set-Cookie",`stackauth_session=${P}; Path=/; SameSite=Lax; Max-Age=${o}${b}${T}`),a.generateToken(S),new Response(JSON.stringify({user:R}),{status:200,headers:S})}let h=await f.json(),g=h.data?.session||h.session||h.data||h;if(g?.jwt){let k=JSON.parse(Buffer.from(g.jwt.split(".")[1],"base64url").toString()),w=Math.floor(Date.now()/1e3),x=$({...k,exp:w+n,iat:w},e.authSecret),S={userId:k.sub||k.user_id||d.sub,address:d.email||g.address,chain:void 0,expiresAt:Date.now()+o*1e3,authMethod:"oauth:google"},b=new Headers({"Content-Type":"application/json"}),T=e.secureCookies!==!1?"; Secure":"",P=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";b.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${T}${P}`);let I=Buffer.from(JSON.stringify(S)).toString("base64url");return b.append("Set-Cookie",`stackauth_session=${I}; Path=/; SameSite=Lax; Max-Age=${o}${T}${P}`),a.generateToken(b),new Response(JSON.stringify({user:S}),{status:200,headers:b})}return Response.json({error:"No session returned"},{status:401})}catch(f){return Response.json({error:f.message||"Google One Tap authentication failed"},{status:500})}}}function ye(e){let r=j({secure:e.secureCookies!==false}),t=e.rateLimiter||C({maxRequests:20,windowMs:6e4}),a=e.stacknetJwtSecret||e.authSecret,n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;function i(s){let c=A(s);if(!c)return null;let p=M(c,e.authSecret);return p?{jwt:c,payload:p}:null}function y(s,c){let p=D(s,e.authSecret,n,300);if(p){let d=e.secureCookies!==false?"; Secure":"",f=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";c.append("Set-Cookie",`stackauth_jwt=${p}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${d}${f}`);}}async function m(s,c){let p=i(s);if(!p)return Response.json({error:"Unauthorized"},{status:401});let d=E(p.jwt,a),f=await fetch(`${e.stacknetUrl}${c}`,{headers:d,signal:AbortSignal.timeout(15e3)}),h=await f.json().catch(()=>({})),g=new Headers({"Content-Type":"application/json"});return y(p.jwt,g),new Response(JSON.stringify(h),{status:f.status,headers:g})}async function l(s,c,p){let d=i(s);if(!d)return Response.json({error:"Unauthorized"},{status:401});let f=r.validateRequest(s);if(!f.valid)return Response.json({error:f.error||"CSRF validation failed"},{status:403});let h=d.payload.sub||d.payload.user_id||"unknown";if(!(await t.check(`billing:${h}`)).allowed)return Response.json({error:"Too many requests"},{status:429});let k=await s.json().catch(()=>({})),w=E(d.jwt,a);w["Content-Type"]="application/json";let x=await fetch(`${e.stacknetUrl}${c}`,{method:"POST",headers:w,body:JSON.stringify({...k,...p}),signal:AbortSignal.timeout(15e3)}),R=await x.json().catch(()=>({})),S=new Headers({"Content-Type":"application/json"});return y(d.jwt,S),new Response(JSON.stringify(R),{status:x.status,headers:S})}let u=`/api/v2/stacks/${e.stackId}`;return {plans:{GET:async s=>{let c=await fetch(`${e.stacknetUrl}${u}/plans`,{signal:AbortSignal.timeout(1e4)}),p=await c.json().catch(()=>({}));return Response.json(p,{status:c.status})}},subscription:{GET:(s=>m(s,`${u}/subscription`))},subscribe:{POST:(s=>{let c=new URL(s.url).origin;return l(s,`${u}/subscribe`,{successUrl:`${c}/billing/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing`})})},cancel:{POST:(s=>l(s,`${u}/cancel-subscription`))},usage:{GET:(s=>m(s,"/v1/account/usage"))},history:{GET:(s=>m(s,`${u}/billing`))},prepaid:{POST:(s=>{let c=new URL(s.url).origin;return l(s,`${u}/prepaid`,{successUrl:`${c}/pricing/prepaid/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing/prepaid`})})},verifyPrepaid:{POST:(s=>l(s,`${u}/verify-prepaid`))},verifySession:{POST:(s=>l(s,`${u}/verify-session`))},subscribeSol:{POST:(s=>l(s,`${u}/subscribe-sol`))},prepaidSol:{POST:(s=>{new URL(s.url).origin;return l(s,`${u}/prepaid-sol`)})},topup:{POST:(s=>l(s,"/v1/account/topup"))}}}function he(e){return async function(t){let a=t.headers.get("stripe-signature");if(!a)return Response.json({error:"Missing Stripe signature"},{status:400});try{let n=await t.text(),o=await fetch(`${e.stacknetUrl}/api/v2/stacks/${e.stackId}/webhook/stripe`,{method:"POST",headers:{"Content-Type":"application/json","stripe-signature":a},body:n,signal:AbortSignal.timeout(1e4)}),i=await o.json().catch(()=>({received:!0}));return Response.json(i,{status:o.status})}catch{return Response.json({error:"Webhook processing failed"},{status:502})}}}function B(){return {"Strict-Transport-Security":"max-age=63072000; includeSubDomains; preload","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-XSS-Protection":"0","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()"}}function ge(e){return async r=>{let t=await e(r),a=B(),n=new Headers(t.headers);for(let[o,i]of Object.entries(a))n.set(o,i);return new Response(t.body,{status:t.status,statusText:t.statusText,headers:n})}}function Se(){return Object.entries(B()).map(([e,r])=>({key:e,value:r}))}
|
|
2
2
|
exports.buildStackNetHeaders=E;exports.createAuthCallback=ne;exports.createBillingProxy=ye;exports.createCSRFProtection=j;exports.createGoogleOneTapHandler=me;exports.createInMemoryRateLimiter=C;exports.createInMemoryReplayStore=oe;exports.createLogoutHandler=ie;exports.createOAuthHandlers=fe;exports.createOTPHandler=de;exports.createSessionHandler=ce;exports.createWebhookHandler=he;exports.decodeJWTPayload=v;exports.extractIP=O;exports.extractJwt=A;exports.generateToken=W;exports.maybeRefreshJWT=D;exports.nextSecurityHeaders=Se;exports.resignForStackNet=X;exports.securityHeaders=B;exports.signJWT=$;exports.verifyJWT=M;exports.verifyJWTSignature=K;exports.withSecurityHeaders=ge;
|
package/dist/server/index.d.cts
CHANGED
|
@@ -135,6 +135,15 @@ declare function createBillingProxy(config: BillingProxyConfig): {
|
|
|
135
135
|
verifySession: {
|
|
136
136
|
POST: Handler;
|
|
137
137
|
};
|
|
138
|
+
subscribeSol: {
|
|
139
|
+
POST: Handler;
|
|
140
|
+
};
|
|
141
|
+
prepaidSol: {
|
|
142
|
+
POST: Handler;
|
|
143
|
+
};
|
|
144
|
+
topup: {
|
|
145
|
+
POST: Handler;
|
|
146
|
+
};
|
|
138
147
|
};
|
|
139
148
|
|
|
140
149
|
/**
|
package/dist/server/index.d.ts
CHANGED
|
@@ -135,6 +135,15 @@ declare function createBillingProxy(config: BillingProxyConfig): {
|
|
|
135
135
|
verifySession: {
|
|
136
136
|
POST: Handler;
|
|
137
137
|
};
|
|
138
|
+
subscribeSol: {
|
|
139
|
+
POST: Handler;
|
|
140
|
+
};
|
|
141
|
+
prepaidSol: {
|
|
142
|
+
POST: Handler;
|
|
143
|
+
};
|
|
144
|
+
topup: {
|
|
145
|
+
POST: Handler;
|
|
146
|
+
};
|
|
138
147
|
};
|
|
139
148
|
|
|
140
149
|
/**
|
package/dist/server/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {createHmac,timingSafeEqual,randomBytes,createHash}from'crypto';function G(e){return Buffer.from(e).toString("base64url")}function ee(e){return Buffer.from(e,"base64url").toString()}function v(e){try{let r=e.split(".");return r.length!==3?null:JSON.parse(ee(r[1]))}catch{return null}}function $(e,r){let t=G(JSON.stringify({alg:"HS256",typ:"JWT"})),n=G(JSON.stringify(e)),o=createHmac("sha256",r).update(`${t}.${n}`).digest("base64url");return `${t}.${n}.${o}`}function K(e,r){try{let t=e.split(".");if(t.length!==3)return !1;let[n,o,s]=t,i=createHmac("sha256",r).update(`${n}.${o}`).digest("base64url"),y=Buffer.from(s),m=Buffer.from(i);return y.length!==m.length?!1:timingSafeEqual(y,m)}catch{return false}}function M(e,r){if(!K(e,r))return null;let t=v(e);return !t||t.exp&&t.exp<Math.floor(Date.now()/1e3)?null:t}function D(e,r,t=900,n=300){let o=M(e,r);return !o?.exp||o.exp*1e3-Date.now()>n*1e3?null:$({...o,exp:Math.floor(Date.now()/1e3)+t},r)}function W(e=32){return randomBytes(e).toString("hex")}function O(e){return e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}var re="__csrf",se="x-csrf-token";function j(e={}){let r=e.cookieName||re,t=e.headerName||se,n=e.tokenLength||32,o=e.secure!==false;return {generateToken(s){let i=W(n),y=[`${r}=${i}`,"Path=/","SameSite=Lax"];return o&&y.push("Secure"),s.append("Set-Cookie",y.join("; ")),i},validateRequest(s){let i=s.headers.get("cookie");if(!i)return {valid:false,error:"No cookies present"};let y=i.split(";").map(d=>d.trim()).find(d=>d.startsWith(`${r}=`))?.slice(r.length+1);if(!y)return {valid:false,error:"CSRF cookie missing"};let m=s.headers.get(t);if(!m)return {valid:false,error:"CSRF header missing"};try{let d=Buffer.from(y),l=Buffer.from(m);return d.length!==l.length?{valid:!1,error:"CSRF token mismatch"}:timingSafeEqual(d,l)?{valid:!0}:{valid:!1,error:"CSRF token mismatch"}}catch{return {valid:false,error:"CSRF validation failed"}}},cookieName:r,headerName:t}}function C(e){let r=new Map,t=setInterval(()=>{let n=Date.now();for(let[o,s]of r)n>=s.resetAt&&r.delete(o);},6e4);return typeof t=="object"&&"unref"in t&&t.unref(),{async check(n){let o=Date.now(),s=r.get(n);return (!s||o>=s.resetAt)&&(s={count:0,resetAt:o+e.windowMs},r.set(n,s)),s.count++,s.count>e.maxRequests?{allowed:false,remaining:0,retryAfter:Math.ceil((s.resetAt-o)/1e3)}:{allowed:true,remaining:e.maxRequests-s.count}}}}function oe(){let e=new Map,r=setInterval(()=>{let t=Date.now();for(let[n,o]of e)t>=o&&e.delete(n);},6e4);return typeof r=="object"&&"unref"in r&&r.unref(),{async has(t){let n=e.get(t);return n?Date.now()>=n?(e.delete(t),false):true:false},async set(t,n){e.set(t,Date.now()+n*1e3);}}}function ne(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),n=j({secure:e.secureCookies!==false}),o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;e.stacknetJwtSecret||e.authSecret;return async function(m){let d=O(m),l=await t.check(`auth:${d}`);if(!l.allowed)return Response.json({error:"Too many login attempts. Please wait."},{status:429,headers:{"Retry-After":String(l.retryAfter||60)}});let a;try{a=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{chain:c,message:u,signature:p,publicKey:f,otp:h,code:g,redirectUrl:k,stackId:w}=a,x=w||e.stackId,R;if(c&&u&&p){let Y={"Content-Type":"application/json"},_=await fetch(`${e.stacknetUrl}/api/v2/stacks/${x}/auth/web3/verify`,{method:"POST",headers:Y,body:JSON.stringify({chain:c,message:u,signature:p,public_key:f}),signal:AbortSignal.timeout(1e4)});if(!_.ok){let q=await _.json().catch(()=>({})),U=q?.error?.message||q?.message||q?.error||`StackNet returned ${_.status}`;return console.error(`[auth-callback] Verify failed: ${_.status}`,U),Response.json({error:"Wallet verification failed",detail:typeof U=="string"?U:void 0},{status:401})}let N=await _.json();R=N.data?.session||N.session||N.data||N,console.log(`[auth-callback] Verify OK, sessionData keys: ${Object.keys(R||{}).join(", ")}`);}else return h||g?Response.json({error:"Use /api/auth/otp for OTP verification"},{status:400}):Response.json({error:"Provide wallet signature or OTP code"},{status:400});if(!R?.jwt)return Response.json({error:"Authentication failed \u2014 no session returned"},{status:401});let S=JSON.parse(Buffer.from(R.jwt.split(".")[1],"base64url").toString()),b=Math.floor(Date.now()/1e3),T={...S,exp:b+o,iat:b},P=$(T,e.authSecret),J={userId:S.sub||S.user_id||S.session_id||S.global_id||"",address:R.address||S.address,chain:R.chain||c,expiresAt:Date.now()+s*1e3,authMethod:c?`web3:${c}`:"otp"},H=new Headers({"Content-Type":"application/json"}),L=e.secureCookies!==false?"; Secure":"",F=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";H.append("Set-Cookie",`stackauth_jwt=${P}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${L}${F}`);let z=Buffer.from(JSON.stringify(J)).toString("base64url");return H.append("Set-Cookie",`stackauth_session=${z}; Path=/; SameSite=Lax; Max-Age=${s}${L}${F}`),n.generateToken(H),new Response(JSON.stringify({user:J}),{status:200,headers:H})}}function X(e,r){if(!r)return null;try{let t=v(e);if(!t||t.exp&&t.exp<Math.floor(Date.now()/1e3))return null;let n=Buffer.from(JSON.stringify({alg:"HS256",typ:"JWT"})).toString("base64url"),o=Buffer.from(JSON.stringify(t)).toString("base64url"),s=createHmac("sha256",r).update(`${n}.${o}`).digest("base64url");return `${n}.${o}.${s}`}catch{return null}}function E(e,r){let t=X(e,r);return t?{Cookie:`stackauth_jwt=${t}`}:{Cookie:`stackauth_jwt=${e}`}}function A(e){let r=e.headers.get("cookie");if(r){let n=r.split(";").map(o=>o.trim()).find(o=>o.startsWith("stackauth_jwt="));if(n)return n.slice(14)}let t=e.headers.get("authorization");return t?.startsWith("Bearer ")?t.slice(7):null}function ie(e){return async function(t){let n=A(t);if(n){let y=v(n),m=y?.session_id||y?.sub;if(m)try{await fetch(`${e.stacknetUrl}/api/v2/sessions/${m}`,{method:"DELETE",signal:AbortSignal.timeout(5e3)});}catch{}}let o=e.secureCookies!==false?"; Secure":"",s=e.cookieDomain?`; Domain=${e.cookieDomain}`:"",i=new Headers({"Content-Type":"application/json"});return i.append("Set-Cookie",`stackauth_jwt=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${o}${s}`),i.append("Set-Cookie",`stackauth_session=; Path=/; SameSite=Lax; Max-Age=0${o}${s}`),i.append("Set-Cookie",`__csrf=; Path=/; SameSite=Lax; Max-Age=0${o}${s}`),new Response(JSON.stringify({success:true}),{status:200,headers:i})}}function ce(e){let r=e.jwtExpiry||900,t=e.sessionMaxAge||604800;return async function(o){let s=A(o);if(!s)return Response.json({session:null},{status:200});let i=M(s,e.authSecret);if(!i)return Response.json({session:null},{status:200});let m={userId:i.sub||i.user_id||i.session_id||i.global_id||"",address:i.address,chain:i.chain,expiresAt:i.session_expires_at||(i.exp?i.exp*1e3:Date.now()+t*1e3),planId:i.plan_id,authMethod:i.auth_method},d=new Headers({"Content-Type":"application/json"}),l=D(s,e.authSecret,r,300);if(l){let a=e.secureCookies!==false?"; Secure":"",c=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";d.append("Set-Cookie",`stackauth_jwt=${l}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${t}${a}${c}`);}return new Response(JSON.stringify({session:m}),{status:200,headers:d})}}function pe(e,r){if(e.length!==r.length)return false;try{return timingSafeEqual(Buffer.from(e),Buffer.from(r))}catch{return false}}function de(e){let r=e.rateLimiter||C({maxRequests:5,windowMs:3e5}),t=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;return async function(i){let y=O(i),m=await r.check(`otp:${y}`);if(!m.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(m.retryAfter||300)}});let d;try{d=await i.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{code:l}=d;if(!l||typeof l!="string"||l.length!==6)return Response.json({error:"Invalid code format"},{status:400});if(!pe(l,e.otpSecret))return Response.json({error:"Invalid code"},{status:401});let a=Math.floor(Date.now()/1e3),u={sub:`otp:${createHash("sha256").update(`otp:${l}:${Date.now()}`).digest("hex").slice(0,32)}`,auth_method:"otp",iat:a,exp:a+n},p=$(u,e.authSecret),f={userId:u.sub,expiresAt:Date.now()+o*1e3,authMethod:"otp"},h=new Headers({"Content-Type":"application/json"}),g=e.secureCookies!==false?"; Secure":"",k=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";h.append("Set-Cookie",`stackauth_jwt=${p}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${g}${k}`);let w=Buffer.from(JSON.stringify(f)).toString("base64url");return h.append("Set-Cookie",`stackauth_session=${w}; Path=/; SameSite=Lax; Max-Age=${o}${g}${k}`),t.generateToken(h),new Response(JSON.stringify({success:true,data:{user:f}}),{status:200,headers:h})}}function fe(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),n=j({secure:e.secureCookies!==false}),o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;async function i(m){let d=new URL(m.url),l=d.searchParams.get("provider"),a=d.searchParams.get("redirectUri")||d.searchParams.get("redirect_uri"),c=d.searchParams.get("stackId")||e.stackId;if(!l)return Response.json({error:"Missing provider parameter"},{status:400});if(!a)return Response.json({error:"Missing redirectUri parameter"},{status:400});try{let u=await fetch(`${e.stacknetUrl}/api/v2/stacks/${c}/auth/oauth/${l}/initiate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({redirectUrl:a}),signal:AbortSignal.timeout(1e4)});if(!u.ok){let h=await u.json().catch(()=>({}));return Response.json({error:h.error?.message||`Failed to start OAuth flow: ${u.statusText}`},{status:u.status})}let p=await u.json(),f=p.data||p;return Response.json({redirect_url:f.url,state:f.state})}catch(u){return Response.json({error:u.message||"Failed to start OAuth flow"},{status:500})}}async function y(m){let d=O(m),l=await t.check(`oauth:${d}`);if(!l.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(l.retryAfter||60)}});let a;try{a=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{provider:c,code:u,state:p,stackId:f}=a,h=f||e.stackId;if(!c||!u||!p)return Response.json({error:"Missing provider, code, or state"},{status:400});try{let g=await fetch(`${e.stacknetUrl}/api/v2/stacks/${h}/auth/oauth/${c}/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:u,state:p}),signal:AbortSignal.timeout(1e4)});if(!g.ok){let L=await g.json().catch(()=>({}));return Response.json({error:L.error?.message||`OAuth verification failed: ${g.statusText}`},{status:401})}let k=await g.json(),w=k.data?.session||k.session||k.data||k;if(!w?.jwt)return Response.json({error:"OAuth authentication failed \u2014 no session returned"},{status:401});let x=JSON.parse(Buffer.from(w.jwt.split(".")[1],"base64url").toString()),R=Math.floor(Date.now()/1e3),S=$({...x,exp:R+o,iat:R},e.authSecret),T={userId:x.sub||x.user_id||x.session_id||x.global_id||"",address:w.address||x.address,chain:void 0,expiresAt:Date.now()+s*1e3,authMethod:`oauth:${c}`},P=new Headers({"Content-Type":"application/json"}),I=e.secureCookies!==!1?"; Secure":"",J=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";P.append("Set-Cookie",`stackauth_jwt=${S}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${I}${J}`);let H=Buffer.from(JSON.stringify(T)).toString("base64url");return P.append("Set-Cookie",`stackauth_session=${H}; Path=/; SameSite=Lax; Max-Age=${s}${I}${J}`),n.generateToken(P),new Response(JSON.stringify({user:T}),{status:200,headers:P})}catch(g){return Response.json({error:g.message||"OAuth callback failed"},{status:500})}}return {startFlow:i,handleCallback:y}}function me(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),n=j({secure:e.secureCookies!==false}),o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;return async function(y){let m=O(y),d=await t.check(`google-onetap:${m}`);if(!d.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(d.retryAfter||60)}});let l;try{l=await y.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{credential:a,stackId:c}=l,u=c||e.stackId;if(!a)return Response.json({error:"Missing credential"},{status:400});let p;try{let f=a.split(".");if(f.length!==3)throw new Error("Invalid JWT");p=JSON.parse(Buffer.from(f[1],"base64url").toString());}catch{return Response.json({error:"Invalid credential format"},{status:400})}try{let f=await fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${encodeURIComponent(a)}`,{signal:AbortSignal.timeout(1e4)});if(!f.ok)return Response.json({error:"Google credential verification failed"},{status:401});let h=await f.json();if(!h.sub||!h.email)return Response.json({error:"Invalid Google token \u2014 missing user info"},{status:401})}catch{return Response.json({error:"Failed to verify Google credential"},{status:500})}try{let f=await fetch(`${e.stacknetUrl}/api/v2/stacks/${u}/auth/oauth/google/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:a,google_id:p.sub,email:p.email,name:p.name,picture:p.picture,one_tap:!0}),signal:AbortSignal.timeout(1e4)});if(!f.ok){let k=Math.floor(Date.now()/1e3),w=p.sub,x=$({sub:w,global_id:`google:${w}`,stack_id:u,chain:"google",email:p.email,credentials:["oauth:google"],iat:k,exp:k+o,iss:"stackauth.network",signed_by:["local"]},e.authSecret),R={userId:w,address:p.email,chain:void 0,expiresAt:Date.now()+s*1e3,authMethod:"oauth:google"},S=new Headers({"Content-Type":"application/json"}),b=e.secureCookies!==!1?"; Secure":"",T=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";S.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${b}${T}`);let P=Buffer.from(JSON.stringify(R)).toString("base64url");return S.append("Set-Cookie",`stackauth_session=${P}; Path=/; SameSite=Lax; Max-Age=${s}${b}${T}`),n.generateToken(S),new Response(JSON.stringify({user:R}),{status:200,headers:S})}let h=await f.json(),g=h.data?.session||h.session||h.data||h;if(g?.jwt){let k=JSON.parse(Buffer.from(g.jwt.split(".")[1],"base64url").toString()),w=Math.floor(Date.now()/1e3),x=$({...k,exp:w+o,iat:w},e.authSecret),S={userId:k.sub||k.user_id||p.sub,address:p.email||g.address,chain:void 0,expiresAt:Date.now()+s*1e3,authMethod:"oauth:google"},b=new Headers({"Content-Type":"application/json"}),T=e.secureCookies!==!1?"; Secure":"",P=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";b.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${T}${P}`);let I=Buffer.from(JSON.stringify(S)).toString("base64url");return b.append("Set-Cookie",`stackauth_session=${I}; Path=/; SameSite=Lax; Max-Age=${s}${T}${P}`),n.generateToken(b),new Response(JSON.stringify({user:S}),{status:200,headers:b})}return Response.json({error:"No session returned"},{status:401})}catch(f){return Response.json({error:f.message||"Google One Tap authentication failed"},{status:500})}}}function ye(e){let r=j({secure:e.secureCookies!==false}),t=e.rateLimiter||C({maxRequests:20,windowMs:6e4}),n=e.stacknetJwtSecret||e.authSecret,o=e.jwtExpiry||900,s=e.sessionMaxAge||604800;function i(a){let c=A(a);if(!c)return null;let u=M(c,e.authSecret);return u?{jwt:c,payload:u}:null}function y(a,c){let u=D(a,e.authSecret,o,300);if(u){let p=e.secureCookies!==false?"; Secure":"",f=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";c.append("Set-Cookie",`stackauth_jwt=${u}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${s}${p}${f}`);}}async function m(a,c){let u=i(a);if(!u)return Response.json({error:"Unauthorized"},{status:401});let p=E(u.jwt,n),f=await fetch(`${e.stacknetUrl}${c}`,{headers:p,signal:AbortSignal.timeout(15e3)}),h=await f.json().catch(()=>({})),g=new Headers({"Content-Type":"application/json"});return y(u.jwt,g),new Response(JSON.stringify(h),{status:f.status,headers:g})}async function d(a,c,u){let p=i(a);if(!p)return Response.json({error:"Unauthorized"},{status:401});let f=r.validateRequest(a);if(!f.valid)return Response.json({error:f.error||"CSRF validation failed"},{status:403});let h=p.payload.sub||p.payload.user_id||"unknown";if(!(await t.check(`billing:${h}`)).allowed)return Response.json({error:"Too many requests"},{status:429});let k=await a.json().catch(()=>({})),w=E(p.jwt,n);w["Content-Type"]="application/json";let x=await fetch(`${e.stacknetUrl}${c}`,{method:"POST",headers:w,body:JSON.stringify({...k,...u}),signal:AbortSignal.timeout(15e3)}),R=await x.json().catch(()=>({})),S=new Headers({"Content-Type":"application/json"});return y(p.jwt,S),new Response(JSON.stringify(R),{status:x.status,headers:S})}let l=`/api/v2/stacks/${e.stackId}`;return {plans:{GET:async a=>{let c=await fetch(`${e.stacknetUrl}${l}/plans`,{signal:AbortSignal.timeout(1e4)}),u=await c.json().catch(()=>({}));return Response.json(u,{status:c.status})}},subscription:{GET:(a=>m(a,`${l}/subscription`))},subscribe:{POST:(a=>{let c=new URL(a.url).origin;return d(a,`${l}/subscribe`,{successUrl:`${c}/billing/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing`})})},cancel:{POST:(a=>d(a,`${l}/cancel-subscription`))},usage:{GET:(a=>m(a,"/v1/account/usage"))},history:{GET:(a=>m(a,`${l}/billing`))},prepaid:{POST:(a=>{let c=new URL(a.url).origin;return d(a,`${l}/prepaid`,{successUrl:`${c}/pricing/prepaid/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing/prepaid`})})},verifyPrepaid:{POST:(a=>d(a,`${l}/verify-prepaid`))},verifySession:{POST:(a=>d(a,`${l}/verify-session`))}}}function he(e){return async function(t){let n=t.headers.get("stripe-signature");if(!n)return Response.json({error:"Missing Stripe signature"},{status:400});try{let o=await t.text(),s=await fetch(`${e.stacknetUrl}/api/v2/stacks/${e.stackId}/webhook/stripe`,{method:"POST",headers:{"Content-Type":"application/json","stripe-signature":n},body:o,signal:AbortSignal.timeout(1e4)}),i=await s.json().catch(()=>({received:!0}));return Response.json(i,{status:s.status})}catch{return Response.json({error:"Webhook processing failed"},{status:502})}}}function B(){return {"Strict-Transport-Security":"max-age=63072000; includeSubDomains; preload","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-XSS-Protection":"0","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()"}}function ge(e){return async r=>{let t=await e(r),n=B(),o=new Headers(t.headers);for(let[s,i]of Object.entries(n))o.set(s,i);return new Response(t.body,{status:t.status,statusText:t.statusText,headers:o})}}function Se(){return Object.entries(B()).map(([e,r])=>({key:e,value:r}))}
|
|
1
|
+
import {createHmac,timingSafeEqual,randomBytes,createHash}from'crypto';function G(e){return Buffer.from(e).toString("base64url")}function ee(e){return Buffer.from(e,"base64url").toString()}function v(e){try{let r=e.split(".");return r.length!==3?null:JSON.parse(ee(r[1]))}catch{return null}}function $(e,r){let t=G(JSON.stringify({alg:"HS256",typ:"JWT"})),a=G(JSON.stringify(e)),n=createHmac("sha256",r).update(`${t}.${a}`).digest("base64url");return `${t}.${a}.${n}`}function K(e,r){try{let t=e.split(".");if(t.length!==3)return !1;let[a,n,o]=t,i=createHmac("sha256",r).update(`${a}.${n}`).digest("base64url"),y=Buffer.from(o),m=Buffer.from(i);return y.length!==m.length?!1:timingSafeEqual(y,m)}catch{return false}}function M(e,r){if(!K(e,r))return null;let t=v(e);return !t||t.exp&&t.exp<Math.floor(Date.now()/1e3)?null:t}function D(e,r,t=900,a=300){let n=M(e,r);return !n?.exp||n.exp*1e3-Date.now()>a*1e3?null:$({...n,exp:Math.floor(Date.now()/1e3)+t},r)}function W(e=32){return randomBytes(e).toString("hex")}function O(e){return e.headers.get("x-forwarded-for")?.split(",")[0]?.trim()||e.headers.get("x-real-ip")||"unknown"}var re="__csrf",se="x-csrf-token";function j(e={}){let r=e.cookieName||re,t=e.headerName||se,a=e.tokenLength||32,n=e.secure!==false;return {generateToken(o){let i=W(a),y=[`${r}=${i}`,"Path=/","SameSite=Lax"];return n&&y.push("Secure"),o.append("Set-Cookie",y.join("; ")),i},validateRequest(o){let i=o.headers.get("cookie");if(!i)return {valid:false,error:"No cookies present"};let y=i.split(";").map(l=>l.trim()).find(l=>l.startsWith(`${r}=`))?.slice(r.length+1);if(!y)return {valid:false,error:"CSRF cookie missing"};let m=o.headers.get(t);if(!m)return {valid:false,error:"CSRF header missing"};try{let l=Buffer.from(y),u=Buffer.from(m);return l.length!==u.length?{valid:!1,error:"CSRF token mismatch"}:timingSafeEqual(l,u)?{valid:!0}:{valid:!1,error:"CSRF token mismatch"}}catch{return {valid:false,error:"CSRF validation failed"}}},cookieName:r,headerName:t}}function C(e){let r=new Map,t=setInterval(()=>{let a=Date.now();for(let[n,o]of r)a>=o.resetAt&&r.delete(n);},6e4);return typeof t=="object"&&"unref"in t&&t.unref(),{async check(a){let n=Date.now(),o=r.get(a);return (!o||n>=o.resetAt)&&(o={count:0,resetAt:n+e.windowMs},r.set(a,o)),o.count++,o.count>e.maxRequests?{allowed:false,remaining:0,retryAfter:Math.ceil((o.resetAt-n)/1e3)}:{allowed:true,remaining:e.maxRequests-o.count}}}}function oe(){let e=new Map,r=setInterval(()=>{let t=Date.now();for(let[a,n]of e)t>=n&&e.delete(a);},6e4);return typeof r=="object"&&"unref"in r&&r.unref(),{async has(t){let a=e.get(t);return a?Date.now()>=a?(e.delete(t),false):true:false},async set(t,a){e.set(t,Date.now()+a*1e3);}}}function ne(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),a=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;e.stacknetJwtSecret||e.authSecret;return async function(m){let l=O(m),u=await t.check(`auth:${l}`);if(!u.allowed)return Response.json({error:"Too many login attempts. Please wait."},{status:429,headers:{"Retry-After":String(u.retryAfter||60)}});let s;try{s=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{chain:c,message:p,signature:d,publicKey:f,otp:h,code:g,redirectUrl:k,stackId:w}=s,x=w||e.stackId,R;if(c&&p&&d){let Y={"Content-Type":"application/json"},_=await fetch(`${e.stacknetUrl}/api/v2/stacks/${x}/auth/web3/verify`,{method:"POST",headers:Y,body:JSON.stringify({chain:c,message:p,signature:d,public_key:f}),signal:AbortSignal.timeout(1e4)});if(!_.ok){let q=await _.json().catch(()=>({})),U=q?.error?.message||q?.message||q?.error||`StackNet returned ${_.status}`;return console.error(`[auth-callback] Verify failed: ${_.status}`,U),Response.json({error:"Wallet verification failed",detail:typeof U=="string"?U:void 0},{status:401})}let N=await _.json();R=N.data?.session||N.session||N.data||N,console.log(`[auth-callback] Verify OK, sessionData keys: ${Object.keys(R||{}).join(", ")}`);}else return h||g?Response.json({error:"Use /api/auth/otp for OTP verification"},{status:400}):Response.json({error:"Provide wallet signature or OTP code"},{status:400});if(!R?.jwt)return Response.json({error:"Authentication failed \u2014 no session returned"},{status:401});let S=JSON.parse(Buffer.from(R.jwt.split(".")[1],"base64url").toString()),b=Math.floor(Date.now()/1e3),T={...S,exp:b+n,iat:b},P=$(T,e.authSecret),J={userId:S.sub||S.user_id||S.session_id||S.global_id||"",address:R.address||S.address,chain:R.chain||c,expiresAt:Date.now()+o*1e3,authMethod:c?`web3:${c}`:"otp"},H=new Headers({"Content-Type":"application/json"}),L=e.secureCookies!==false?"; Secure":"",F=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";H.append("Set-Cookie",`stackauth_jwt=${P}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${L}${F}`);let z=Buffer.from(JSON.stringify(J)).toString("base64url");return H.append("Set-Cookie",`stackauth_session=${z}; Path=/; SameSite=Lax; Max-Age=${o}${L}${F}`),a.generateToken(H),new Response(JSON.stringify({user:J}),{status:200,headers:H})}}function X(e,r){if(!r)return null;try{let t=v(e);if(!t||t.exp&&t.exp<Math.floor(Date.now()/1e3))return null;let a=Buffer.from(JSON.stringify({alg:"HS256",typ:"JWT"})).toString("base64url"),n=Buffer.from(JSON.stringify(t)).toString("base64url"),o=createHmac("sha256",r).update(`${a}.${n}`).digest("base64url");return `${a}.${n}.${o}`}catch{return null}}function E(e,r){let t=X(e,r);return t?{Cookie:`stackauth_jwt=${t}`}:{Cookie:`stackauth_jwt=${e}`}}function A(e){let r=e.headers.get("cookie");if(r){let a=r.split(";").map(n=>n.trim()).find(n=>n.startsWith("stackauth_jwt="));if(a)return a.slice(14)}let t=e.headers.get("authorization");return t?.startsWith("Bearer ")?t.slice(7):null}function ie(e){return async function(t){let a=A(t);if(a){let y=v(a),m=y?.session_id||y?.sub;if(m)try{await fetch(`${e.stacknetUrl}/api/v2/sessions/${m}`,{method:"DELETE",signal:AbortSignal.timeout(5e3)});}catch{}}let n=e.secureCookies!==false?"; Secure":"",o=e.cookieDomain?`; Domain=${e.cookieDomain}`:"",i=new Headers({"Content-Type":"application/json"});return i.append("Set-Cookie",`stackauth_jwt=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${n}${o}`),i.append("Set-Cookie",`stackauth_session=; Path=/; SameSite=Lax; Max-Age=0${n}${o}`),i.append("Set-Cookie",`__csrf=; Path=/; SameSite=Lax; Max-Age=0${n}${o}`),new Response(JSON.stringify({success:true}),{status:200,headers:i})}}function ce(e){let r=e.jwtExpiry||900,t=e.sessionMaxAge||604800;return async function(n){let o=A(n);if(!o)return Response.json({session:null},{status:200});let i=M(o,e.authSecret);if(!i)return Response.json({session:null},{status:200});let m={userId:i.sub||i.user_id||i.session_id||i.global_id||"",address:i.address,chain:i.chain,expiresAt:i.session_expires_at||(i.exp?i.exp*1e3:Date.now()+t*1e3),planId:i.plan_id,authMethod:i.auth_method},l=new Headers({"Content-Type":"application/json"}),u=D(o,e.authSecret,r,300);if(u){let s=e.secureCookies!==false?"; Secure":"",c=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";l.append("Set-Cookie",`stackauth_jwt=${u}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${t}${s}${c}`);}return new Response(JSON.stringify({session:m}),{status:200,headers:l})}}function pe(e,r){if(e.length!==r.length)return false;try{return timingSafeEqual(Buffer.from(e),Buffer.from(r))}catch{return false}}function de(e){let r=e.rateLimiter||C({maxRequests:5,windowMs:3e5}),t=j({secure:e.secureCookies!==false}),a=e.jwtExpiry||900,n=e.sessionMaxAge||604800;return async function(i){let y=O(i),m=await r.check(`otp:${y}`);if(!m.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(m.retryAfter||300)}});let l;try{l=await i.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{code:u}=l;if(!u||typeof u!="string"||u.length!==6)return Response.json({error:"Invalid code format"},{status:400});if(!pe(u,e.otpSecret))return Response.json({error:"Invalid code"},{status:401});let s=Math.floor(Date.now()/1e3),p={sub:`otp:${createHash("sha256").update(`otp:${u}:${Date.now()}`).digest("hex").slice(0,32)}`,auth_method:"otp",iat:s,exp:s+a},d=$(p,e.authSecret),f={userId:p.sub,expiresAt:Date.now()+n*1e3,authMethod:"otp"},h=new Headers({"Content-Type":"application/json"}),g=e.secureCookies!==false?"; Secure":"",k=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";h.append("Set-Cookie",`stackauth_jwt=${d}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${n}${g}${k}`);let w=Buffer.from(JSON.stringify(f)).toString("base64url");return h.append("Set-Cookie",`stackauth_session=${w}; Path=/; SameSite=Lax; Max-Age=${n}${g}${k}`),t.generateToken(h),new Response(JSON.stringify({success:true,data:{user:f}}),{status:200,headers:h})}}function fe(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),a=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;async function i(m){let l=new URL(m.url),u=l.searchParams.get("provider"),s=l.searchParams.get("redirectUri")||l.searchParams.get("redirect_uri"),c=l.searchParams.get("stackId")||e.stackId;if(!u)return Response.json({error:"Missing provider parameter"},{status:400});if(!s)return Response.json({error:"Missing redirectUri parameter"},{status:400});try{let p=await fetch(`${e.stacknetUrl}/api/v2/stacks/${c}/auth/oauth/${u}/initiate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({redirectUrl:s}),signal:AbortSignal.timeout(1e4)});if(!p.ok){let h=await p.json().catch(()=>({}));return Response.json({error:h.error?.message||`Failed to start OAuth flow: ${p.statusText}`},{status:p.status})}let d=await p.json(),f=d.data||d;return Response.json({redirect_url:f.url,state:f.state})}catch(p){return Response.json({error:p.message||"Failed to start OAuth flow"},{status:500})}}async function y(m){let l=O(m),u=await t.check(`oauth:${l}`);if(!u.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(u.retryAfter||60)}});let s;try{s=await m.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{provider:c,code:p,state:d,stackId:f}=s,h=f||e.stackId;if(!c||!p||!d)return Response.json({error:"Missing provider, code, or state"},{status:400});try{let g=await fetch(`${e.stacknetUrl}/api/v2/stacks/${h}/auth/oauth/${c}/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:p,state:d}),signal:AbortSignal.timeout(1e4)});if(!g.ok){let L=await g.json().catch(()=>({}));return Response.json({error:L.error?.message||`OAuth verification failed: ${g.statusText}`},{status:401})}let k=await g.json(),w=k.data?.session||k.session||k.data||k;if(!w?.jwt)return Response.json({error:"OAuth authentication failed \u2014 no session returned"},{status:401});let x=JSON.parse(Buffer.from(w.jwt.split(".")[1],"base64url").toString()),R=Math.floor(Date.now()/1e3),S=$({...x,exp:R+n,iat:R},e.authSecret),T={userId:x.sub||x.user_id||x.session_id||x.global_id||"",address:w.address||x.address,chain:void 0,expiresAt:Date.now()+o*1e3,authMethod:`oauth:${c}`},P=new Headers({"Content-Type":"application/json"}),I=e.secureCookies!==!1?"; Secure":"",J=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";P.append("Set-Cookie",`stackauth_jwt=${S}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${I}${J}`);let H=Buffer.from(JSON.stringify(T)).toString("base64url");return P.append("Set-Cookie",`stackauth_session=${H}; Path=/; SameSite=Lax; Max-Age=${o}${I}${J}`),a.generateToken(P),new Response(JSON.stringify({user:T}),{status:200,headers:P})}catch(g){return Response.json({error:g.message||"OAuth callback failed"},{status:500})}}return {startFlow:i,handleCallback:y}}function me(e,r){let t=r?.rateLimiter||C({maxRequests:10,windowMs:6e4}),a=j({secure:e.secureCookies!==false}),n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;return async function(y){let m=O(y),l=await t.check(`google-onetap:${m}`);if(!l.allowed)return Response.json({error:"Too many attempts. Please wait."},{status:429,headers:{"Retry-After":String(l.retryAfter||60)}});let u;try{u=await y.json();}catch{return Response.json({error:"Invalid request body"},{status:400})}let{credential:s,stackId:c}=u,p=c||e.stackId;if(!s)return Response.json({error:"Missing credential"},{status:400});let d;try{let f=s.split(".");if(f.length!==3)throw new Error("Invalid JWT");d=JSON.parse(Buffer.from(f[1],"base64url").toString());}catch{return Response.json({error:"Invalid credential format"},{status:400})}try{let f=await fetch(`https://oauth2.googleapis.com/tokeninfo?id_token=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(1e4)});if(!f.ok)return Response.json({error:"Google credential verification failed"},{status:401});let h=await f.json();if(!h.sub||!h.email)return Response.json({error:"Invalid Google token \u2014 missing user info"},{status:401})}catch{return Response.json({error:"Failed to verify Google credential"},{status:500})}try{let f=await fetch(`${e.stacknetUrl}/api/v2/stacks/${p}/auth/oauth/google/callback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({credential:s,google_id:d.sub,email:d.email,name:d.name,picture:d.picture,one_tap:!0}),signal:AbortSignal.timeout(1e4)});if(!f.ok){let k=Math.floor(Date.now()/1e3),w=d.sub,x=$({sub:w,global_id:`google:${w}`,stack_id:p,chain:"google",email:d.email,credentials:["oauth:google"],iat:k,exp:k+n,iss:"stackauth.network",signed_by:["local"]},e.authSecret),R={userId:w,address:d.email,chain:void 0,expiresAt:Date.now()+o*1e3,authMethod:"oauth:google"},S=new Headers({"Content-Type":"application/json"}),b=e.secureCookies!==!1?"; Secure":"",T=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";S.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${b}${T}`);let P=Buffer.from(JSON.stringify(R)).toString("base64url");return S.append("Set-Cookie",`stackauth_session=${P}; Path=/; SameSite=Lax; Max-Age=${o}${b}${T}`),a.generateToken(S),new Response(JSON.stringify({user:R}),{status:200,headers:S})}let h=await f.json(),g=h.data?.session||h.session||h.data||h;if(g?.jwt){let k=JSON.parse(Buffer.from(g.jwt.split(".")[1],"base64url").toString()),w=Math.floor(Date.now()/1e3),x=$({...k,exp:w+n,iat:w},e.authSecret),S={userId:k.sub||k.user_id||d.sub,address:d.email||g.address,chain:void 0,expiresAt:Date.now()+o*1e3,authMethod:"oauth:google"},b=new Headers({"Content-Type":"application/json"}),T=e.secureCookies!==!1?"; Secure":"",P=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";b.append("Set-Cookie",`stackauth_jwt=${x}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${T}${P}`);let I=Buffer.from(JSON.stringify(S)).toString("base64url");return b.append("Set-Cookie",`stackauth_session=${I}; Path=/; SameSite=Lax; Max-Age=${o}${T}${P}`),a.generateToken(b),new Response(JSON.stringify({user:S}),{status:200,headers:b})}return Response.json({error:"No session returned"},{status:401})}catch(f){return Response.json({error:f.message||"Google One Tap authentication failed"},{status:500})}}}function ye(e){let r=j({secure:e.secureCookies!==false}),t=e.rateLimiter||C({maxRequests:20,windowMs:6e4}),a=e.stacknetJwtSecret||e.authSecret,n=e.jwtExpiry||900,o=e.sessionMaxAge||604800;function i(s){let c=A(s);if(!c)return null;let p=M(c,e.authSecret);return p?{jwt:c,payload:p}:null}function y(s,c){let p=D(s,e.authSecret,n,300);if(p){let d=e.secureCookies!==false?"; Secure":"",f=e.cookieDomain?`; Domain=${e.cookieDomain}`:"";c.append("Set-Cookie",`stackauth_jwt=${p}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${o}${d}${f}`);}}async function m(s,c){let p=i(s);if(!p)return Response.json({error:"Unauthorized"},{status:401});let d=E(p.jwt,a),f=await fetch(`${e.stacknetUrl}${c}`,{headers:d,signal:AbortSignal.timeout(15e3)}),h=await f.json().catch(()=>({})),g=new Headers({"Content-Type":"application/json"});return y(p.jwt,g),new Response(JSON.stringify(h),{status:f.status,headers:g})}async function l(s,c,p){let d=i(s);if(!d)return Response.json({error:"Unauthorized"},{status:401});let f=r.validateRequest(s);if(!f.valid)return Response.json({error:f.error||"CSRF validation failed"},{status:403});let h=d.payload.sub||d.payload.user_id||"unknown";if(!(await t.check(`billing:${h}`)).allowed)return Response.json({error:"Too many requests"},{status:429});let k=await s.json().catch(()=>({})),w=E(d.jwt,a);w["Content-Type"]="application/json";let x=await fetch(`${e.stacknetUrl}${c}`,{method:"POST",headers:w,body:JSON.stringify({...k,...p}),signal:AbortSignal.timeout(15e3)}),R=await x.json().catch(()=>({})),S=new Headers({"Content-Type":"application/json"});return y(d.jwt,S),new Response(JSON.stringify(R),{status:x.status,headers:S})}let u=`/api/v2/stacks/${e.stackId}`;return {plans:{GET:async s=>{let c=await fetch(`${e.stacknetUrl}${u}/plans`,{signal:AbortSignal.timeout(1e4)}),p=await c.json().catch(()=>({}));return Response.json(p,{status:c.status})}},subscription:{GET:(s=>m(s,`${u}/subscription`))},subscribe:{POST:(s=>{let c=new URL(s.url).origin;return l(s,`${u}/subscribe`,{successUrl:`${c}/billing/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing`})})},cancel:{POST:(s=>l(s,`${u}/cancel-subscription`))},usage:{GET:(s=>m(s,"/v1/account/usage"))},history:{GET:(s=>m(s,`${u}/billing`))},prepaid:{POST:(s=>{let c=new URL(s.url).origin;return l(s,`${u}/prepaid`,{successUrl:`${c}/pricing/prepaid/success?session_id={CHECKOUT_SESSION_ID}`,cancelUrl:`${c}/pricing/prepaid`})})},verifyPrepaid:{POST:(s=>l(s,`${u}/verify-prepaid`))},verifySession:{POST:(s=>l(s,`${u}/verify-session`))},subscribeSol:{POST:(s=>l(s,`${u}/subscribe-sol`))},prepaidSol:{POST:(s=>{new URL(s.url).origin;return l(s,`${u}/prepaid-sol`)})},topup:{POST:(s=>l(s,"/v1/account/topup"))}}}function he(e){return async function(t){let a=t.headers.get("stripe-signature");if(!a)return Response.json({error:"Missing Stripe signature"},{status:400});try{let n=await t.text(),o=await fetch(`${e.stacknetUrl}/api/v2/stacks/${e.stackId}/webhook/stripe`,{method:"POST",headers:{"Content-Type":"application/json","stripe-signature":a},body:n,signal:AbortSignal.timeout(1e4)}),i=await o.json().catch(()=>({received:!0}));return Response.json(i,{status:o.status})}catch{return Response.json({error:"Webhook processing failed"},{status:502})}}}function B(){return {"Strict-Transport-Security":"max-age=63072000; includeSubDomains; preload","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","X-XSS-Protection":"0","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()"}}function ge(e){return async r=>{let t=await e(r),a=B(),n=new Headers(t.headers);for(let[o,i]of Object.entries(a))n.set(o,i);return new Response(t.body,{status:t.status,statusText:t.statusText,headers:n})}}function Se(){return Object.entries(B()).map(([e,r])=>({key:e,value:r}))}
|
|
2
2
|
export{E as buildStackNetHeaders,ne as createAuthCallback,ye as createBillingProxy,j as createCSRFProtection,me as createGoogleOneTapHandler,C as createInMemoryRateLimiter,oe as createInMemoryReplayStore,ie as createLogoutHandler,fe as createOAuthHandlers,de as createOTPHandler,ce as createSessionHandler,he as createWebhookHandler,v as decodeJWTPayload,O as extractIP,A as extractJwt,W as generateToken,D as maybeRefreshJWT,Se as nextSecurityHeaders,X as resignForStackNet,B as securityHeaders,$ as signJWT,M as verifyJWT,K as verifyJWTSignature,ge as withSecurityHeaders};
|