authhero 0.147.0 → 0.148.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/authhero.cjs CHANGED
@@ -26,7 +26,7 @@
26
26
  }};
27
27
  <\/script>
28
28
  </body>
29
- </html>`;return new Response(i,{headers:{"Content-Type":"text/html"}})}async function d2(t,e,n,r,i){var m,w,h;if(!n.redirect_uri)throw new A(400,{message:"Missing redirect_uri in authParams"});if(!r.email)throw new A(400,{message:"Missing email in user"});const[o]=await t.env.data.keys.list();if(!o)throw new A(500,{message:"No signing key found"});if(!((m=e.addons)!=null&&m.samlp))throw new A(400,{message:`SAML Addon is not enabled for client ${e.id}`});const{recipient:a,audience:c}=e.addons.samlp,d=n.state||"";if(!a||!d||!r||!n.state)throw new A(400,{message:"Missing recipient or inResponseTo"});const l=JSON.parse(n.state),u=new URL(n.redirect_uri),f=await l2(t,{issuer:t.env.ISSUER,audience:c||n.client_id,destination:u.toString(),inResponseTo:l.requestId,userId:((h=(w=r.app_metadata)==null?void 0:w.vimeo)==null?void 0:h.user_id)||r.user_id,email:r.email,sessionIndex:i,signature:{privateKeyPem:o.pkcs7,cert:o.cert,kid:o.kid}});return c2(u.toString(),f,l.relayState)}async function l2(t,e){const n=e.notBefore||new Date().toISOString(),r=e.notAfter||new Date(new Date(n).getTime()+10*60*1e3).toISOString(),i=e.issueInstant||n,o=e.sessionNotOnOrAfter||r,a=e.responseId||`_${xe()}`,c=e.assertionId||`_${xe()}`,d=[{"samlp:Response":[{"saml:Issuer":[{"#text":e.issuer}]},{"samlp:Status":[{"samlp:StatusCode":[],":@":{"@_Value":"urn:oasis:names:tc:SAML:2.0:status:Success"}}]},{"saml:Assertion":[{"saml:Issuer":[{"#text":e.issuer}]},{"saml:Subject":[{"saml:NameID":[{"#text":e.email}],":@":{"@_Format":"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"}},{"saml:SubjectConfirmation":[{"saml:SubjectConfirmationData":[],":@":{"@_InResponseTo":e.inResponseTo,"@_NotOnOrAfter":r,"@_Recipient":e.destination}}],":@":{"@_Method":"urn:oasis:names:tc:SAML:2.0:cm:bearer"}}]},{"saml:Conditions":[{"saml:AudienceRestriction":[{"saml:Audience":[{"#text":e.audience}]}]}],":@":{"@_NotBefore":n,"@_NotOnOrAfter":r}},{"saml:AuthnStatement":[{"saml:AuthnContext":[{"saml:AuthnContextClassRef":[{"#text":"urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"}]}]}],":@":{"@_AuthnInstant":i,"@_SessionIndex":e.sessionIndex,"@_SessionNotOnOrAfter":o}},{"saml:AttributeStatement":[{"saml:Attribute":[{"saml:AttributeValue":[{"#text":e.userId}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_FriendlyName":"persistent","@_Name":"id","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":e.email}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"email","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"manage-account"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"default-roles-master"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"offline_access"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"view-profile"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"uma_authorization"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"manage-account-links"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}}]}],":@":{"@_xmlns":"urn:oasis:names:tc:SAML:2.0:assertion","@_ID":c,"@_IssueInstant":i,"@_Version":"2.0"}}],":@":{"@_xmlns:samlp":"urn:oasis:names:tc:SAML:2.0:protocol","@_xmlns:saml":"urn:oasis:names:tc:SAML:2.0:assertion","@_Destination":e.destination,"@_ID":a,"@_InResponseTo":e.inResponseTo,"@_IssueInstant":i,"@_Version":"2.0"}}];let u=new a2.XMLBuilder({ignoreAttributes:!1,suppressEmptyNode:!0,preserveOrder:!0}).build(d);if(e.signature){const m=await fetch(t.env.SAML_SIGN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({xmlContent:u,privateKey:e.signature.privateKeyPem,publicCert:e.signature.cert})});if(!m.ok)throw new Error(`Failed to sign SAML response: ${m.status}`);u=await m.text()}return e.encode===!1?u:btoa(u)}var u2={deno:"Deno",bun:"Bun",workerd:"Cloudflare-Workers",node:"Node.js"},p2=()=>{var n,r;const t=globalThis;if(typeof navigator<"u"&&typeof navigator.userAgent=="string"){for(const[i,o]of Object.entries(u2))if(f2(o))return i}return typeof(t==null?void 0:t.EdgeRuntime)=="string"?"edge-light":(t==null?void 0:t.fastly)!==void 0?"fastly":((r=(n=t==null?void 0:t.process)==null?void 0:n.release)==null?void 0:r.name)==="node"?"node":"other"},f2=t=>navigator.userAgent.startsWith(t);function It(t,e){p2()==="workerd"&&t.executionCtx.waitUntil(e)}function jt(t){var e,n,r,i;return{auth0Client:(e=t.query("auth0Client"))==null?void 0:e.slice(0,255),ip:(n=t.header("x-real-ip"))==null?void 0:n.slice(0,45),useragent:(r=t.header("user-agent"))==null?void 0:r.slice(0,512),countryCode:(i=t.header("cf-ipcountry"))==null?void 0:i.slice(0,2)}}const Lu=["sub","iss","aud","exp","nbf","iat","jti"];async function As(t,e){var v,b;const{authParams:n,user:r,client:i,session_id:o}=e,c=(await t.env.data.keys.list()).filter(E=>!E.revoked_at||new Date(E.revoked_at)>new Date),d=c[c.length-1];if(!(d!=null&&d.pkcs7))throw new A(500,{message:"No signing key available"});const l=O_(d.pkcs7),u=t.var.custom_domain?`https://${t.var.custom_domain}/`:t.env.ISSUER,f={aud:n.audience||"default",scope:n.scope||"",sub:(r==null?void 0:r.user_id)||n.client_id,iss:u,tenant_id:t.var.tenant_id,sid:o},m=r&&((v=n.scope)!=null&&v.split(" ").includes("openid"))?{aud:n.client_id,sub:r.user_id,iss:u,sid:o,nonce:n.nonce,given_name:r.given_name,family_name:r.family_name,nickname:r.nickname,picture:r.picture,locale:r.locale,name:r.name,email:r.email,email_verified:r.email_verified}:void 0;(b=t.env.hooks)!=null&&b.onExecuteCredentialsExchange&&await t.env.hooks.onExecuteCredentialsExchange({client:i,user:r,request:{ip:t.req.header("x-real-ip")||"",user_agent:t.req.header("user-agent")||"",method:t.req.method,url:t.req.url},scope:n.scope||"",grant_type:""},{accessToken:{setCustomClaim:(E,C)=>{if(Lu.includes(E))throw new Error(`Cannot overwrite reserved claim '${E}'`);f[E]=C}},idToken:{setCustomClaim:(E,C)=>{if(Lu.includes(E))throw new Error(`Cannot overwrite reserved claim '${E}'`);m&&(m[E]=C)}},access:{deny:E=>{throw new A(400,{message:`Access denied: ${E}`})}}});const w={includeIssuedTimestamp:!0,expiresIn:new Hd(1,"d"),headers:{kid:d.kid}},h=await ju("RS256",l,f,w),y=m?await ju("RS256",l,m,w):void 0;return{access_token:h,refresh_token:e.refresh_token,id_token:y,token_type:"Bearer",expires_in:86400}}async function y0(t,e){return{code:(await t.env.data.codes.create(e.client.tenant.id,{code_id:xe(),user_id:e.user.user_id,code_type:"authorization_code",login_id:e.login_id,expires_at:new Date(Date.now()+B_*1e3).toISOString()})).code_id,state:e.authParams.state}}async function h2(t,e){const{client:n,scope:r,audience:i=n.tenant.audience,session_id:o}=e,a=jt(t.req);return await t.env.data.refreshTokens.create(n.tenant.id,{id:xe(),session_id:o,client_id:n.id,idle_expires_at:new Date(Date.now()+ks*1e3).toISOString(),user_id:e.user.user_id,device:{last_ip:a.ip||"",initial_ip:a.ip||"",last_user_agent:a.useragent||"",initial_user_agent:a.useragent||"",initial_asn:"",last_asn:""},resource_servers:[{audience:i,scopes:r}],rotating:!1})}async function w0(t,{user:e,client:n,loginSession:r}){const i=await t.env.data.sessions.create(n.tenant.id,{id:xe(),user_id:e.user_id,idle_expires_at:new Date(Date.now()+ks*1e3).toISOString(),device:{last_ip:t.req.header("x-real-ip")||"",initial_ip:t.req.header("x-real-ip")||"",last_user_agent:t.req.header("user-agent")||"",initial_user_agent:t.req.header("user-agent")||"",initial_asn:"",last_asn:""},clients:[n.id]});return await t.env.data.loginSessions.update(n.tenant.id,r.id,{session_id:i.id}),i}async function Zt(t,e){var h,y;const{authParams:n,user:r,client:i,ticketAuth:o}=e,a=we(t,{type:he.SUCCESS_LOGIN,description:`Successful login for ${r.user_id}`,userId:r.user_id});if(It(t,t.env.data.logs.create(i.tenant.id,a)),It(t,t.env.data.users.update(i.tenant.id,r.user_id,{last_login:new Date().toISOString(),last_ip:t.req.header("x-real-ip")||"",login_count:r.login_count+1})),o){if(!e.loginSession)throw new A(500,{message:"Login session not found for ticket auth."});const v=j_(),b=xe(12),E=await t.env.data.codes.create(i.tenant.id,{code_id:xe(),code_type:"ticket",login_id:e.loginSession.id,expires_at:new Date(Date.now()+U_).toISOString(),code_verifier:[b,v].join("|")});return t.json({login_ticket:E.code_id,co_verifier:v,co_id:b})}let c=e.refreshToken,d=e.sessionId,l=r;if(!d&&((h=e.loginSession)!=null&&h.session_id)){d=e.loginSession.session_id;const v=await t.env.data.sessions.get(i.tenant.id,d);v&&!v.clients.includes(i.id)&&await t.env.data.sessions.update(i.tenant.id,d,{clients:[...v.clients,i.id]}),!c&&((y=n.scope)!=null&&y.split(" ").includes("offline_access"))&&(c=(await h2(t,{user:l,client:i,session_id:d,scope:n.scope,audience:n.audience})).id)}else if(!d){if(!e.loginSession)throw new A(500,{message:"Login session not found for creating a new session."});d=(await w0(t,{user:l,client:i,loginSession:e.loginSession})).id;const b=await b2(t,t.env.data,i.tenant.id,r,e.loginSession,{client:i,authParams:n});if(b instanceof Response)return b;l=b}if(e.authParams.response_mode===Dt.SAML_POST){if(!d)throw new A(500,{message:"Session ID not available for SAML response"});return d2(t,e.client,e.authParams,l,d)}const u=await As(t,{authParams:n,user:l,client:i,session_id:d,refresh_token:c});if(n.response_mode===Dt.WEB_MESSAGE){if(d){const v=rc(i.tenant.id,d,t.var.custom_domain||t.req.header("host")||"");v&&t.header("set-cookie",v)}else console.warn("Session ID not available for WEB_MESSAGE, cookie will not be set.");return u}const f=new Headers;if(d){const v=rc(i.tenant.id,d,t.var.custom_domain||t.req.header("host")||"");v&&f.set("set-cookie",v)}const m=n.response_type||it.CODE;if(m===it.CODE){if(!e.loginSession)throw new A(500,{message:"Login session not found for code response type."});const v=await y0(t,{user:l,client:i,authParams:n,login_id:e.loginSession.id});if(!n.redirect_uri)throw new A(400,{message:"Redirect uri not found for code response type."});const b=new URL(n.redirect_uri);return b.searchParams.set("code",v.code),v.state&&b.searchParams.set("state",v.state),f.set("location",b.toString()),new Response("Redirecting",{status:302,headers:f})}if(!n.redirect_uri)throw new A(400,{message:"Redirect uri not found for this response mode."});const w=new URL(n.redirect_uri);if(m===it.TOKEN||m===it.TOKEN_ID_TOKEN)w.hash=new URLSearchParams({access_token:u.access_token,...u.id_token&&{id_token:u.id_token},token_type:u.token_type,expires_in:u.expires_in.toString(),...n.state&&{state:n.state},...n.scope&&{scope:n.scope}}).toString();else throw new A(500,{message:`Unsupported response type ('${m}') for redirect with tokens.`});return f.set("location",w.toString()),new Response("Redirecting",{status:302,headers:f})}async function g2(t,e,n){const r=await t.env.data.tenants.get(e);if(!r)throw new Error(`Tenant not found: ${e}`);return As(t,{client:{id:t.env.ISSUER,tenant:r,created_at:new Date().toISOString(),updated_at:new Date().toISOString(),name:t.env.ISSUER,disable_sign_ups:!1,connections:[]},authParams:{client_id:t.env.ISSUER,response_type:it.TOKEN,scope:n}})}async function v0(t,e,n){const r=await g2(t,n.tenant_id,"webhook");for await(const i of e.filter(o=>"url"in o))if(!(await fetch(i.url,{method:"POST",headers:{Authorization:`Bearer ${r.access_token}`,"Content-Type":"application/json"},body:JSON.stringify(n)})).ok){const a=we(t,{type:he.FAILED_HOOK,description:`Failed to invoke hook ${i.hook_id}`});await t.env.data.logs.create(n.tenant_id,a)}}function m2(t){return async(e,n)=>{const{hooks:r}=await t.env.data.hooks.list(e);return await v0(t,r,{tenant_id:e,user:n,trigger_id:"post-user-registration"}),n}}function _2(t){return async(e,n)=>{const{hooks:r}=await t.env.data.hooks.list(e,{q:"trigger_id:pre-user-signup",page:0,per_page:100,include_totals:!1});await v0(t,r,{tenant_id:e,email:n,trigger_id:"pre-user-signup"})}}function Uu(t){return typeof t.form_id=="string"}async function y2(t,e,n){var d;const r=t.env.data,i=t.var.tenant_id||t.req.header("tenant-id");if(!i)throw new A(400,{message:"Missing tenant_id in context"});const o=await r.forms.get(i,e);if(!o)throw new A(404,{message:"Form not found for post-user-login hook"});let a=(d=o.start)==null?void 0:d.next_node;if(!a&&o.nodes&&o.nodes.length>0){const l=o.nodes.find(u=>u.type==="STEP");a=l==null?void 0:l.id}if(!a)throw new A(400,{message:"No start node found in form"});let c=`/u/forms/${o.id}/nodes/${a}?state=${encodeURIComponent(n.id)}`;return new Response(null,{status:302,headers:{location:c}})}function Mu(t){return typeof t.url=="string"}function w2(t,e){return async(n,r)=>{var a,c,d;const i={method:t.req.method,ip:t.req.query("x-real-ip")||"",user_agent:t.req.query("user-agent"),url:((a=t.var.loginSession)==null?void 0:a.authorization_url)||t.req.url};if((c=t.env.hooks)!=null&&c.onExecutePreUserRegistration)try{await t.env.hooks.onExecutePreUserRegistration({user:r,request:i},{user:{setUserMetadata:async(l,u)=>{r[l]=u}}})}catch{const u=we(t,{type:he.FAILED_SIGNUP,description:"Pre user registration hook failed"});await e.logs.create(n,u)}let o=await S_(e)(n,r);if((d=t.env.hooks)!=null&&d.onExecutePostUserRegistration)try{await t.env.hooks.onExecutePostUserRegistration({user:r,request:i},{user:{}})}catch{const u=we(t,{type:he.FAILED_SIGNUP,description:"Post user registration hook failed"});await t.env.data.logs.create(n,u)}return await m2(t)(n,o),o}}async function v2(t,e,n,r){var i;if(e.disable_sign_ups){const o=(i=t.var.loginSession)==null?void 0:i.authorization_url;if(!(o&&new URL(o).searchParams.get("screen_hint")==="signup")&&!await zs({userAdapter:n.users,tenant_id:e.tenant.id,email:r})){const d=we(t,{type:he.FAILED_SIGNUP,description:"Public signup is disabled"});throw await n.logs.create(e.tenant.id,d),new A(400,{message:"Signups are disabled for this client"})}}await _2(t)(t.var.tenant_id||"",r)}function Es(t,e){return{...e,users:{...e.users,create:w2(t,e)}}}async function b2(t,e,n,r,i,o){var d;(d=t.env.hooks)!=null&&d.onExecutePostLogin&&(o!=null&&o.client)&&(o!=null&&o.authParams)&&await t.env.hooks.onExecutePostLogin({client:o.client,user:r,request:{ip:t.req.header("x-real-ip")||"",user_agent:t.req.header("user-agent")||"",method:t.req.method,url:t.req.url},scope:o.authParams.scope||"",grant_type:""},{prompt:{render:l=>{}}});const{hooks:a}=await e.hooks.list(n,{q:"trigger_id:post-user-login",page:0,per_page:100,include_totals:!1});if(i){const l=a.find(u=>u.enabled&&Uu(u));if(l&&Uu(l))return y2(t,l.form_id,i)}const c=a.filter(l=>l.enabled&&Mu(l));for(const l of c)if(Mu(l))try{await fetch(l.url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenant_id:n,user:r,trigger_id:"post-user-login"})})}catch{const f=we(t,{type:he.FAILED_HOOK,description:`Failed to invoke post-user-login webhook: ${l.url}`});await e.logs.create(n,f)}return r}function b0(t){return Es(t,t.env.data)}async function Zd(t,e,n){return(await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n}`})).users}async function kr({userAdapter:t,tenant_id:e,username:n,provider:r}){const i=r==="sms"?`phone_number:${n}`:`email:${n}`,{users:o}=await t.list(e,{page:0,per_page:10,include_totals:!1,q:`${i} provider:${r}`});return o.length>1&&console.error("More than one user found for same email and provider"),o[0]||null}async function zs({userAdapter:t,tenant_id:e,email:n}){var c;const{users:r}=await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n}`}),i=r.filter(d=>!(d.provider==="auth2"&&!d.email_verified));if(i.length===0)return;const o=i.filter(d=>!d.linked_to);if(o.length>0)return o.length>1&&console.error("More than one primary user found for same email"),o[0];const a=await t.get(e,(c=i[0])==null?void 0:c.linked_to);if(!a)throw new Error("Primary account not found");return a}async function Io({userAdapter:t,tenant_id:e,username:n,provider:r}){const i=await kr({userAdapter:t,tenant_id:e,username:n,provider:r});return i?i.linked_to?t.get(e,i.linked_to):i:null}async function Is(t,e){const{username:n,provider:r,connection:i,client:o,userId:a,isSocial:c,profileData:d={},ip:l=""}=e;let u=await Io({userAdapter:t.env.data.users,tenant_id:e.client.tenant.id,username:n,provider:r});if(!u){const f={user_id:`${r}|${a||xs()}`,email:i!=="sms"?n:void 0,phone_number:i==="sms"?n:void 0,name:n,provider:r,connection:i,email_verified:!0,last_ip:l,is_social:c,last_login:new Date().toISOString(),profileData:JSON.stringify(d)};u=await b0(t).users.create(o.tenant.id,f),t.set("user_id",u.user_id)}return u}const Vt=s.z.object({page:s.z.string().min(0).optional().default("0").transform(t=>parseInt(t,10)).openapi({description:"The page number where 0 is the first page"}),per_page:s.z.string().min(1).optional().default("10").transform(t=>parseInt(t,10)).openapi({description:"The number of items per page"}),include_totals:s.z.string().optional().default("false").transform(t=>t==="true").openapi({description:"If the total number of items should be included in the response"}),sort:s.z.string().regex(/^.+:(-1|1)$/).optional().openapi({description:"A property that should have the format 'string:-1' or 'string:1'"}),q:s.z.string().optional().openapi({description:"A lucene query string used to filter the results"})});function Vn(t){if(!t)return;const[e,n]=t.split(":"),r=n==="1"?"asc":"desc";if(!(!e||!r))return{sort_by:e,sort_order:r}}const Fu=Xt.extend({users:s.z.array(Et)}),$2=Xt.extend({sessions:s.z.array($s)}),x2=new s.OpenAPIHono().openapi(s.createRoute({tags:["users"],method:"get",path:"/",request:{query:Vt,headers:s.z.object({"tenant-id":s.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:s.z.union([s.z.array(Et),Fu])}},description:"List of users"}}}),async t=>{const{page:e,per_page:n,include_totals:r,sort:i,q:o}=t.req.valid("query"),{"tenant-id":a}=t.req.valid("header");if(o!=null&&o.includes("identities.profileData.email")){const u=o.split("=")[1],m=(await t.env.data.users.list(a,{page:e,per_page:n,include_totals:r,q:`email:${u}`})).users.filter(y=>y.linked_to),[w]=m;if(!w)return t.json([]);const h=await t.env.data.users.get(a,w.linked_to);if(!h)throw new A(500,{message:"Primary account not found"});return t.json([Et.parse(h)])}const c=["-_exists_:linked_to"];o&&c.push(o);const d=await t.env.data.users.list(a,{page:e,per_page:n,include_totals:r,sort:Vn(i),q:c.join(" ")}),l=d.users.filter(u=>!u.linked_to);return r?t.json(Fu.parse({users:l,length:d.length,start:d.start,limit:d.limit})):t.json(s.z.array(Et).parse(l))}).openapi(s.createRoute({tags:["users"],method:"get",path:"/{user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:Et}},description:"List of users"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{"tenant-id":n}=t.req.valid("header"),r=await t.env.data.users.get(n,e);if(!r)throw new A(404);if(r.linked_to)throw new A(404,{message:"User is linked to another user"});return t.json(r)}).openapi(s.createRoute({tags:["users"],method:"delete",path:"/{user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{description:"Status"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{"tenant-id":n}=t.req.valid("header");if(!await t.env.data.users.remove(n,e))throw new A(404);return t.text("OK")}).openapi(s.createRoute({tags:["users"],method:"post",path:"/",request:{headers:s.z.object({"tenant-id":s.z.string()}),body:{content:{"application/json":{schema:s.z.object({...bo.shape})}}}},security:[{Bearer:["auth:write"]}],responses:{201:{content:{"application/json":{schema:Et}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json");t.set("body",n);const{email:r,phone_number:i,name:o,linked_to:a,email_verified:c,provider:d,connection:l}=n,u=`${n.provider}|${n.user_id||xs()}`;try{const f=await t.env.data.users.create(e,{email:r,user_id:u,name:o||r||i,phone_number:i,provider:d,connection:l,linked_to:a??void 0,email_verified:c||!1,last_ip:"",is_social:!1,last_login:new Date().toISOString()});t.set("user_id",f.user_id);const m=we(t,{type:he.SUCCESS_API_OPERATION,description:"User created"});It(t,t.env.data.logs.create(e,m));const w={...f,identities:[{connection:f.connection,provider:f.provider,user_id:Nu(f.user_id),isSocial:f.is_social}]};return t.json(Et.parse(w),{status:201})}catch(f){throw f.message==="User already exists"?new A(409,{message:"User already exists"}):f}}).openapi(s.createRoute({tags:["users"],method:"patch",path:"/{user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),body:{content:{"application/json":{schema:s.z.object({...bo.shape,verify_email:s.z.boolean(),password:s.z.string()}).partial()}}},params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{description:"Status"}}}),async t=>{var u;const{data:e}=t.env,{"tenant-id":n}=t.req.valid("header"),r=t.req.valid("json"),{user_id:i}=t.req.valid("param"),{verify_email:o,password:a,...c}=r,d=await e.users.get(n,i);if(!d)throw new A(404);if(c.email&&c.email!==d.email){const f=await Zd(t.env.data.users,n,c.email);if(f.length&&f.some(m=>m.user_id!==i))throw new A(409,{message:"Another user with the same email address already exists."})}if(d.linked_to)throw new A(404,{message:"User is linked to another user"});if(await t.env.data.users.update(n,i,c),a){const f=(u=d.identities)==null?void 0:u.find(h=>h.connection==="Username-Password-Authentication");if(!f)throw new A(400,{message:"User does not have a password identity"});const m={user_id:f.user_id,password:await vi.hash(a,10),algorithm:"bcrypt"};await e.passwords.get(n,f.user_id)?await e.passwords.update(n,m):await e.passwords.create(n,m)}const l=await t.env.data.users.get(n,i);if(!l)throw new A(500);return t.json(l)}).openapi(s.createRoute({tags:["users"],method:"post",path:"/{user_id}/identities",request:{headers:s.z.object({"tenant-id":s.z.string()}),body:{content:{"application/json":{schema:s.z.union([s.z.object({link_with:s.z.string()}),s.z.object({user_id:s.z.string(),provider:s.z.string(),connection:s.z.string().optional()})])}}},params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{201:{content:{"application/json":{schema:s.z.array(s.z.object({connection:s.z.string(),provider:s.z.string(),user_id:s.z.string(),isSocial:s.z.boolean()}))}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json"),{user_id:r}=t.req.valid("param"),i="link_with"in n?n.link_with:n.user_id,o=await t.env.data.users.get(e,r);if(!o)throw new A(400,{message:"Linking an inexistent identity is not allowed."});await t.env.data.users.update(e,i,{linked_to:r});const a=await t.env.data.users.list(e,{page:0,per_page:10,include_totals:!1,q:`linked_to:${r}`}),c=[o,...a.users].map(d=>({connection:d.connection,provider:d.provider,user_id:Nu(d.user_id),isSocial:d.is_social}));return t.json(c,{status:201})}).openapi(s.createRoute({tags:["users"],method:"delete",path:"/{user_id}/identities/{provider}/{linked_user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string(),provider:s.z.string(),linked_user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:s.z.array(Et)}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),{user_id:n,provider:r,linked_user_id:i}=t.req.valid("param");await t.env.data.users.unlink(e,n,r,i);const o=await t.env.data.users.get(e,n);if(!o)throw new A(404);return t.json([Et.parse(o)])}).openapi(s.createRoute({tags:["users"],method:"get",path:"/{user_id}/sessions",request:{query:Vt,headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:s.z.union([s.z.array($s),$2])}},description:"List of sessions"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{include_totals:n,page:r,per_page:i}=t.req.valid("query"),{"tenant-id":o}=t.req.valid("header"),a=await t.env.data.sessions.list(o,{page:r,per_page:i,include_totals:n,q:`user_id:${e}`});return n?t.json(a):t.json(a.sessions)});/*! *****************************************************************************
29
+ </html>`;return new Response(i,{headers:{"Content-Type":"text/html"}})}async function d2(t,e,n,r,i){var m,w,h;if(!n.redirect_uri)throw new A(400,{message:"Missing redirect_uri in authParams"});if(!r.email)throw new A(400,{message:"Missing email in user"});const[o]=await t.env.data.keys.list();if(!o)throw new A(500,{message:"No signing key found"});if(!((m=e.addons)!=null&&m.samlp))throw new A(400,{message:`SAML Addon is not enabled for client ${e.id}`});const{recipient:a,audience:c}=e.addons.samlp,d=n.state||"";if(!a||!d||!r||!n.state)throw new A(400,{message:"Missing recipient or inResponseTo"});const l=JSON.parse(n.state),u=new URL(n.redirect_uri),f=await l2(t,{issuer:t.env.ISSUER,audience:c||n.client_id,destination:u.toString(),inResponseTo:l.requestId,userId:((h=(w=r.app_metadata)==null?void 0:w.vimeo)==null?void 0:h.user_id)||r.user_id,email:r.email,sessionIndex:i,signature:{privateKeyPem:o.pkcs7,cert:o.cert,kid:o.kid}});return c2(u.toString(),f,l.relayState)}async function l2(t,e){const n=e.notBefore||new Date().toISOString(),r=e.notAfter||new Date(new Date(n).getTime()+10*60*1e3).toISOString(),i=e.issueInstant||n,o=e.sessionNotOnOrAfter||r,a=e.responseId||`_${xe()}`,c=e.assertionId||`_${xe()}`,d=[{"samlp:Response":[{"saml:Issuer":[{"#text":e.issuer}]},{"samlp:Status":[{"samlp:StatusCode":[],":@":{"@_Value":"urn:oasis:names:tc:SAML:2.0:status:Success"}}]},{"saml:Assertion":[{"saml:Issuer":[{"#text":e.issuer}]},{"saml:Subject":[{"saml:NameID":[{"#text":e.email}],":@":{"@_Format":"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"}},{"saml:SubjectConfirmation":[{"saml:SubjectConfirmationData":[],":@":{"@_InResponseTo":e.inResponseTo,"@_NotOnOrAfter":r,"@_Recipient":e.destination}}],":@":{"@_Method":"urn:oasis:names:tc:SAML:2.0:cm:bearer"}}]},{"saml:Conditions":[{"saml:AudienceRestriction":[{"saml:Audience":[{"#text":e.audience}]}]}],":@":{"@_NotBefore":n,"@_NotOnOrAfter":r}},{"saml:AuthnStatement":[{"saml:AuthnContext":[{"saml:AuthnContextClassRef":[{"#text":"urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"}]}]}],":@":{"@_AuthnInstant":i,"@_SessionIndex":e.sessionIndex,"@_SessionNotOnOrAfter":o}},{"saml:AttributeStatement":[{"saml:Attribute":[{"saml:AttributeValue":[{"#text":e.userId}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_FriendlyName":"persistent","@_Name":"id","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":e.email}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"email","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"manage-account"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"default-roles-master"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"offline_access"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"view-profile"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"uma_authorization"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}},{"saml:Attribute":[{"saml:AttributeValue":[{"#text":"manage-account-links"}],":@":{"@_xmlns:xs":"http://www.w3.org/2001/XMLSchema","@_xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance","@_xsi:type":"xs:string"}}],":@":{"@_Name":"Role","@_NameFormat":"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"}}]}],":@":{"@_xmlns":"urn:oasis:names:tc:SAML:2.0:assertion","@_ID":c,"@_IssueInstant":i,"@_Version":"2.0"}}],":@":{"@_xmlns:samlp":"urn:oasis:names:tc:SAML:2.0:protocol","@_xmlns:saml":"urn:oasis:names:tc:SAML:2.0:assertion","@_Destination":e.destination,"@_ID":a,"@_InResponseTo":e.inResponseTo,"@_IssueInstant":i,"@_Version":"2.0"}}];let u=new a2.XMLBuilder({ignoreAttributes:!1,suppressEmptyNode:!0,preserveOrder:!0}).build(d);if(e.signature){const m=await fetch(t.env.SAML_SIGN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({xmlContent:u,privateKey:e.signature.privateKeyPem,publicCert:e.signature.cert})});if(!m.ok)throw new Error(`Failed to sign SAML response: ${m.status}`);u=await m.text()}return e.encode===!1?u:btoa(u)}var u2={deno:"Deno",bun:"Bun",workerd:"Cloudflare-Workers",node:"Node.js"},p2=()=>{var n,r;const t=globalThis;if(typeof navigator<"u"&&typeof navigator.userAgent=="string"){for(const[i,o]of Object.entries(u2))if(f2(o))return i}return typeof(t==null?void 0:t.EdgeRuntime)=="string"?"edge-light":(t==null?void 0:t.fastly)!==void 0?"fastly":((r=(n=t==null?void 0:t.process)==null?void 0:n.release)==null?void 0:r.name)==="node"?"node":"other"},f2=t=>navigator.userAgent.startsWith(t);function It(t,e){p2()==="workerd"&&t.executionCtx.waitUntil(e)}function jt(t){var e,n,r,i;return{auth0Client:(e=t.query("auth0Client"))==null?void 0:e.slice(0,255),ip:(n=t.header("x-real-ip"))==null?void 0:n.slice(0,45),useragent:(r=t.header("user-agent"))==null?void 0:r.slice(0,512),countryCode:(i=t.header("cf-ipcountry"))==null?void 0:i.slice(0,2)}}const Lu=["sub","iss","aud","exp","nbf","iat","jti"];async function As(t,e){var v,b;const{authParams:n,user:r,client:i,session_id:o}=e,c=(await t.env.data.keys.list()).filter(E=>!E.revoked_at||new Date(E.revoked_at)>new Date),d=c[c.length-1];if(!(d!=null&&d.pkcs7))throw new A(500,{message:"No signing key available"});const l=O_(d.pkcs7),u=t.var.custom_domain?`https://${t.var.custom_domain}/`:t.env.ISSUER,f={aud:n.audience||"default",scope:n.scope||"",sub:(r==null?void 0:r.user_id)||n.client_id,iss:u,tenant_id:t.var.tenant_id,sid:o},m=r&&((v=n.scope)!=null&&v.split(" ").includes("openid"))?{aud:n.client_id,sub:r.user_id,iss:u,sid:o,nonce:n.nonce,given_name:r.given_name,family_name:r.family_name,nickname:r.nickname,picture:r.picture,locale:r.locale,name:r.name,email:r.email,email_verified:r.email_verified}:void 0;(b=t.env.hooks)!=null&&b.onExecuteCredentialsExchange&&await t.env.hooks.onExecuteCredentialsExchange({ctx:t,client:i,user:r,request:{ip:t.req.header("x-real-ip")||"",user_agent:t.req.header("user-agent")||"",method:t.req.method,url:t.req.url},scope:n.scope||"",grant_type:""},{accessToken:{setCustomClaim:(E,C)=>{if(Lu.includes(E))throw new Error(`Cannot overwrite reserved claim '${E}'`);f[E]=C}},idToken:{setCustomClaim:(E,C)=>{if(Lu.includes(E))throw new Error(`Cannot overwrite reserved claim '${E}'`);m&&(m[E]=C)}},access:{deny:E=>{throw new A(400,{message:`Access denied: ${E}`})}}});const w={includeIssuedTimestamp:!0,expiresIn:new Hd(1,"d"),headers:{kid:d.kid}},h=await ju("RS256",l,f,w),y=m?await ju("RS256",l,m,w):void 0;return{access_token:h,refresh_token:e.refresh_token,id_token:y,token_type:"Bearer",expires_in:86400}}async function y0(t,e){return{code:(await t.env.data.codes.create(e.client.tenant.id,{code_id:xe(),user_id:e.user.user_id,code_type:"authorization_code",login_id:e.login_id,expires_at:new Date(Date.now()+B_*1e3).toISOString()})).code_id,state:e.authParams.state}}async function h2(t,e){const{client:n,scope:r,audience:i=n.tenant.audience,session_id:o}=e,a=jt(t.req);return await t.env.data.refreshTokens.create(n.tenant.id,{id:xe(),session_id:o,client_id:n.id,idle_expires_at:new Date(Date.now()+ks*1e3).toISOString(),user_id:e.user.user_id,device:{last_ip:a.ip||"",initial_ip:a.ip||"",last_user_agent:a.useragent||"",initial_user_agent:a.useragent||"",initial_asn:"",last_asn:""},resource_servers:[{audience:i,scopes:r}],rotating:!1})}async function w0(t,{user:e,client:n,loginSession:r}){const i=await t.env.data.sessions.create(n.tenant.id,{id:xe(),user_id:e.user_id,idle_expires_at:new Date(Date.now()+ks*1e3).toISOString(),device:{last_ip:t.req.header("x-real-ip")||"",initial_ip:t.req.header("x-real-ip")||"",last_user_agent:t.req.header("user-agent")||"",initial_user_agent:t.req.header("user-agent")||"",initial_asn:"",last_asn:""},clients:[n.id]});return await t.env.data.loginSessions.update(n.tenant.id,r.id,{session_id:i.id}),i}async function Zt(t,e){var h,y;const{authParams:n,user:r,client:i,ticketAuth:o}=e,a=we(t,{type:he.SUCCESS_LOGIN,description:`Successful login for ${r.user_id}`,userId:r.user_id});if(It(t,t.env.data.logs.create(i.tenant.id,a)),It(t,t.env.data.users.update(i.tenant.id,r.user_id,{last_login:new Date().toISOString(),last_ip:t.req.header("x-real-ip")||"",login_count:r.login_count+1})),o){if(!e.loginSession)throw new A(500,{message:"Login session not found for ticket auth."});const v=j_(),b=xe(12),E=await t.env.data.codes.create(i.tenant.id,{code_id:xe(),code_type:"ticket",login_id:e.loginSession.id,expires_at:new Date(Date.now()+U_).toISOString(),code_verifier:[b,v].join("|")});return t.json({login_ticket:E.code_id,co_verifier:v,co_id:b})}let c=e.refreshToken,d=e.sessionId,l=r;if(!d&&((h=e.loginSession)!=null&&h.session_id)){d=e.loginSession.session_id;const v=await t.env.data.sessions.get(i.tenant.id,d);v&&!v.clients.includes(i.id)&&await t.env.data.sessions.update(i.tenant.id,d,{clients:[...v.clients,i.id]}),!c&&((y=n.scope)!=null&&y.split(" ").includes("offline_access"))&&(c=(await h2(t,{user:l,client:i,session_id:d,scope:n.scope,audience:n.audience})).id)}else if(!d){if(!e.loginSession)throw new A(500,{message:"Login session not found for creating a new session."});d=(await w0(t,{user:l,client:i,loginSession:e.loginSession})).id;const b=await b2(t,t.env.data,i.tenant.id,r,e.loginSession,{client:i,authParams:n});if(b instanceof Response)return b;l=b}if(e.authParams.response_mode===Dt.SAML_POST){if(!d)throw new A(500,{message:"Session ID not available for SAML response"});return d2(t,e.client,e.authParams,l,d)}const u=await As(t,{authParams:n,user:l,client:i,session_id:d,refresh_token:c});if(n.response_mode===Dt.WEB_MESSAGE){if(d){const v=rc(i.tenant.id,d,t.var.custom_domain||t.req.header("host")||"");v&&t.header("set-cookie",v)}else console.warn("Session ID not available for WEB_MESSAGE, cookie will not be set.");return u}const f=new Headers;if(d){const v=rc(i.tenant.id,d,t.var.custom_domain||t.req.header("host")||"");v&&f.set("set-cookie",v)}const m=n.response_type||it.CODE;if(m===it.CODE){if(!e.loginSession)throw new A(500,{message:"Login session not found for code response type."});const v=await y0(t,{user:l,client:i,authParams:n,login_id:e.loginSession.id});if(!n.redirect_uri)throw new A(400,{message:"Redirect uri not found for code response type."});const b=new URL(n.redirect_uri);return b.searchParams.set("code",v.code),v.state&&b.searchParams.set("state",v.state),f.set("location",b.toString()),new Response("Redirecting",{status:302,headers:f})}if(!n.redirect_uri)throw new A(400,{message:"Redirect uri not found for this response mode."});const w=new URL(n.redirect_uri);if(m===it.TOKEN||m===it.TOKEN_ID_TOKEN)w.hash=new URLSearchParams({access_token:u.access_token,...u.id_token&&{id_token:u.id_token},token_type:u.token_type,expires_in:u.expires_in.toString(),...n.state&&{state:n.state},...n.scope&&{scope:n.scope}}).toString();else throw new A(500,{message:`Unsupported response type ('${m}') for redirect with tokens.`});return f.set("location",w.toString()),new Response("Redirecting",{status:302,headers:f})}async function g2(t,e,n){const r=await t.env.data.tenants.get(e);if(!r)throw new Error(`Tenant not found: ${e}`);return As(t,{client:{id:t.env.ISSUER,tenant:r,created_at:new Date().toISOString(),updated_at:new Date().toISOString(),name:t.env.ISSUER,disable_sign_ups:!1,connections:[]},authParams:{client_id:t.env.ISSUER,response_type:it.TOKEN,scope:n}})}async function v0(t,e,n){const r=await g2(t,n.tenant_id,"webhook");for await(const i of e.filter(o=>"url"in o))if(!(await fetch(i.url,{method:"POST",headers:{Authorization:`Bearer ${r.access_token}`,"Content-Type":"application/json"},body:JSON.stringify(n)})).ok){const a=we(t,{type:he.FAILED_HOOK,description:`Failed to invoke hook ${i.hook_id}`});await t.env.data.logs.create(n.tenant_id,a)}}function m2(t){return async(e,n)=>{const{hooks:r}=await t.env.data.hooks.list(e);return await v0(t,r,{tenant_id:e,user:n,trigger_id:"post-user-registration"}),n}}function _2(t){return async(e,n)=>{const{hooks:r}=await t.env.data.hooks.list(e,{q:"trigger_id:pre-user-signup",page:0,per_page:100,include_totals:!1});await v0(t,r,{tenant_id:e,email:n,trigger_id:"pre-user-signup"})}}function Uu(t){return typeof t.form_id=="string"}async function y2(t,e,n){var d;const r=t.env.data,i=t.var.tenant_id||t.req.header("tenant-id");if(!i)throw new A(400,{message:"Missing tenant_id in context"});const o=await r.forms.get(i,e);if(!o)throw new A(404,{message:"Form not found for post-user-login hook"});let a=(d=o.start)==null?void 0:d.next_node;if(!a&&o.nodes&&o.nodes.length>0){const l=o.nodes.find(u=>u.type==="STEP");a=l==null?void 0:l.id}if(!a)throw new A(400,{message:"No start node found in form"});let c=`/u/forms/${o.id}/nodes/${a}?state=${encodeURIComponent(n.id)}`;return new Response(null,{status:302,headers:{location:c}})}function Mu(t){return typeof t.url=="string"}function w2(t,e){return async(n,r)=>{var a,c,d;const i={method:t.req.method,ip:t.req.query("x-real-ip")||"",user_agent:t.req.query("user-agent"),url:((a=t.var.loginSession)==null?void 0:a.authorization_url)||t.req.url};if((c=t.env.hooks)!=null&&c.onExecutePreUserRegistration)try{await t.env.hooks.onExecutePreUserRegistration({ctx:t,user:r,request:i},{user:{setUserMetadata:async(l,u)=>{r[l]=u}}})}catch{const u=we(t,{type:he.FAILED_SIGNUP,description:"Pre user registration hook failed"});await e.logs.create(n,u)}let o=await S_(e)(n,r);if((d=t.env.hooks)!=null&&d.onExecutePostUserRegistration)try{await t.env.hooks.onExecutePostUserRegistration({ctx:t,user:r,request:i},{user:{}})}catch{const u=we(t,{type:he.FAILED_SIGNUP,description:"Post user registration hook failed"});await t.env.data.logs.create(n,u)}return await m2(t)(n,o),o}}async function v2(t,e,n,r){var i;if(e.disable_sign_ups){const o=(i=t.var.loginSession)==null?void 0:i.authorization_url;if(!(o&&new URL(o).searchParams.get("screen_hint")==="signup")&&!await zs({userAdapter:n.users,tenant_id:e.tenant.id,email:r})){const d=we(t,{type:he.FAILED_SIGNUP,description:"Public signup is disabled"});throw await n.logs.create(e.tenant.id,d),new A(400,{message:"Signups are disabled for this client"})}}await _2(t)(t.var.tenant_id||"",r)}function Es(t,e){return{...e,users:{...e.users,create:w2(t,e)}}}async function b2(t,e,n,r,i,o){var d;(d=t.env.hooks)!=null&&d.onExecutePostLogin&&(o!=null&&o.client)&&(o!=null&&o.authParams)&&await t.env.hooks.onExecutePostLogin({ctx:t,client:o.client,user:r,request:{ip:t.req.header("x-real-ip")||"",user_agent:t.req.header("user-agent")||"",method:t.req.method,url:t.req.url},scope:o.authParams.scope||"",grant_type:""},{prompt:{render:l=>{}}});const{hooks:a}=await e.hooks.list(n,{q:"trigger_id:post-user-login",page:0,per_page:100,include_totals:!1});if(i){const l=a.find(u=>u.enabled&&Uu(u));if(l&&Uu(l))return y2(t,l.form_id,i)}const c=a.filter(l=>l.enabled&&Mu(l));for(const l of c)if(Mu(l))try{await fetch(l.url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenant_id:n,user:r,trigger_id:"post-user-login"})})}catch{const f=we(t,{type:he.FAILED_HOOK,description:`Failed to invoke post-user-login webhook: ${l.url}`});await e.logs.create(n,f)}return r}function b0(t){return Es(t,t.env.data)}async function Zd(t,e,n){return(await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n}`})).users}async function kr({userAdapter:t,tenant_id:e,username:n,provider:r}){const i=r==="sms"?`phone_number:${n}`:`email:${n}`,{users:o}=await t.list(e,{page:0,per_page:10,include_totals:!1,q:`${i} provider:${r}`});return o.length>1&&console.error("More than one user found for same email and provider"),o[0]||null}async function zs({userAdapter:t,tenant_id:e,email:n}){var c;const{users:r}=await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n}`}),i=r.filter(d=>!(d.provider==="auth2"&&!d.email_verified));if(i.length===0)return;const o=i.filter(d=>!d.linked_to);if(o.length>0)return o.length>1&&console.error("More than one primary user found for same email"),o[0];const a=await t.get(e,(c=i[0])==null?void 0:c.linked_to);if(!a)throw new Error("Primary account not found");return a}async function Io({userAdapter:t,tenant_id:e,username:n,provider:r}){const i=await kr({userAdapter:t,tenant_id:e,username:n,provider:r});return i?i.linked_to?t.get(e,i.linked_to):i:null}async function Is(t,e){const{username:n,provider:r,connection:i,client:o,userId:a,isSocial:c,profileData:d={},ip:l=""}=e;let u=await Io({userAdapter:t.env.data.users,tenant_id:e.client.tenant.id,username:n,provider:r});if(!u){const f={user_id:`${r}|${a||xs()}`,email:i!=="sms"?n:void 0,phone_number:i==="sms"?n:void 0,name:n,provider:r,connection:i,email_verified:!0,last_ip:l,is_social:c,last_login:new Date().toISOString(),profileData:JSON.stringify(d)};u=await b0(t).users.create(o.tenant.id,f),t.set("user_id",u.user_id)}return u}const Vt=s.z.object({page:s.z.string().min(0).optional().default("0").transform(t=>parseInt(t,10)).openapi({description:"The page number where 0 is the first page"}),per_page:s.z.string().min(1).optional().default("10").transform(t=>parseInt(t,10)).openapi({description:"The number of items per page"}),include_totals:s.z.string().optional().default("false").transform(t=>t==="true").openapi({description:"If the total number of items should be included in the response"}),sort:s.z.string().regex(/^.+:(-1|1)$/).optional().openapi({description:"A property that should have the format 'string:-1' or 'string:1'"}),q:s.z.string().optional().openapi({description:"A lucene query string used to filter the results"})});function Vn(t){if(!t)return;const[e,n]=t.split(":"),r=n==="1"?"asc":"desc";if(!(!e||!r))return{sort_by:e,sort_order:r}}const Fu=Xt.extend({users:s.z.array(Et)}),$2=Xt.extend({sessions:s.z.array($s)}),x2=new s.OpenAPIHono().openapi(s.createRoute({tags:["users"],method:"get",path:"/",request:{query:Vt,headers:s.z.object({"tenant-id":s.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:s.z.union([s.z.array(Et),Fu])}},description:"List of users"}}}),async t=>{const{page:e,per_page:n,include_totals:r,sort:i,q:o}=t.req.valid("query"),{"tenant-id":a}=t.req.valid("header");if(o!=null&&o.includes("identities.profileData.email")){const u=o.split("=")[1],m=(await t.env.data.users.list(a,{page:e,per_page:n,include_totals:r,q:`email:${u}`})).users.filter(y=>y.linked_to),[w]=m;if(!w)return t.json([]);const h=await t.env.data.users.get(a,w.linked_to);if(!h)throw new A(500,{message:"Primary account not found"});return t.json([Et.parse(h)])}const c=["-_exists_:linked_to"];o&&c.push(o);const d=await t.env.data.users.list(a,{page:e,per_page:n,include_totals:r,sort:Vn(i),q:c.join(" ")}),l=d.users.filter(u=>!u.linked_to);return r?t.json(Fu.parse({users:l,length:d.length,start:d.start,limit:d.limit})):t.json(s.z.array(Et).parse(l))}).openapi(s.createRoute({tags:["users"],method:"get",path:"/{user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:Et}},description:"List of users"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{"tenant-id":n}=t.req.valid("header"),r=await t.env.data.users.get(n,e);if(!r)throw new A(404);if(r.linked_to)throw new A(404,{message:"User is linked to another user"});return t.json(r)}).openapi(s.createRoute({tags:["users"],method:"delete",path:"/{user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{description:"Status"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{"tenant-id":n}=t.req.valid("header");if(!await t.env.data.users.remove(n,e))throw new A(404);return t.text("OK")}).openapi(s.createRoute({tags:["users"],method:"post",path:"/",request:{headers:s.z.object({"tenant-id":s.z.string()}),body:{content:{"application/json":{schema:s.z.object({...bo.shape})}}}},security:[{Bearer:["auth:write"]}],responses:{201:{content:{"application/json":{schema:Et}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json");t.set("body",n);const{email:r,phone_number:i,name:o,linked_to:a,email_verified:c,provider:d,connection:l}=n,u=`${n.provider}|${n.user_id||xs()}`;try{const f=await t.env.data.users.create(e,{email:r,user_id:u,name:o||r||i,phone_number:i,provider:d,connection:l,linked_to:a??void 0,email_verified:c||!1,last_ip:"",is_social:!1,last_login:new Date().toISOString()});t.set("user_id",f.user_id);const m=we(t,{type:he.SUCCESS_API_OPERATION,description:"User created"});It(t,t.env.data.logs.create(e,m));const w={...f,identities:[{connection:f.connection,provider:f.provider,user_id:Nu(f.user_id),isSocial:f.is_social}]};return t.json(Et.parse(w),{status:201})}catch(f){throw f.message==="User already exists"?new A(409,{message:"User already exists"}):f}}).openapi(s.createRoute({tags:["users"],method:"patch",path:"/{user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),body:{content:{"application/json":{schema:s.z.object({...bo.shape,verify_email:s.z.boolean(),password:s.z.string()}).partial()}}},params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{description:"Status"}}}),async t=>{var u;const{data:e}=t.env,{"tenant-id":n}=t.req.valid("header"),r=t.req.valid("json"),{user_id:i}=t.req.valid("param"),{verify_email:o,password:a,...c}=r,d=await e.users.get(n,i);if(!d)throw new A(404);if(c.email&&c.email!==d.email){const f=await Zd(t.env.data.users,n,c.email);if(f.length&&f.some(m=>m.user_id!==i))throw new A(409,{message:"Another user with the same email address already exists."})}if(d.linked_to)throw new A(404,{message:"User is linked to another user"});if(await t.env.data.users.update(n,i,c),a){const f=(u=d.identities)==null?void 0:u.find(h=>h.connection==="Username-Password-Authentication");if(!f)throw new A(400,{message:"User does not have a password identity"});const m={user_id:f.user_id,password:await vi.hash(a,10),algorithm:"bcrypt"};await e.passwords.get(n,f.user_id)?await e.passwords.update(n,m):await e.passwords.create(n,m)}const l=await t.env.data.users.get(n,i);if(!l)throw new A(500);return t.json(l)}).openapi(s.createRoute({tags:["users"],method:"post",path:"/{user_id}/identities",request:{headers:s.z.object({"tenant-id":s.z.string()}),body:{content:{"application/json":{schema:s.z.union([s.z.object({link_with:s.z.string()}),s.z.object({user_id:s.z.string(),provider:s.z.string(),connection:s.z.string().optional()})])}}},params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{201:{content:{"application/json":{schema:s.z.array(s.z.object({connection:s.z.string(),provider:s.z.string(),user_id:s.z.string(),isSocial:s.z.boolean()}))}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json"),{user_id:r}=t.req.valid("param"),i="link_with"in n?n.link_with:n.user_id,o=await t.env.data.users.get(e,r);if(!o)throw new A(400,{message:"Linking an inexistent identity is not allowed."});await t.env.data.users.update(e,i,{linked_to:r});const a=await t.env.data.users.list(e,{page:0,per_page:10,include_totals:!1,q:`linked_to:${r}`}),c=[o,...a.users].map(d=>({connection:d.connection,provider:d.provider,user_id:Nu(d.user_id),isSocial:d.is_social}));return t.json(c,{status:201})}).openapi(s.createRoute({tags:["users"],method:"delete",path:"/{user_id}/identities/{provider}/{linked_user_id}",request:{headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string(),provider:s.z.string(),linked_user_id:s.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:s.z.array(Et)}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),{user_id:n,provider:r,linked_user_id:i}=t.req.valid("param");await t.env.data.users.unlink(e,n,r,i);const o=await t.env.data.users.get(e,n);if(!o)throw new A(404);return t.json([Et.parse(o)])}).openapi(s.createRoute({tags:["users"],method:"get",path:"/{user_id}/sessions",request:{query:Vt,headers:s.z.object({"tenant-id":s.z.string()}),params:s.z.object({user_id:s.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:s.z.union([s.z.array($s),$2])}},description:"List of sessions"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{include_totals:n,page:r,per_page:i}=t.req.valid("query"),{"tenant-id":o}=t.req.valid("header"),a=await t.env.data.sessions.list(o,{page:r,per_page:i,include_totals:n,q:`user_id:${e}`});return n?t.json(a):t.json(a.sessions)});/*! *****************************************************************************
30
30
  Copyright (C) Microsoft. All rights reserved.
31
31
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use
32
32
  this file except in compliance with the License. You may obtain a copy of the
@@ -13726,6 +13726,10 @@ export type HookRequest = {
13726
13726
  url: string;
13727
13727
  };
13728
13728
  export type HookEvent = {
13729
+ ctx: Context<{
13730
+ Bindings: Bindings;
13731
+ Variables: Variables;
13732
+ }>;
13729
13733
  client?: Client;
13730
13734
  request: HookRequest;
13731
13735
  transaction?: Transaction;
package/dist/authhero.mjs CHANGED
@@ -5905,6 +5905,7 @@ async function go(t, e) {
5905
5905
  } : void 0;
5906
5906
  (b = t.env.hooks) != null && b.onExecuteCredentialsExchange && await t.env.hooks.onExecuteCredentialsExchange(
5907
5907
  {
5908
+ ctx: t,
5908
5909
  client: i,
5909
5910
  user: r,
5910
5911
  request: {
@@ -6301,6 +6302,7 @@ function Xy(t, e) {
6301
6302
  try {
6302
6303
  await t.env.hooks.onExecutePreUserRegistration(
6303
6304
  {
6305
+ ctx: t,
6304
6306
  user: r,
6305
6307
  request: i
6306
6308
  },
@@ -6324,6 +6326,7 @@ function Xy(t, e) {
6324
6326
  try {
6325
6327
  await t.env.hooks.onExecutePostUserRegistration(
6326
6328
  {
6329
+ ctx: t,
6327
6330
  user: r,
6328
6331
  request: i
6329
6332
  },
@@ -6371,6 +6374,7 @@ async function Yy(t, e, n, r, i, s) {
6371
6374
  var d;
6372
6375
  (d = t.env.hooks) != null && d.onExecutePostLogin && (s != null && s.client) && (s != null && s.authParams) && await t.env.hooks.onExecutePostLogin(
6373
6376
  {
6377
+ ctx: t,
6374
6378
  client: s.client,
6375
6379
  user: r,
6376
6380
  request: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "authhero",
3
- "version": "0.147.0",
3
+ "version": "0.148.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],