authhero 0.84.0 → 0.85.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 G0(t,e,n,r,i){var m,v,f;if(!n.redirect_uri)throw new N(400,{message:"Missing redirect_uri in authParams"});const[s]=await t.env.data.keys.list();if(!s)throw new N(500,{message:"No signing key found"});if(!((m=e.addons)!=null&&m.samlp))throw new N(400,{message:`SAML Addon is not enabled for client ${e.id}`});const{recipient:o,audience:c}=e.addons.samlp,l=n.state||"";if(!o||!l||!r||!n.state)throw new N(400,{message:"Missing recipient or inResponseTo"});const u=JSON.parse(n.state),p=new URL(n.redirect_uri),h=await J0(t,{issuer:t.env.ISSUER,audience:c||n.client_id,destination:p.toString(),inResponseTo:u.requestId,userId:((f=(v=r.app_metadata)==null?void 0:v.vimeo)==null?void 0:f.user_id)||r.user_id,email:r.email,sessionIndex:i,signature:{privateKeyPem:s.pkcs7,cert:s.cert,kid:s.kid}});return W0(p.toString(),h,u.relayState)}async function J0(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,s=e.sessionNotOnOrAfter||r,o=e.responseId||`_${De()}`,c=e.assertionId||`_${De()}`,l=[{"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":s}},{"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":o,"@_InResponseTo":e.inResponseTo,"@_IssueInstant":i,"@_Version":"2.0"}}];let p=new K0.XMLBuilder({ignoreAttributes:!1,suppressEmptyNode:!0,preserveOrder:!0}).build(l);if(e.signature){const m=await fetch(t.env.SAML_SIGN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({xmlContent:p,privateKey:e.signature.privateKeyPem,publicCert:e.signature.cert})});if(!m.ok)throw new Error(`Failed to sign SAML response: ${m.status}`);p=await m.text()}return e.encode===!1?p:btoa(p)}var Z0={deno:"Deno",bun:"Bun",workerd:"Cloudflare-Workers",node:"Node.js"},Y0=()=>{var n,r;const t=globalThis;if(typeof navigator<"u"&&typeof navigator.userAgent=="string"){for(const[i,s]of Object.entries(Z0))if(X0(s))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"},X0=t=>navigator.userAgent.startsWith(t);function et(t,e){Y0()==="workerd"&&t.executionCtx.waitUntil(e)}const Hu=["sub","iss","aud","exp","nbf","iat","jti"];async function eo(t,e){var _,w;const{authParams:n,user:r,client:i,session_id:s}=e,c=(await t.env.data.keys.list()).filter(S=>!S.revoked_at||new Date(S.revoked_at)>new Date),l=c[c.length-1];if(!(l!=null&&l.pkcs7))throw new N(500,{message:"No signing key available"});const u=v_(l.pkcs7),p={aud:n.audience||"default",scope:n.scope||"",sub:(r==null?void 0:r.user_id)||n.client_id,iss:t.env.ISSUER,tenant_id:t.var.tenant_id,sid:s},h=r&&((_=n.scope)!=null&&_.split(" ").includes("openid"))?{aud:n.client_id,sub:r.user_id,iss:t.env.ISSUER,sid:s,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;(w=t.env.hooks)!=null&&w.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:(S,C)=>{if(Hu.includes(S))throw new Error(`Cannot overwrite reserved claim '${S}'`);p[S]=C}},idToken:{setCustomClaim:(S,C)=>{if(Hu.includes(S))throw new Error(`Cannot overwrite reserved claim '${S}'`);h&&(h[S]=C)}},access:{deny:S=>{throw new N(400,{message:`Access denied: ${S}`})}}});const m={includeIssuedTimestamp:!0,expiresIn:new sl(1,"d"),headers:{kid:l.kid}},v=await Lu("RS256",u,p,m),f=h?await Lu("RS256",u,h,m):void 0;return{access_token:v,refresh_token:e.refresh_token,id_token:f,token_type:"Bearer",expires_in:86400}}async function Q0(t,e){const{client:n,scope:r,audience:i=n.tenant.audience,session_id:s}=e;return await t.env.data.refreshTokens.create(n.tenant.id,{id:De(),session_id:s,client_id:n.id,expires_at:new Date(Date.now()+Xs*1e3).toISOString(),user_id:e.user.user_id,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:""},resource_servers:[{audience:i,scopes:r}],rotating:!1})}async function Pf(t,e){const{user:n,client:r,scope:i,audience:s}=e,o=await t.env.data.sessions.create(r.tenant.id,{id:De(),user_id:n.user_id,idle_expires_at:new Date(Date.now()+Xs*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:[r.id]}),c=i!=null&&i.split(" ").includes("offline_access")?await Q0(t,{...e,session_id:o.id,scope:i,audience:s}):void 0;return{...o,refresh_token:c}}async function sn(t,e){var v;const{authParams:n,user:r,client:i,ticketAuth:s}=e,o=ve(t,{type:he.SUCCESS_LOGIN,description:`Successful login for ${r.user_id}`,userId:r.user_id});if(et(t,t.env.data.logs.create(i.tenant.id,o)),et(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})),s){if(!e.loginSession)throw new N(500,{message:"Login session not found"});const f=y_(),_=De(12),w=await t.env.data.codes.create(i.tenant.id,{code_id:De(),code_type:"ticket",login_id:e.loginSession.login_id,expires_at:new Date(Date.now()+S_).toISOString(),code_verifier:[_,f].join("|")});return t.json({login_ticket:w.code_id,co_verifier:f,co_id:_})}let c=e.refreshToken,l=e.sessionId,u=r;if(!l){u=await ry(t,t.env.data)(i.tenant.id,r);const f=await Pf(t,{user:r,client:i,scope:n.scope,audience:n.audience});l=f.id,c=(v=f.refresh_token)==null?void 0:v.id}if(e.authParams.response_mode===Zt.SAML_POST)return G0(t,e.client,e.authParams,u,l);const p=await eo(t,{authParams:n,user:u,client:i,session_id:l,refresh_token:c}),h=new Headers({"set-cookie":zf(i.tenant.id,l)});if(n.response_mode===Zt.WEB_MESSAGE)return t.json(p,{headers:h});if((n.response_type||Jt.CODE)===Jt.CODE){if(!e.loginSession)throw new N(500,{message:"Login session not found"});const f=await t.env.data.codes.create(i.tenant.id,{code_id:De(),user_id:r.user_id,code_type:"authorization_code",login_id:e.loginSession.login_id,expires_at:new Date(Date.now()+k_*1e3).toISOString()});if(!n.redirect_uri)throw new N(400,{message:"Redirect uri not found"});const _=new URL(n.redirect_uri);_.searchParams.set("code",f.code_id),n.state&&_.searchParams.set("state",n.state),h.set("location",_.toString())}return new Response("Redirecting",{status:302,headers:h})}async function ey(t,e,n){const r=await t.env.data.tenants.get(e);if(!r)throw new Error(`Tenant not found: ${e}`);return eo(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:Jt.TOKEN,scope:n}})}async function dl(t,e,n){const r=await ey(t,n.tenant_id,"webhook");for await(const i of e)if(!(await fetch(i.url,{method:"POST",headers:{Authorization:`Bearer ${r.access_token}`,"Content-Type":"application/json"},body:JSON.stringify(n)})).ok){const o=ve(t,{type:he.FAILED_HOOK,description:`Failed to invoke hook ${i.hook_id}`});await t.env.data.logs.create(n.tenant_id,o)}}function ty(t){return async(e,n)=>{const{hooks:r}=await t.env.data.hooks.list(e);return await dl(t,r,{tenant_id:e,user:n,trigger_id:"post-user-registration"}),n}}function ny(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 dl(t,r,{tenant_id:e,email:n,trigger_id:"pre-user-signup"})}}function ry(t,e){return async(n,r)=>{const{hooks:i}=await e.hooks.list(n,{q:"trigger_id:post-user-login",page:0,per_page:100,include_totals:!1});return await dl(t,i,{tenant_id:n,user:r,trigger_id:"post-user-login"}),r}}function iy(t,e){return async(n,r)=>{var o,c,l;const i={method:t.req.method,ip:t.req.query("x-real-ip")||"",user_agent:t.req.query("user-agent"),url:((o=t.var.loginSession)==null?void 0:o.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(u,p)=>{r[u]=p}}})}catch{const p=ve(t,{type:he.FAILED_SIGNUP,description:"Pre user registration hook failed"});await e.logs.create(n,p)}let s=await d_(e)(n,r);if((l=t.env.hooks)!=null&&l.onExecutePostUserRegistration)try{await t.env.hooks.onExecutePostUserRegistration({user:r,request:i},{user:{}})}catch{const p=ve(t,{type:he.FAILED_SIGNUP,description:"Post user registration hook failed"});await t.env.data.logs.create(n,p)}return await ty(t)(n,s),s}}async function sy(t,e,n,r){if(e.disable_sign_ups&&!await no({userAdapter:n.users,tenant_id:e.tenant.id,email:r})){const s=ve(t,{type:he.FAILED_SIGNUP,description:"Public signup is disabled"});throw await n.logs.create(e.tenant.id,s),new N(400,{message:"Signups are disabled for this client"})}await ny(t)(t.var.tenant_id||"",r)}function to(t,e){return{...e,users:{...e.users,create:iy(t,e)}}}function Rf(t){return to(t,t.env.data)}async function Lf(t,e,n){return(await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n}`})).users}async function si({userAdapter:t,tenant_id:e,email:n,provider:r}){const{users:i}=await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n} provider:${r}`});return i.length>1&&console.error("More than one user found for same email and provider"),i[0]||null}async function no({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(l=>!(l.provider==="auth2"&&!l.email_verified));if(i.length===0)return;const s=i.filter(l=>!l.linked_to);if(s.length>0)return s.length>1&&console.error("More than one primary user found for same email"),s[0];const o=await t.get(e,(c=i[0])==null?void 0:c.linked_to);if(!o)throw new Error("Primary account not found");return o}async function cs({userAdapter:t,tenant_id:e,email:n,provider:r}){const i=await si({userAdapter:t,tenant_id:e,email:n,provider:r});return i?i.linked_to?t.get(e,i.linked_to):i:null}async function ro(t,e){const{email:n,provider:r,connection:i,client:s,userId:o,isSocial:c,profileData:l={},ip:u=""}=e;let p=await cs({userAdapter:t.env.data.users,tenant_id:e.client.tenant.id,email:n,provider:r});if(!p){const h={user_id:`${r}|${o||Ys()}`,email:n,name:n,provider:r,connection:i,email_verified:!0,last_ip:u,is_social:c,last_login:new Date().toISOString(),profileData:JSON.stringify(l)};p=await Rf(t).users.create(s.tenant.id,h),t.set("user_id",p.user_id)}return p}const Yt=a.z.object({page:a.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:a.z.string().min(1).optional().default("10").transform(t=>parseInt(t,10)).openapi({description:"The number of items per page"}),include_totals:a.z.string().optional().default("false").transform(t=>t==="true").openapi({description:"If the total number of items should be included in the response"}),sort:a.z.string().regex(/^.+:(-1|1)$/).optional().openapi({description:"A property that should have the format 'string:-1' or 'string:1'"}),q:a.z.string().optional().openapi({description:"A lucene query string used to filter the results"})});function cr(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=nn.extend({users:a.z.array(bt)}),oy=nn.extend({sessions:a.z.array(Zs)}),ay=new a.OpenAPIHono().openapi(a.createRoute({tags:["users"],method:"get",path:"/",request:{query:Yt,headers:a.z.object({"tenant-id":a.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:a.z.union([a.z.array(bt),Fu])}},description:"List of users"}}}),async t=>{const{page:e,per_page:n,include_totals:r,sort:i,q:s}=t.req.valid("query"),{"tenant-id":o}=t.req.valid("header");if(s!=null&&s.includes("identities.profileData.email")){const p=s.split("=")[1],m=(await t.env.data.users.list(o,{page:e,per_page:n,include_totals:r,q:`email:${p}`})).users.filter(_=>_.linked_to),[v]=m;if(!v)return t.json([]);const f=await t.env.data.users.get(o,v.linked_to);if(!f)throw new N(500,{message:"Primary account not found"});return t.json([bt.parse(f)])}const c=["-_exists_:linked_to"];s&&c.push(s);const l=await t.env.data.users.list(o,{page:e,per_page:n,include_totals:r,sort:cr(i),q:c.join(" ")}),u=l.users.filter(p=>!p.linked_to);return r?t.json(Fu.parse({users:u,length:l.length,start:l.start,limit:l.limit})):t.json(a.z.array(bt).parse(u))}).openapi(a.createRoute({tags:["users"],method:"get",path:"/{user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:bt}},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 N(404);if(r.linked_to)throw new N(404,{message:"User is linked to another user"});return t.json(r)}).openapi(a.createRoute({tags:["users"],method:"delete",path:"/{user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.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 N(404);return t.text("OK")}).openapi(a.createRoute({tags:["users"],method:"post",path:"/",request:{headers:a.z.object({"tenant-id":a.z.string()}),body:{content:{"application/json":{schema:a.z.object({...es.shape})}}}},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:bt}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json");t.set("body",n);const{email:r}=n;if(!r)throw new N(400,{message:"Email is required"});const i=r.toLowerCase(),s=`${n.provider}|${n.user_id||Ys()}`;try{const o=await t.env.data.users.create(e,{email:i,user_id:s,name:n.name||i,provider:n.provider,connection:n.connection,email_verified:n.email_verified||!1,last_ip:"",is_social:!1,last_login:new Date().toISOString()});t.set("user_id",o.user_id);const c=ve(t,{type:he.SUCCESS_API_OPERATION,description:"User created"});et(t,t.env.data.logs.create(e,c));const l={...o,identities:[{connection:o.connection,provider:o.provider,user_id:Pu(o.user_id),isSocial:o.is_social}]};return t.json(bt.parse(l),{status:201})}catch(o){throw o.message==="User already exists"?new N(409,{message:"User already exists"}):o}}).openapi(a.createRoute({tags:["users"],method:"patch",path:"/{user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),body:{content:{"application/json":{schema:a.z.object({...es.shape,verify_email:a.z.boolean(),password:a.z.string()}).partial()}}},params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{description:"Status"}}}),async t=>{var u;const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json"),{user_id:r}=t.req.valid("param"),{verify_email:i,password:s,...o}=n,c=await t.env.data.users.get(e,r);if(!c)throw new N(404);if(o.email&&o.email!==c.email){const p=await Lf(t.env.data.users,e,o.email);if(p.length&&p.some(h=>h.user_id!==r))throw new N(409,{message:"Another user with the same email address already exists."})}if(c.linked_to)throw new N(404,{message:"User is linked to another user"});if(await t.env.data.users.update(e,r,o),s){const p=(u=c.identities)==null?void 0:u.find(h=>h.connection==="Username-Password-Authentication");if(!p)throw new N(400,{message:"User does not have a password identity"});await t.env.data.passwords.update(e,{user_id:`${p.provider}|${p.user_id}`,password:ii.hashSync(s,10),algorithm:"bcrypt"})}const l=await t.env.data.users.get(e,r);if(!l)throw new N(500);return t.json(l)}).openapi(a.createRoute({tags:["users"],method:"post",path:"/{user_id}/identities",request:{headers:a.z.object({"tenant-id":a.z.string()}),body:{content:{"application/json":{schema:a.z.union([a.z.object({link_with:a.z.string()}),a.z.object({user_id:a.z.string(),provider:a.z.string(),connection:a.z.string().optional()})])}}},params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:a.z.array(a.z.object({connection:a.z.string(),provider:a.z.string(),user_id:a.z.string(),isSocial:a.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,s=await t.env.data.users.get(e,r);if(!s)throw new N(400,{message:"Linking an inexistent identity is not allowed."});await t.env.data.users.update(e,i,{linked_to:r});const o=await t.env.data.users.list(e,{page:0,per_page:10,include_totals:!1,q:`linked_to:${r}`}),c=[s,...o.users].map(l=>({connection:l.connection,provider:l.provider,user_id:Pu(l.user_id),isSocial:l.is_social}));return t.json(c,{status:201})}).openapi(a.createRoute({tags:["users"],method:"delete",path:"/{user_id}/identities/{provider}/{linked_user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.z.string(),provider:a.z.string(),linked_user_id:a.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:a.z.array(bt)}},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 s=await t.env.data.users.get(e,n);if(!s)throw new N(404);return t.json([bt.parse(s)])}).openapi(a.createRoute({tags:["users"],method:"get",path:"/{user_id}/sessions",request:{query:Yt,headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:a.z.union([a.z.array(Zs),oy])}},description:"List of sessions"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{include_totals:n}=t.req.valid("query"),{"tenant-id":r}=t.req.valid("header"),i=await t.env.data.sessions.list(r,{page:0,per_page:10,include_totals:!1,q:`user_id:${e}`});return n?t.json(i):t.json(i.sessions)});/*! *****************************************************************************
29
+ </html>`;return new Response(i,{headers:{"Content-Type":"text/html"}})}async function G0(t,e,n,r,i){var m,v,f;if(!n.redirect_uri)throw new N(400,{message:"Missing redirect_uri in authParams"});const[s]=await t.env.data.keys.list();if(!s)throw new N(500,{message:"No signing key found"});if(!((m=e.addons)!=null&&m.samlp))throw new N(400,{message:`SAML Addon is not enabled for client ${e.id}`});const{recipient:o,audience:c}=e.addons.samlp,l=n.state||"";if(!o||!l||!r||!n.state)throw new N(400,{message:"Missing recipient or inResponseTo"});const u=JSON.parse(n.state),p=new URL(n.redirect_uri),h=await J0(t,{issuer:t.env.ISSUER,audience:c||n.client_id,destination:p.toString(),inResponseTo:u.requestId,userId:((f=(v=r.app_metadata)==null?void 0:v.vimeo)==null?void 0:f.user_id)||r.user_id,email:r.email,sessionIndex:i,signature:{privateKeyPem:s.pkcs7,cert:s.cert,kid:s.kid}});return W0(p.toString(),h,u.relayState)}async function J0(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,s=e.sessionNotOnOrAfter||r,o=e.responseId||`_${De()}`,c=e.assertionId||`_${De()}`,l=[{"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":s}},{"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":o,"@_InResponseTo":e.inResponseTo,"@_IssueInstant":i,"@_Version":"2.0"}}];let p=new K0.XMLBuilder({ignoreAttributes:!1,suppressEmptyNode:!0,preserveOrder:!0}).build(l);if(e.signature){const m=await fetch(t.env.SAML_SIGN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({xmlContent:p,privateKey:e.signature.privateKeyPem,publicCert:e.signature.cert})});if(!m.ok)throw new Error(`Failed to sign SAML response: ${m.status}`);p=await m.text()}return e.encode===!1?p:btoa(p)}var Z0={deno:"Deno",bun:"Bun",workerd:"Cloudflare-Workers",node:"Node.js"},Y0=()=>{var n,r;const t=globalThis;if(typeof navigator<"u"&&typeof navigator.userAgent=="string"){for(const[i,s]of Object.entries(Z0))if(X0(s))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"},X0=t=>navigator.userAgent.startsWith(t);function et(t,e){Y0()==="workerd"&&t.executionCtx.waitUntil(e)}const Hu=["sub","iss","aud","exp","nbf","iat","jti"];async function eo(t,e){var _,w;const{authParams:n,user:r,client:i,session_id:s}=e,c=(await t.env.data.keys.list()).filter(S=>!S.revoked_at||new Date(S.revoked_at)>new Date),l=c[c.length-1];if(!(l!=null&&l.pkcs7))throw new N(500,{message:"No signing key available"});const u=v_(l.pkcs7),p={aud:n.audience||"default",scope:n.scope||"",sub:(r==null?void 0:r.user_id)||n.client_id,iss:t.env.ISSUER,tenant_id:t.var.tenant_id,sid:s},h=r&&((_=n.scope)!=null&&_.split(" ").includes("openid"))?{aud:n.client_id,sub:r.user_id,iss:t.env.ISSUER,sid:s,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;(w=t.env.hooks)!=null&&w.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:(S,C)=>{if(Hu.includes(S))throw new Error(`Cannot overwrite reserved claim '${S}'`);p[S]=C}},idToken:{setCustomClaim:(S,C)=>{if(Hu.includes(S))throw new Error(`Cannot overwrite reserved claim '${S}'`);h&&(h[S]=C)}},access:{deny:S=>{throw new N(400,{message:`Access denied: ${S}`})}}});const m={includeIssuedTimestamp:!0,expiresIn:new sl(1,"d"),headers:{kid:l.kid}},v=await Lu("RS256",u,p,m),f=h?await Lu("RS256",u,h,m):void 0;return{access_token:v,refresh_token:e.refresh_token,id_token:f,token_type:"Bearer",expires_in:86400}}async function Q0(t,e){const{client:n,scope:r,audience:i=n.tenant.audience,session_id:s}=e;return await t.env.data.refreshTokens.create(n.tenant.id,{id:De(),session_id:s,client_id:n.id,expires_at:new Date(Date.now()+Xs*1e3).toISOString(),user_id:e.user.user_id,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:""},resource_servers:[{audience:i,scopes:r}],rotating:!1})}async function Pf(t,e){const{user:n,client:r,scope:i,audience:s}=e,o=await t.env.data.sessions.create(r.tenant.id,{id:De(),user_id:n.user_id,idle_expires_at:new Date(Date.now()+Xs*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:[r.id]}),c=i!=null&&i.split(" ").includes("offline_access")?await Q0(t,{...e,session_id:o.id,scope:i,audience:s}):void 0;return{...o,refresh_token:c}}async function sn(t,e){var v;const{authParams:n,user:r,client:i,ticketAuth:s}=e,o=ve(t,{type:he.SUCCESS_LOGIN,description:`Successful login for ${r.user_id}`,userId:r.user_id});if(et(t,t.env.data.logs.create(i.tenant.id,o)),et(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})),s){if(!e.loginSession)throw new N(500,{message:"Login session not found"});const f=y_(),_=De(12),w=await t.env.data.codes.create(i.tenant.id,{code_id:De(),code_type:"ticket",login_id:e.loginSession.login_id,expires_at:new Date(Date.now()+S_).toISOString(),code_verifier:[_,f].join("|")});return t.json({login_ticket:w.code_id,co_verifier:f,co_id:_})}let c=e.refreshToken,l=e.sessionId,u=r;if(!l){u=await ry(t,t.env.data)(i.tenant.id,r);const f=await Pf(t,{user:r,client:i,scope:n.scope,audience:n.audience});l=f.id,c=(v=f.refresh_token)==null?void 0:v.id}if(e.authParams.response_mode===Zt.SAML_POST)return G0(t,e.client,e.authParams,u,l);const p=await eo(t,{authParams:n,user:u,client:i,session_id:l,refresh_token:c}),h=new Headers({"set-cookie":zf(i.tenant.id,l)});if(n.response_mode===Zt.WEB_MESSAGE)return t.json(p,{headers:h});if((n.response_type||Jt.CODE)===Jt.CODE){if(!e.loginSession)throw new N(500,{message:"Login session not found"});const f=await t.env.data.codes.create(i.tenant.id,{code_id:De(),user_id:r.user_id,code_type:"authorization_code",login_id:e.loginSession.login_id,expires_at:new Date(Date.now()+k_*1e3).toISOString()});if(!n.redirect_uri)throw new N(400,{message:"Redirect uri not found"});const _=new URL(n.redirect_uri);_.searchParams.set("code",f.code_id),n.state&&_.searchParams.set("state",n.state),h.set("location",_.toString())}return new Response("Redirecting",{status:302,headers:h})}async function ey(t,e,n){const r=await t.env.data.tenants.get(e);if(!r)throw new Error(`Tenant not found: ${e}`);return eo(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:Jt.TOKEN,scope:n}})}async function dl(t,e,n){const r=await ey(t,n.tenant_id,"webhook");for await(const i of e)if(!(await fetch(i.url,{method:"POST",headers:{Authorization:`Bearer ${r.access_token}`,"Content-Type":"application/json"},body:JSON.stringify(n)})).ok){const o=ve(t,{type:he.FAILED_HOOK,description:`Failed to invoke hook ${i.hook_id}`});await t.env.data.logs.create(n.tenant_id,o)}}function ty(t){return async(e,n)=>{const{hooks:r}=await t.env.data.hooks.list(e);return await dl(t,r,{tenant_id:e,user:n,trigger_id:"post-user-registration"}),n}}function ny(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 dl(t,r,{tenant_id:e,email:n,trigger_id:"pre-user-signup"})}}function ry(t,e){return async(n,r)=>{const{hooks:i}=await e.hooks.list(n,{q:"trigger_id:post-user-login",page:0,per_page:100,include_totals:!1});return await dl(t,i,{tenant_id:n,user:r,trigger_id:"post-user-login"}),r}}function iy(t,e){return async(n,r)=>{var o,c,l;const i={method:t.req.method,ip:t.req.query("x-real-ip")||"",user_agent:t.req.query("user-agent"),url:((o=t.var.loginSession)==null?void 0:o.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(u,p)=>{r[u]=p}}})}catch{const p=ve(t,{type:he.FAILED_SIGNUP,description:"Pre user registration hook failed"});await e.logs.create(n,p)}let s=await d_(e)(n,r);if((l=t.env.hooks)!=null&&l.onExecutePostUserRegistration)try{await t.env.hooks.onExecutePostUserRegistration({user:r,request:i},{user:{}})}catch{const p=ve(t,{type:he.FAILED_SIGNUP,description:"Post user registration hook failed"});await t.env.data.logs.create(n,p)}return await ty(t)(n,s),s}}async function sy(t,e,n,r){if(e.disable_sign_ups&&!await no({userAdapter:n.users,tenant_id:e.tenant.id,email:r})){const s=ve(t,{type:he.FAILED_SIGNUP,description:"Public signup is disabled"});throw await n.logs.create(e.tenant.id,s),new N(400,{message:"Signups are disabled for this client"})}await ny(t)(t.var.tenant_id||"",r)}function to(t,e){return{...e,users:{...e.users,create:iy(t,e)}}}function Rf(t){return to(t,t.env.data)}async function Lf(t,e,n){return(await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n}`})).users}async function si({userAdapter:t,tenant_id:e,email:n,provider:r}){const{users:i}=await t.list(e,{page:0,per_page:10,include_totals:!1,q:`email:${n} provider:${r}`});return i.length>1&&console.error("More than one user found for same email and provider"),i[0]||null}async function no({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(l=>!(l.provider==="auth2"&&!l.email_verified));if(i.length===0)return;const s=i.filter(l=>!l.linked_to);if(s.length>0)return s.length>1&&console.error("More than one primary user found for same email"),s[0];const o=await t.get(e,(c=i[0])==null?void 0:c.linked_to);if(!o)throw new Error("Primary account not found");return o}async function cs({userAdapter:t,tenant_id:e,email:n,provider:r}){const i=await si({userAdapter:t,tenant_id:e,email:n,provider:r});return i?i.linked_to?t.get(e,i.linked_to):i:null}async function ro(t,e){const{email:n,provider:r,connection:i,client:s,userId:o,isSocial:c,profileData:l={},ip:u=""}=e;let p=await cs({userAdapter:t.env.data.users,tenant_id:e.client.tenant.id,email:n,provider:r});if(!p){const h={user_id:`${r}|${o||Ys()}`,email:n,name:n,provider:r,connection:i,email_verified:!0,last_ip:u,is_social:c,last_login:new Date().toISOString(),profileData:JSON.stringify(l)};p=await Rf(t).users.create(s.tenant.id,h),t.set("user_id",p.user_id)}return p}const Yt=a.z.object({page:a.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:a.z.string().min(1).optional().default("10").transform(t=>parseInt(t,10)).openapi({description:"The number of items per page"}),include_totals:a.z.string().optional().default("false").transform(t=>t==="true").openapi({description:"If the total number of items should be included in the response"}),sort:a.z.string().regex(/^.+:(-1|1)$/).optional().openapi({description:"A property that should have the format 'string:-1' or 'string:1'"}),q:a.z.string().optional().openapi({description:"A lucene query string used to filter the results"})});function cr(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=nn.extend({users:a.z.array(bt)}),oy=nn.extend({sessions:a.z.array(Zs)}),ay=new a.OpenAPIHono().openapi(a.createRoute({tags:["users"],method:"get",path:"/",request:{query:Yt,headers:a.z.object({"tenant-id":a.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:a.z.union([a.z.array(bt),Fu])}},description:"List of users"}}}),async t=>{const{page:e,per_page:n,include_totals:r,sort:i,q:s}=t.req.valid("query"),{"tenant-id":o}=t.req.valid("header");if(s!=null&&s.includes("identities.profileData.email")){const p=s.split("=")[1],m=(await t.env.data.users.list(o,{page:e,per_page:n,include_totals:r,q:`email:${p}`})).users.filter(_=>_.linked_to),[v]=m;if(!v)return t.json([]);const f=await t.env.data.users.get(o,v.linked_to);if(!f)throw new N(500,{message:"Primary account not found"});return t.json([bt.parse(f)])}const c=["-_exists_:linked_to"];s&&c.push(s);const l=await t.env.data.users.list(o,{page:e,per_page:n,include_totals:r,sort:cr(i),q:c.join(" ")}),u=l.users.filter(p=>!p.linked_to);return r?t.json(Fu.parse({users:u,length:l.length,start:l.start,limit:l.limit})):t.json(a.z.array(bt).parse(u))}).openapi(a.createRoute({tags:["users"],method:"get",path:"/{user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:bt}},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 N(404);if(r.linked_to)throw new N(404,{message:"User is linked to another user"});return t.json(r)}).openapi(a.createRoute({tags:["users"],method:"delete",path:"/{user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.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 N(404);return t.text("OK")}).openapi(a.createRoute({tags:["users"],method:"post",path:"/",request:{headers:a.z.object({"tenant-id":a.z.string()}),body:{content:{"application/json":{schema:a.z.object({...es.shape})}}}},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:bt}},description:"Status"}}}),async t=>{const{"tenant-id":e}=t.req.valid("header"),n=t.req.valid("json");t.set("body",n);const{email:r}=n;if(!r)throw new N(400,{message:"Email is required"});const i=r.toLowerCase(),s=`${n.provider}|${n.user_id||Ys()}`;try{const o=await t.env.data.users.create(e,{email:i,user_id:s,name:n.name||i,provider:n.provider,connection:n.connection,email_verified:n.email_verified||!1,last_ip:"",is_social:!1,last_login:new Date().toISOString()});t.set("user_id",o.user_id);const c=ve(t,{type:he.SUCCESS_API_OPERATION,description:"User created"});et(t,t.env.data.logs.create(e,c));const l={...o,identities:[{connection:o.connection,provider:o.provider,user_id:Pu(o.user_id),isSocial:o.is_social}]};return t.json(bt.parse(l),{status:201})}catch(o){throw o.message==="User already exists"?new N(409,{message:"User already exists"}):o}}).openapi(a.createRoute({tags:["users"],method:"patch",path:"/{user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),body:{content:{"application/json":{schema:a.z.object({...es.shape,verify_email:a.z.boolean(),password:a.z.string()}).partial()}}},params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{description:"Status"}}}),async t=>{var p;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:s,password:o,...c}=r,l=await e.users.get(n,i);if(!l)throw new N(404);if(c.email&&c.email!==l.email){const h=await Lf(t.env.data.users,n,c.email);if(h.length&&h.some(m=>m.user_id!==i))throw new N(409,{message:"Another user with the same email address already exists."})}if(l.linked_to)throw new N(404,{message:"User is linked to another user"});if(await t.env.data.users.update(n,i,c),o){const h=(p=l.identities)==null?void 0:p.find(f=>f.connection==="Username-Password-Authentication");if(!h)throw new N(400,{message:"User does not have a password identity"});const m={user_id:h.user_id,password:await ii.hash(o,10),algorithm:"bcrypt"};await e.passwords.get(n,h.user_id)?await e.passwords.update(n,m):await e.passwords.create(n,m)}const u=await t.env.data.users.get(n,i);if(!u)throw new N(500);return t.json(u)}).openapi(a.createRoute({tags:["users"],method:"post",path:"/{user_id}/identities",request:{headers:a.z.object({"tenant-id":a.z.string()}),body:{content:{"application/json":{schema:a.z.union([a.z.object({link_with:a.z.string()}),a.z.object({user_id:a.z.string(),provider:a.z.string(),connection:a.z.string().optional()})])}}},params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:a.z.array(a.z.object({connection:a.z.string(),provider:a.z.string(),user_id:a.z.string(),isSocial:a.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,s=await t.env.data.users.get(e,r);if(!s)throw new N(400,{message:"Linking an inexistent identity is not allowed."});await t.env.data.users.update(e,i,{linked_to:r});const o=await t.env.data.users.list(e,{page:0,per_page:10,include_totals:!1,q:`linked_to:${r}`}),c=[s,...o.users].map(l=>({connection:l.connection,provider:l.provider,user_id:Pu(l.user_id),isSocial:l.is_social}));return t.json(c,{status:201})}).openapi(a.createRoute({tags:["users"],method:"delete",path:"/{user_id}/identities/{provider}/{linked_user_id}",request:{headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.z.string(),provider:a.z.string(),linked_user_id:a.z.string()})},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:a.z.array(bt)}},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 s=await t.env.data.users.get(e,n);if(!s)throw new N(404);return t.json([bt.parse(s)])}).openapi(a.createRoute({tags:["users"],method:"get",path:"/{user_id}/sessions",request:{query:Yt,headers:a.z.object({"tenant-id":a.z.string()}),params:a.z.object({user_id:a.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:a.z.union([a.z.array(Zs),oy])}},description:"List of sessions"}}}),async t=>{const{user_id:e}=t.req.valid("param"),{include_totals:n}=t.req.valid("query"),{"tenant-id":r}=t.req.valid("header"),i=await t.env.data.sessions.list(r,{page:0,per_page:10,include_totals:!1,q:`user_id:${e}`});return n?t.json(i):t.json(i.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
package/dist/authhero.mjs CHANGED
@@ -6265,44 +6265,48 @@ const zu = jn.extend({
6265
6265
  }
6266
6266
  }),
6267
6267
  async (t) => {
6268
- var u;
6269
- const { "tenant-id": e } = t.req.valid("header"), n = t.req.valid("json"), { user_id: r } = t.req.valid("param"), { verify_email: i, password: s, ...o } = n, c = await t.env.data.users.get(e, r);
6270
- if (!c)
6268
+ var p;
6269
+ 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: s, password: o, ...c } = r, l = await e.users.get(n, i);
6270
+ if (!l)
6271
6271
  throw new j(404);
6272
- if (o.email && o.email !== c.email) {
6273
- const p = await kf(
6272
+ if (c.email && c.email !== l.email) {
6273
+ const h = await kf(
6274
6274
  t.env.data.users,
6275
- e,
6276
- o.email
6275
+ n,
6276
+ c.email
6277
6277
  );
6278
- if (p.length && p.some((h) => h.user_id !== r))
6278
+ if (h.length && h.some((m) => m.user_id !== i))
6279
6279
  throw new j(409, {
6280
6280
  message: "Another user with the same email address already exists."
6281
6281
  });
6282
6282
  }
6283
- if (c.linked_to)
6283
+ if (l.linked_to)
6284
6284
  throw new j(404, {
6285
6285
  // not the auth0 error message but I'd rather deviate here
6286
6286
  message: "User is linked to another user"
6287
6287
  });
6288
- if (await t.env.data.users.update(e, r, o), s) {
6289
- const p = (u = c.identities) == null ? void 0 : u.find(
6290
- (h) => h.connection === "Username-Password-Authentication"
6288
+ if (await t.env.data.users.update(n, i, c), o) {
6289
+ const h = (p = l.identities) == null ? void 0 : p.find(
6290
+ (f) => f.connection === "Username-Password-Authentication"
6291
6291
  );
6292
- if (!p)
6292
+ if (!h)
6293
6293
  throw new j(400, {
6294
6294
  message: "User does not have a password identity"
6295
6295
  });
6296
- await t.env.data.passwords.update(e, {
6297
- user_id: `${p.provider}|${p.user_id}`,
6298
- password: si.hashSync(s, 10),
6296
+ const m = {
6297
+ user_id: h.user_id,
6298
+ password: await si.hash(o, 10),
6299
6299
  algorithm: "bcrypt"
6300
- });
6300
+ };
6301
+ await e.passwords.get(
6302
+ n,
6303
+ h.user_id
6304
+ ) ? await e.passwords.update(n, m) : await e.passwords.create(n, m);
6301
6305
  }
6302
- const l = await t.env.data.users.get(e, r);
6303
- if (!l)
6306
+ const u = await t.env.data.users.get(n, i);
6307
+ if (!u)
6304
6308
  throw new j(500);
6305
- return t.json(l);
6309
+ return t.json(u);
6306
6310
  }
6307
6311
  ).openapi(
6308
6312
  q({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "authhero",
3
- "version": "0.84.0",
3
+ "version": "0.85.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -25,7 +25,7 @@
25
25
  "vite": "^5.4.11",
26
26
  "vite-plugin-dts": "^4.3.0",
27
27
  "vitest": "^2.1.5",
28
- "@authhero/kysely-adapter": "^0.9.3"
28
+ "@authhero/kysely-adapter": "^9.3.0"
29
29
  },
30
30
  "dependencies": {
31
31
  "@peculiar/x509": "^1.12.3",