@authhero/multi-tenancy 14.16.0 → 14.17.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.
@@ -1 +1 @@
1
- "use strict";var oe=Object.defineProperty;var ie=(e,t,n)=>t in e?oe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var k=(e,t,n)=>ie(e,typeof t!="symbol"?t+"":t,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ce=require("hono"),T=require("authhero"),_=require("@hono/zod-openapi");function J(e){const{controlPlaneTenantId:t,requireOrganizationMatch:n=!0}=e;return{async onTenantAccessValidation(r,a){if(a===t)return!0;if(n){const o=r.var.org_name,s=r.var.organization_id,i=o||s;return i?i.toLowerCase()===a.toLowerCase():!1}return!0}}}function X(e,t,n,r){if(t===n)return!0;const a=r||e;return a?a.toLowerCase()===t.toLowerCase():!1}function Y(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(n){console.error(`Failed to resolve data adapters for tenant ${t}:`,n);return}}}}function le(e){return`urn:authhero:tenant:${e.toLowerCase()}`}function N(e){return{async beforeCreate(t,n){return!n.audience&&n.id?{...n,audience:le(n.id)}:n},async afterCreate(t,n){const{accessControl:r,databaseIsolation:a}=e;r&&t.ctx&&await ue(t,n,r),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(t,n){const{accessControl:r,databaseIsolation:a}=e;if(r)try{const s=(await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(i=>i.name===n);s&&await t.adapters.organizations.remove(r.controlPlaneTenantId,s.id)}catch(o){console.warn(`Failed to remove organization for tenant ${n}:`,o)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(o){console.warn(`Failed to deprovision database for tenant ${n}:`,o)}}}}async function ue(e,t,n){const{controlPlaneTenantId:r,defaultPermissions:a,defaultRoles:o,issuer:s,adminRoleName:i="Tenant Admin",adminRoleDescription:u="Full access to all tenant management operations",addCreatorToOrganization:c=!0}=n,l=await e.adapters.organizations.create(r,{name:t.id,display_name:t.friendly_name||t.id});let g;if(s&&(g=await fe(e,r,i,u)),c&&e.ctx){const d=e.ctx.var.user;if(d!=null&&d.sub&&!await de(e,r,d.sub))try{await e.adapters.userOrganizations.create(r,{user_id:d.sub,organization_id:l.id}),g&&await e.adapters.userRoles.create(r,d.sub,g,l.id)}catch(m){console.warn(`Failed to add creator ${d.sub} to organization ${l.id}:`,m)}}o&&o.length>0&&console.log(`Would assign roles ${o.join(", ")} to organization ${l.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${l.id}`)}async function de(e,t,n){const r=await e.adapters.userRoles.list(t,n,void 0,"");for(const a of r)if((await e.adapters.rolePermissions.list(t,a.id,{per_page:1e3})).some(i=>i.permission_name==="admin:organizations"))return!0;return!1}async function fe(e,t,n,r){const o=(await e.adapters.roles.list(t,{})).roles.find(c=>c.name===n);if(o)return o.id;const s=await e.adapters.roles.create(t,{name:n,description:r}),i=T.MANAGEMENT_API_AUDIENCE,u=T.MANAGEMENT_API_SCOPES.map(c=>({role_id:s.id,resource_server_identifier:i,permission_name:c.value}));return await e.adapters.rolePermissions.assign(t,s.id,u),s.id}function U(e,t,n=()=>!0){const{controlPlaneTenantId:r,getChildTenantIds:a,getAdapters:o}=e,s=new Map;async function i(l,g,d){return(await t(l).list(g,{q:`name:${d}`,per_page:1}))[0]??null}async function u(l){const g=await a(),d=t(await o(r));await Promise.all(g.map(async f=>{try{const m=await o(f),w=t(m),y={...d.transform(l),is_system:!0},C=await i(m,f,l.name),A=C?w.getId(C):void 0;if(C&&A){const S=w.preserveOnUpdate?w.preserveOnUpdate(C,y):y;await w.update(f,A,S)}else await w.create(f,y)}catch(m){console.error(`Failed to sync ${d.listKey} "${l.name}" to tenant "${f}":`,m)}}))}async function c(l){const g=await a();await Promise.all(g.map(async d=>{try{const f=await o(d),m=t(f),w=await i(f,d,l),b=w?m.getId(w):void 0;w&&b&&await m.remove(d,b)}catch(f){console.error(`Failed to delete entity "${l}" from tenant "${d}":`,f)}}))}return{afterCreate:async(l,g)=>{l.tenantId===r&&n(g)&&await u(g)},afterUpdate:async(l,g,d)=>{l.tenantId===r&&n(d)&&await u(d)},beforeDelete:async(l,g)=>{if(l.tenantId!==r)return;const f=await t(l.adapters).get(l.tenantId,g);f&&n(f)&&s.set(g,f)},afterDelete:async(l,g)=>{if(l.tenantId!==r)return;const d=s.get(g);d&&(s.delete(g),await c(d.name))}}}function G(e,t,n=()=>!0){const{controlPlaneTenantId:r,getControlPlaneAdapters:a,getAdapters:o}=e;return{async afterCreate(s,i){if(i.id!==r)try{const u=await a(),c=await o(i.id),l=t(u),g=t(c),d=await T.fetchAll(f=>l.listPaginated(r,f),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(d.filter(f=>n(f)).map(async f=>{try{const m=l.transform(f);await g.create(i.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync entity to new tenant "${i.id}":`,m)}}))}catch(u){console.error(`Failed to sync entities to new tenant "${i.id}":`,u)}}}}const L=e=>({list:async(t,n)=>(await e.resourceServers.list(t,n)).resource_servers,listPaginated:(t,n)=>e.resourceServers.list(t,n),get:(t,n)=>e.resourceServers.get(t,n),create:(t,n)=>e.resourceServers.create(t,n),update:(t,n,r)=>e.resourceServers.update(t,n,r),remove:(t,n)=>e.resourceServers.remove(t,n),listKey:"resource_servers",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,identifier:t.identifier,scopes:t.scopes,signing_alg:t.signing_alg,token_lifetime:t.token_lifetime,token_lifetime_for_web:t.token_lifetime_for_web})}),W=e=>({list:async(t,n)=>(await e.roles.list(t,n)).roles,listPaginated:(t,n)=>e.roles.list(t,n),get:(t,n)=>e.roles.get(t,n),create:(t,n)=>e.roles.create(t,n),update:(t,n,r)=>e.roles.update(t,n,r),remove:(t,n)=>e.roles.remove(t,n),listKey:"roles",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,description:t.description})});function K(e){var t;return((t=e.metadata)==null?void 0:t.sync)!==!1}function Z(e){const{sync:t={},filters:n={}}=e,r=t.resourceServers??!0,a=t.roles??!0,o=m=>K(m)?n.resourceServers?n.resourceServers(m):!0:!1,s=m=>K(m)?n.roles?n.roles(m):!0:!1,i=r?U(e,L,o):void 0,u=a?U(e,W,s):void 0,c=r?G(e,L,o):void 0,l=a?G(e,W,s):void 0,g=a?{async afterCreate(m,w){var b;if(w.id!==e.controlPlaneTenantId){await((b=l==null?void 0:l.afterCreate)==null?void 0:b.call(l,m,w));try{const y=await e.getControlPlaneAdapters(),C=await e.getAdapters(w.id),A=await T.fetchAll(p=>y.roles.list(e.controlPlaneTenantId,p),"roles",{cursorField:"id",pageSize:100}),S=new Map;for(const p of A.filter(P=>{var h;return((h=n.roles)==null?void 0:h.call(n,P))??!0})){const P=await d(C,w.id,p.name);P&&S.set(p.name,P.id)}for(const p of A.filter(P=>{var h;return((h=n.roles)==null?void 0:h.call(n,P))??!0})){const P=S.get(p.name);if(P)try{const h=await y.rolePermissions.list(e.controlPlaneTenantId,p.id,{});h.length>0&&await C.rolePermissions.assign(w.id,P,h.map(R=>({role_id:P,resource_server_identifier:R.resource_server_identifier,permission_name:R.permission_name})))}catch(h){console.error(`Failed to sync permissions for role "${p.name}" to tenant "${w.id}":`,h)}}}catch(y){console.error(`Failed to sync role permissions to tenant "${w.id}":`,y)}}}}:void 0;async function d(m,w,b){return(await m.roles.list(w,{q:`name:${b}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:i,roles:u},tenantHooks:{async afterCreate(m,w){const b=[c==null?void 0:c.afterCreate,(g==null?void 0:g.afterCreate)??(l==null?void 0:l.afterCreate)],y=[];for(const C of b)if(C)try{await C(m,w)}catch(A){y.push(A instanceof Error?A:new Error(String(A)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,y.map(C=>C.message).join("; "))}}}}var I=class extends Error{constructor(t=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});k(this,"res");k(this,"status");this.res=n==null?void 0:n.res,this.status=t}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function q(e,t){const n=new _.OpenAPIHono;return n.openapi(_.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:T.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:_.z.object({tenants:_.z.array(T.tenantSchema),start:_.z.number().optional(),limit:_.z.number().optional(),length:_.z.number().optional()})}},description:"List of tenants"}}}),async r=>{var m,w,b,y,C,A;const a=r.req.valid("query"),{page:o,per_page:s,include_totals:i,q:u}=a,c=r.var.user,l=(c==null?void 0:c.permissions)||[];if(l.includes("auth:read")||l.includes("admin:organizations")){const S=await r.env.data.tenants.list({page:o,per_page:s,include_totals:i,q:u});return i?r.json({tenants:S.tenants,start:((m=S.totals)==null?void 0:m.start)??0,limit:((w=S.totals)==null?void 0:w.limit)??s,length:S.tenants.length}):r.json({tenants:S.tenants})}const d=((b=e.accessControl)==null?void 0:b.controlPlaneTenantId)??((y=r.env.data.multiTenancyConfig)==null?void 0:y.controlPlaneTenantId);if(d&&(c!=null&&c.sub)){const p=(await T.fetchAll(M=>r.env.data.userOrganizations.listUserOrganizations(d,c.sub,M),"organizations")).map(M=>M.name);if(p.length===0)return i?r.json({tenants:[],start:0,limit:s??50,length:0}):r.json({tenants:[]});const P=p.length,h=o??0,R=s??50,F=h*R,D=p.slice(F,F+R);if(D.length===0)return i?r.json({tenants:[],start:F,limit:R,length:P}):r.json({tenants:[]});const z=D.map(M=>`id:${M}`).join(" OR "),v=u?`(${z}) AND (${u})`:z,$=await r.env.data.tenants.list({q:v,per_page:R,include_totals:!1});return i?r.json({tenants:$.tenants,start:F,limit:R,length:P}):r.json({tenants:$.tenants})}const f=await r.env.data.tenants.list({page:o,per_page:s,include_totals:i,q:u});return i?r.json({tenants:f.tenants,start:((C=f.totals)==null?void 0:C.start)??0,limit:((A=f.totals)==null?void 0:A.limit)??s,length:f.tenants.length}):r.json({tenants:f.tenants})}),n.openapi(_.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:T.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:T.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async r=>{var u,c;const a=r.var.user;if(!(a!=null&&a.sub))throw new I(401,{message:"Authentication required to create tenants"});let o=r.req.valid("json");const s={adapters:r.env.data,ctx:r};(u=t.tenants)!=null&&u.beforeCreate&&(o=await t.tenants.beforeCreate(s,o));const i=await r.env.data.tenants.create(o);return(c=t.tenants)!=null&&c.afterCreate&&await t.tenants.afterCreate(s,i),r.json(i,201)}),n.openapi(_.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:_.z.object({id:_.z.string()})},security:[{Bearer:["delete:tenants"]}],responses:{204:{description:"Tenant deleted"},403:{description:"Access denied or cannot delete the control plane"},404:{description:"Tenant not found"}}}),async r=>{var u,c,l,g;const{id:a}=r.req.valid("param"),o=((u=e.accessControl)==null?void 0:u.controlPlaneTenantId)??((c=r.env.data.multiTenancyConfig)==null?void 0:c.controlPlaneTenantId);if(o){const d=r.var.user;if(!(d!=null&&d.sub))throw new I(401,{message:"Authentication required"});if(a===o)throw new I(403,{message:"Cannot delete the control plane"});if(!(await T.fetchAll(w=>r.env.data.userOrganizations.listUserOrganizations(o,d.sub,w),"organizations")).some(w=>w.name===a))throw new I(403,{message:"Access denied to this tenant"})}if(!await r.env.data.tenants.get(a))throw new I(404,{message:"Tenant not found"});const i={adapters:r.env.data,ctx:r};return(l=t.tenants)!=null&&l.beforeDelete&&await t.tenants.beforeDelete(i,a),await r.env.data.tenants.remove(a),(g=t.tenants)!=null&&g.afterDelete&&await t.tenants.afterDelete(i,a),r.body(null,204)}),n.openapi(_.createRoute({tags:["tenants","settings"],method:"get",path:"/settings",request:{headers:_.z.object({"tenant-id":_.z.string().optional()})},security:[{Bearer:["read:tenants","auth:read"]}],responses:{200:{content:{"application/json":{schema:T.tenantSchema}},description:"Current tenant settings"}}}),async r=>{const a=await r.env.data.tenants.get(r.var.tenant_id);if(!a)throw new I(404,{message:"Tenant not found"});return r.json(a)}),n.openapi(_.createRoute({tags:["tenants","settings"],method:"patch",path:"/settings",request:{headers:_.z.object({"tenant-id":_.z.string().optional()}),body:{content:{"application/json":{schema:_.z.object(T.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["update:tenants","auth:write"]}],responses:{200:{content:{"application/json":{schema:T.tenantSchema}},description:"Updated tenant settings"}}}),async r=>{const a=r.req.valid("json"),{id:o,...s}=a,i=await r.env.data.tenants.get(r.var.tenant_id);if(!i)throw new I(404,{message:"Tenant not found"});const u=T.deepMergePatch(i,s);await r.env.data.tenants.update(r.var.tenant_id,u);const c=await r.env.data.tenants.get(r.var.tenant_id);if(!c)throw new I(500,{message:"Failed to retrieve updated tenant"});return r.json(c)}),n}function me(e){const t=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:n,type:r}of t){const a=e.match(n);if(a&&a[1])return{type:r,id:a[1]}}return null}async function ge(e,t,n){try{switch(n.type){case"resource_server":{const r=await e.resourceServers.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"role":{const r=await e.roles.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"connection":{const r=await e.connections.get(t,n.id);return(r==null?void 0:r.is_system)===!0}default:return!1}}catch{return!1}}function we(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function x(){return async(e,t)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return t();const n=me(e.req.path);if(!n)return t();const r=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!r)return t();if(await ge(e.env.data,r,n))throw new I(403,{message:`This ${we(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function E(e,t){const n=t.find(a=>a.strategy===e.strategy);if(!(n!=null&&n.options))return e;const r=T.connectionSchema.passthrough().parse({...n,...e});return r.options=T.connectionOptionsSchema.passthrough().parse({...n.options||{},...e.options}),r}function j(e,t){const n=[...t||[],...e||[]];return[...new Set(n)]}function pe(e,t){if(!(t!=null&&t.length))return e||[];if(!(e!=null&&e.length))return t;const n=new Map;for(const r of t)n.set(r.value,r);for(const r of e)n.set(r.value,r);return Array.from(n.values())}function Q(e,t){return t?{...e,scopes:pe(e.scopes,t.scopes)}:e}function V(e,t){return t?{...e,callbacks:j(e.callbacks,t.callbacks),web_origins:j(e.web_origins,t.web_origins),allowed_logout_urls:j(e.allowed_logout_urls,t.allowed_logout_urls),allowed_origins:j(e.allowed_origins,t.allowed_origins)}:e}function ee(e,t){const{controlPlaneTenantId:n,controlPlaneClientId:r}=t;return{...e,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r},connections:{...e.connections,get:async(a,o)=>{const s=await e.connections.get(a,o);if(!s||!n||a===n)return s;const i=await e.connections.list(n);return E(s,i.connections||[])},list:async(a,o)=>{const s=await e.connections.list(a,o);if(!n||a===n)return s;const i=await e.connections.list(n),u=s.connections.map(c=>E(c,i.connections||[]));return{...s,connections:u}}},clientConnections:{...e.clientConnections,listByClient:async(a,o)=>{let s=await e.clientConnections.listByClient(a,o);if(s.length===0&&(s=(await e.connections.list(a)).connections||[]),!n||a===n)return s;const i=await e.connections.list(n);return s.map(u=>E(u,i.connections||[]))}},clients:{...e.clients,get:async(a,o)=>{const s=await e.clients.get(a,o);if(!s)return null;if(!n||!r||a===n&&o===r)return s;const i=await e.clients.get(n,r);return V(s,i)},getByClientId:async a=>{const o=await e.clients.getByClientId(a);if(!o)return null;if(!n||!r||o.tenant_id===n&&o.client_id===r)return o;const s=await e.clients.get(n,r);return{...V(o,s),tenant_id:o.tenant_id}}},emailProviders:{...e.emailProviders,get:async a=>{const o=await e.emailProviders.get(a);return o||(!n||a===n?null:e.emailProviders.get(n))}},resourceServers:{...e.resourceServers,get:async(a,o)=>{const s=await e.resourceServers.get(a,o);if(!s||!n||a===n)return s;const u=(await e.resourceServers.list(n,{q:`identifier:${s.identifier}`,per_page:1})).resource_servers[0]??null;return Q(s,u)},list:async(a,o)=>{const s=await e.resourceServers.list(a,o);if(!n||a===n)return s;const i=await e.resourceServers.list(n),u=new Map(i.resource_servers.map(l=>[l.identifier,l])),c=s.resource_servers.map(l=>Q(l,u.get(l.identifier)??null));return{...s,resource_servers:c}}}}}function B(e,t){return ee(e,t)}function te(e){return async(t,n)=>{const r=t.var.user;return(r==null?void 0:r.tenant_id)===e&&r.org_name&&t.set("tenant_id",r.org_name),n()}}function ne(e){return async(t,n)=>{if(!e.accessControl)return n();const{controlPlaneTenantId:r}=e.accessControl,a=t.var.org_name,o=t.var.organization_id,s=a||o;let i=t.var.tenant_id;const u=t.var.user,l=(u!=null&&u.aud?Array.isArray(u.aud)?u.aud:[u.aud]:[]).includes(T.MANAGEMENT_API_AUDIENCE);if(!i&&s&&l&&(t.set("tenant_id",s),i=s),!i)throw new I(400,{message:"Tenant ID not found in request"});if(!X(o,i,r,a))throw new I(403,{message:`Access denied to tenant ${i}`});return n()}}function re(e){return async(t,n)=>{if(!e.subdomainRouting)return n();const{baseDomain:r,reservedSubdomains:a=[],resolveSubdomain:o}=e.subdomainRouting,s=t.req.header("host")||"";let i=null;if(s.endsWith(r)){const c=s.slice(0,-(r.length+1));c&&!c.includes(".")&&(i=c)}if(i&&a.includes(i)&&(i=null),!i)return e.accessControl&&t.set("tenant_id",e.accessControl.controlPlaneTenantId),n();let u=null;if(o)u=await o(i);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const c=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,i);c&&(u=c.id)}catch{}if(!u)throw new I(404,{message:`Tenant not found for subdomain: ${i}`});return t.set("tenant_id",u),n()}}function ae(e){return async(t,n)=>{if(!e.databaseIsolation)return n();const r=t.var.tenant_id;if(!r)throw new I(400,{message:"Tenant ID not found in request"});try{const a=await e.databaseIsolation.getAdapters(r);t.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${r}:`,a),new I(500,{message:"Failed to resolve tenant database"})}return n()}}function H(e){const t=re(e),n=ne(e),r=ae(e);return async(a,o)=>(await t(a,async()=>{}),await n(a,async()=>{}),await r(a,async()=>{}),o())}function he(e){const{dataAdapter:t,controlPlane:n,controlPlane:{tenantId:r="control_plane",clientId:a}={},sync:o={resourceServers:!0,roles:!0},defaultPermissions:s=["tenant:admin"],requireOrganizationMatch:i=!1,managementApiExtensions:u=[],entityHooks:c,getChildTenantIds:l,getAdapters:g,...d}=e;let f=t,m=t;n&&(f=B(t,{controlPlaneTenantId:r,controlPlaneClientId:a}),m={...t,multiTenancyConfig:{controlPlaneTenantId:r,controlPlaneClientId:a}});const w=o!==!1,b=w?{resourceServers:o.resourceServers??!0,roles:o.roles??!0}:{resourceServers:!1,roles:!1},A={controlPlaneTenantId:r,getChildTenantIds:l??(async()=>(await T.fetchAll(v=>f.tenants.list(v),"tenants",{cursorField:"id",pageSize:100})).filter(v=>v.id!==r).map(v=>v.id)),getAdapters:g??(async()=>f),getControlPlaneAdapters:async()=>f,sync:b},{entityHooks:S,tenantHooks:p}=Z(A),P={resourceServers:[S.resourceServers,...(c==null?void 0:c.resourceServers)??[]],roles:[S.roles,...(c==null?void 0:c.roles)??[]],connections:(c==null?void 0:c.connections)??[],tenants:(c==null?void 0:c.tenants)??[],rolePermissions:(c==null?void 0:c.rolePermissions)??[]},h=N({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:i,defaultPermissions:s}}),F=q({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:i,defaultPermissions:s}},{tenants:{async beforeCreate(z,v){return h.beforeCreate&&(v=await h.beforeCreate(z,v)),p.beforeCreate&&(v=await p.beforeCreate(z,v)),v},async afterCreate(z,v){var $,M;await(($=h.afterCreate)==null?void 0:$.call(h,z,v)),await((M=p.afterCreate)==null?void 0:M.call(p,z,v))},async beforeDelete(z,v){var $,M;await(($=h.beforeDelete)==null?void 0:$.call(h,z,v)),await((M=p.beforeDelete)==null?void 0:M.call(p,z,v))}}}),{app:D}=T.init({dataAdapter:f,managementDataAdapter:m,...d,entityHooks:P,managementApiExtensions:[...u,{path:"/tenants",router:F}]});return D.use("/api/v2/*",te(r)),w&&D.use("/api/v2/*",x()),{app:D,controlPlaneTenantId:r}}function ye(e){const t=O(e);return{name:"multi-tenancy",middleware:H(e),hooks:t,routes:[{path:"/management",handler:q(e,t)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),e.accessControl&&console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(" - Database isolation enabled")}}}function O(e){const t=e.accessControl?J(e.accessControl):{},n=e.databaseIsolation?Y(e.databaseIsolation):{},r=N(e);return{...t,...n,tenants:r}}function se(e){const t=new ce.Hono,n=O(e);return t.route("/tenants",q(e,n)),t}function ve(e){return{hooks:O(e),middleware:H(e),app:se(e),config:e,wrapAdapters:(t,n)=>{var r;return B(t,{controlPlaneTenantId:(r=e.accessControl)==null?void 0:r.controlPlaneTenantId,controlPlaneClientId:n==null?void 0:n.controlPlaneClientId})}}}exports.createAccessControlHooks=J;exports.createAccessControlMiddleware=ne;exports.createControlPlaneTenantMiddleware=te;exports.createDatabaseHooks=Y;exports.createDatabaseMiddleware=ae;exports.createMultiTenancy=se;exports.createMultiTenancyHooks=O;exports.createMultiTenancyMiddleware=H;exports.createMultiTenancyPlugin=ye;exports.createProtectSyncedMiddleware=x;exports.createProvisioningHooks=N;exports.createRuntimeFallbackAdapter=ee;exports.createSubdomainMiddleware=re;exports.createSyncHooks=Z;exports.createTenantsOpenAPIRouter=q;exports.initMultiTenant=he;exports.setupMultiTenancy=ve;exports.validateTenantAccess=X;exports.withRuntimeFallback=B;
1
+ "use strict";var oe=Object.defineProperty;var ie=(e,t,n)=>t in e?oe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var k=(e,t,n)=>ie(e,typeof t!="symbol"?t+"":t,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ce=require("hono"),T=require("authhero"),_=require("@hono/zod-openapi");function J(e){const{controlPlaneTenantId:t,requireOrganizationMatch:n=!0}=e;return{async onTenantAccessValidation(r,a){if(a===t)return!0;if(n){const o=r.var.org_name,s=r.var.organization_id,i=o||s;return i?i.toLowerCase()===a.toLowerCase():!1}return!0}}}function X(e,t,n,r){if(t===n)return!0;const a=r||e;return a?a.toLowerCase()===t.toLowerCase():!1}function Y(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(n){console.error(`Failed to resolve data adapters for tenant ${t}:`,n);return}}}}function le(e){return`urn:authhero:tenant:${e.toLowerCase()}`}function N(e){return{async beforeCreate(t,n){return!n.audience&&n.id?{...n,audience:le(n.id)}:n},async afterCreate(t,n){const{accessControl:r,databaseIsolation:a}=e;r&&t.ctx&&await ue(t,n,r),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(t,n){const{accessControl:r,databaseIsolation:a}=e;if(r)try{const s=(await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(i=>i.name===n);s&&await t.adapters.organizations.remove(r.controlPlaneTenantId,s.id)}catch(o){console.warn(`Failed to remove organization for tenant ${n}:`,o)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(o){console.warn(`Failed to deprovision database for tenant ${n}:`,o)}}}}async function ue(e,t,n){const{controlPlaneTenantId:r,defaultPermissions:a,defaultRoles:o,issuer:s,adminRoleName:i="Tenant Admin",adminRoleDescription:u="Full access to all tenant management operations",addCreatorToOrganization:c=!0}=n,l=await e.adapters.organizations.create(r,{name:t.id,display_name:t.friendly_name||t.id});let g;if(s&&(g=await fe(e,r,i,u)),c&&e.ctx){const d=e.ctx.var.user;if(d!=null&&d.sub&&!await de(e,r,d.sub))try{await e.adapters.userOrganizations.create(r,{user_id:d.sub,organization_id:l.id}),g&&await e.adapters.userRoles.create(r,d.sub,g,l.id)}catch(m){console.warn(`Failed to add creator ${d.sub} to organization ${l.id}:`,m)}}o&&o.length>0&&console.log(`Would assign roles ${o.join(", ")} to organization ${l.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${l.id}`)}async function de(e,t,n){const r=await e.adapters.userRoles.list(t,n,void 0,"");for(const a of r)if((await e.adapters.rolePermissions.list(t,a.id,{per_page:1e3})).some(i=>i.permission_name==="admin:organizations"))return!0;return!1}async function fe(e,t,n,r){const o=(await e.adapters.roles.list(t,{})).roles.find(c=>c.name===n);if(o)return o.id;const s=await e.adapters.roles.create(t,{name:n,description:r}),i=T.MANAGEMENT_API_AUDIENCE,u=T.MANAGEMENT_API_SCOPES.map(c=>({role_id:s.id,resource_server_identifier:i,permission_name:c.value}));return await e.adapters.rolePermissions.assign(t,s.id,u),s.id}function U(e,t,n=()=>!0){const{controlPlaneTenantId:r,getChildTenantIds:a,getAdapters:o}=e,s=new Map;async function i(l,g,d){return(await t(l).list(g,{q:`name:${d}`,per_page:1}))[0]??null}async function u(l){const g=await a(),d=t(await o(r));await Promise.all(g.map(async f=>{try{const m=await o(f),w=t(m),y={...d.transform(l),is_system:!0},C=await i(m,f,l.name),A=C?w.getId(C):void 0;if(C&&A){const S=w.preserveOnUpdate?w.preserveOnUpdate(C,y):y;await w.update(f,A,S)}else await w.create(f,y)}catch(m){console.error(`Failed to sync ${d.listKey} "${l.name}" to tenant "${f}":`,m)}}))}async function c(l){const g=await a();await Promise.all(g.map(async d=>{try{const f=await o(d),m=t(f),w=await i(f,d,l),b=w?m.getId(w):void 0;w&&b&&await m.remove(d,b)}catch(f){console.error(`Failed to delete entity "${l}" from tenant "${d}":`,f)}}))}return{afterCreate:async(l,g)=>{l.tenantId===r&&n(g)&&await u(g)},afterUpdate:async(l,g,d)=>{l.tenantId===r&&n(d)&&await u(d)},beforeDelete:async(l,g)=>{if(l.tenantId!==r)return;const f=await t(l.adapters).get(l.tenantId,g);f&&n(f)&&s.set(g,f)},afterDelete:async(l,g)=>{if(l.tenantId!==r)return;const d=s.get(g);d&&(s.delete(g),await c(d.name))}}}function G(e,t,n=()=>!0){const{controlPlaneTenantId:r,getControlPlaneAdapters:a,getAdapters:o}=e;return{async afterCreate(s,i){if(i.id!==r)try{const u=await a(),c=await o(i.id),l=t(u),g=t(c),d=await T.fetchAll(f=>l.listPaginated(r,f),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(d.filter(f=>n(f)).map(async f=>{try{const m=l.transform(f);await g.create(i.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync entity to new tenant "${i.id}":`,m)}}))}catch(u){console.error(`Failed to sync entities to new tenant "${i.id}":`,u)}}}}const L=e=>({list:async(t,n)=>(await e.resourceServers.list(t,n)).resource_servers,listPaginated:(t,n)=>e.resourceServers.list(t,n),get:(t,n)=>e.resourceServers.get(t,n),create:(t,n)=>e.resourceServers.create(t,n),update:(t,n,r)=>e.resourceServers.update(t,n,r),remove:(t,n)=>e.resourceServers.remove(t,n),listKey:"resource_servers",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,identifier:t.identifier,scopes:t.scopes,signing_alg:t.signing_alg,token_lifetime:t.token_lifetime,token_lifetime_for_web:t.token_lifetime_for_web})}),W=e=>({list:async(t,n)=>(await e.roles.list(t,n)).roles,listPaginated:(t,n)=>e.roles.list(t,n),get:(t,n)=>e.roles.get(t,n),create:(t,n)=>e.roles.create(t,n),update:(t,n,r)=>e.roles.update(t,n,r),remove:(t,n)=>e.roles.remove(t,n),listKey:"roles",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,description:t.description})});function K(e){var t;return((t=e.metadata)==null?void 0:t.sync)!==!1}function Z(e){const{sync:t={},filters:n={}}=e,r=t.resourceServers??!0,a=t.roles??!0,o=m=>K(m)?n.resourceServers?n.resourceServers(m):!0:!1,s=m=>K(m)?n.roles?n.roles(m):!0:!1,i=r?U(e,L,o):void 0,u=a?U(e,W,s):void 0,c=r?G(e,L,o):void 0,l=a?G(e,W,s):void 0,g=a?{async afterCreate(m,w){var b;if(w.id!==e.controlPlaneTenantId){await((b=l==null?void 0:l.afterCreate)==null?void 0:b.call(l,m,w));try{const y=await e.getControlPlaneAdapters(),C=await e.getAdapters(w.id),A=await T.fetchAll(p=>y.roles.list(e.controlPlaneTenantId,p),"roles",{cursorField:"id",pageSize:100}),S=new Map;for(const p of A.filter(P=>{var h;return((h=n.roles)==null?void 0:h.call(n,P))??!0})){const P=await d(C,w.id,p.name);P&&S.set(p.name,P.id)}for(const p of A.filter(P=>{var h;return((h=n.roles)==null?void 0:h.call(n,P))??!0})){const P=S.get(p.name);if(P)try{const h=await y.rolePermissions.list(e.controlPlaneTenantId,p.id,{});h.length>0&&await C.rolePermissions.assign(w.id,P,h.map(R=>({role_id:P,resource_server_identifier:R.resource_server_identifier,permission_name:R.permission_name})))}catch(h){console.error(`Failed to sync permissions for role "${p.name}" to tenant "${w.id}":`,h)}}}catch(y){console.error(`Failed to sync role permissions to tenant "${w.id}":`,y)}}}}:void 0;async function d(m,w,b){return(await m.roles.list(w,{q:`name:${b}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:i,roles:u},tenantHooks:{async afterCreate(m,w){const b=[c==null?void 0:c.afterCreate,(g==null?void 0:g.afterCreate)??(l==null?void 0:l.afterCreate)],y=[];for(const C of b)if(C)try{await C(m,w)}catch(A){y.push(A instanceof Error?A:new Error(String(A)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,y.map(C=>C.message).join("; "))}}}}var I=class extends Error{constructor(t=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});k(this,"res");k(this,"status");this.res=n==null?void 0:n.res,this.status=t}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function j(e,t){const n=new _.OpenAPIHono;return n.openapi(_.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:T.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:_.z.object({tenants:_.z.array(T.tenantSchema),start:_.z.number().optional(),limit:_.z.number().optional(),length:_.z.number().optional()})}},description:"List of tenants"}}}),async r=>{var m,w,b,y,C,A;const a=r.req.valid("query"),{page:o,per_page:s,include_totals:i,q:u}=a,c=r.var.user,l=(c==null?void 0:c.permissions)||[];if(l.includes("auth:read")||l.includes("admin:organizations")){const S=await r.env.data.tenants.list({page:o,per_page:s,include_totals:i,q:u});return i?r.json({tenants:S.tenants,start:((m=S.totals)==null?void 0:m.start)??0,limit:((w=S.totals)==null?void 0:w.limit)??s,length:S.tenants.length}):r.json({tenants:S.tenants})}const d=((b=e.accessControl)==null?void 0:b.controlPlaneTenantId)??((y=r.env.data.multiTenancyConfig)==null?void 0:y.controlPlaneTenantId);if(d&&(c!=null&&c.sub)){const p=(await T.fetchAll(M=>r.env.data.userOrganizations.listUserOrganizations(d,c.sub,M),"organizations")).map(M=>M.name);if(p.length===0)return i?r.json({tenants:[],start:0,limit:s??50,length:0}):r.json({tenants:[]});const P=p.length,h=o??0,R=s??50,F=h*R,q=p.slice(F,F+R);if(q.length===0)return i?r.json({tenants:[],start:F,limit:R,length:P}):r.json({tenants:[]});const z=q.map(M=>`id:${M}`).join(" OR "),v=u?`(${z}) AND (${u})`:z,$=await r.env.data.tenants.list({q:v,per_page:R,include_totals:!1});return i?r.json({tenants:$.tenants,start:F,limit:R,length:P}):r.json({tenants:$.tenants})}const f=await r.env.data.tenants.list({page:o,per_page:s,include_totals:i,q:u});return i?r.json({tenants:f.tenants,start:((C=f.totals)==null?void 0:C.start)??0,limit:((A=f.totals)==null?void 0:A.limit)??s,length:f.tenants.length}):r.json({tenants:f.tenants})}),n.openapi(_.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:T.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:T.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async r=>{var u,c;const a=r.var.user;if(!(a!=null&&a.sub))throw new I(401,{message:"Authentication required to create tenants"});let o=r.req.valid("json");const s={adapters:r.env.data,ctx:r};(u=t.tenants)!=null&&u.beforeCreate&&(o=await t.tenants.beforeCreate(s,o));const i=await r.env.data.tenants.create(o);return(c=t.tenants)!=null&&c.afterCreate&&await t.tenants.afterCreate(s,i),r.json(i,201)}),n.openapi(_.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:_.z.object({id:_.z.string()})},security:[{Bearer:["delete:tenants"]}],responses:{204:{description:"Tenant deleted"},403:{description:"Access denied or cannot delete the control plane"},404:{description:"Tenant not found"}}}),async r=>{var u,c,l,g;const{id:a}=r.req.valid("param"),o=((u=e.accessControl)==null?void 0:u.controlPlaneTenantId)??((c=r.env.data.multiTenancyConfig)==null?void 0:c.controlPlaneTenantId);if(o){const d=r.var.user;if(!(d!=null&&d.sub))throw new I(401,{message:"Authentication required"});if(a===o)throw new I(403,{message:"Cannot delete the control plane"});if(!(await T.fetchAll(w=>r.env.data.userOrganizations.listUserOrganizations(o,d.sub,w),"organizations")).some(w=>w.name===a))throw new I(403,{message:"Access denied to this tenant"})}if(!await r.env.data.tenants.get(a))throw new I(404,{message:"Tenant not found"});const i={adapters:r.env.data,ctx:r};return(l=t.tenants)!=null&&l.beforeDelete&&await t.tenants.beforeDelete(i,a),await r.env.data.tenants.remove(a),(g=t.tenants)!=null&&g.afterDelete&&await t.tenants.afterDelete(i,a),r.body(null,204)}),n.openapi(_.createRoute({tags:["tenants","settings"],method:"get",path:"/settings",request:{headers:_.z.object({"tenant-id":_.z.string().optional()})},security:[{Bearer:["read:tenants","auth:read"]}],responses:{200:{content:{"application/json":{schema:T.tenantSchema}},description:"Current tenant settings"}}}),async r=>{const a=await r.env.data.tenants.get(r.var.tenant_id);if(!a)throw new I(404,{message:"Tenant not found"});return r.json(a)}),n.openapi(_.createRoute({tags:["tenants","settings"],method:"patch",path:"/settings",request:{headers:_.z.object({"tenant-id":_.z.string().optional()}),body:{content:{"application/json":{schema:_.z.object(T.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["update:tenants","auth:write"]}],responses:{200:{content:{"application/json":{schema:T.tenantSchema}},description:"Updated tenant settings"}}}),async r=>{const a=r.req.valid("json"),{id:o,...s}=a,i=await r.env.data.tenants.get(r.var.tenant_id);if(!i)throw new I(404,{message:"Tenant not found"});const u=T.deepMergePatch(i,s);await r.env.data.tenants.update(r.var.tenant_id,u);const c=await r.env.data.tenants.get(r.var.tenant_id);if(!c)throw new I(500,{message:"Failed to retrieve updated tenant"});return r.json(c)}),n}function me(e){const t=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:n,type:r}of t){const a=e.match(n);if(a&&a[1])return{type:r,id:a[1]}}return null}async function ge(e,t,n){try{switch(n.type){case"resource_server":{const r=await e.resourceServers.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"role":{const r=await e.roles.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"connection":{const r=await e.connections.get(t,n.id);return(r==null?void 0:r.is_system)===!0}default:return!1}}catch{return!1}}function we(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function x(){return async(e,t)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return t();const n=me(e.req.path);if(!n)return t();const r=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!r)return t();if(await ge(e.env.data,r,n))throw new I(403,{message:`This ${we(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function E(e,t){const n=t.find(a=>a.strategy===e.strategy);if(!(n!=null&&n.options))return e;const r=T.connectionSchema.passthrough().parse({...n,...e});return r.options=T.connectionOptionsSchema.passthrough().parse({...n.options||{},...e.options}),r}function D(e,t){const n=[...t||[],...e||[]];return[...new Set(n)]}function pe(e,t){if(!(t!=null&&t.length))return e||[];if(!(e!=null&&e.length))return t;const n=new Map;for(const r of t)n.set(r.value,r);for(const r of e)n.set(r.value,r);return Array.from(n.values())}function Q(e,t){return t?{...e,scopes:pe(e.scopes,t.scopes)}:e}function V(e,t){return t?{...e,callbacks:D(e.callbacks,t.callbacks),web_origins:D(e.web_origins,t.web_origins),allowed_logout_urls:D(e.allowed_logout_urls,t.allowed_logout_urls),allowed_origins:D(e.allowed_origins,t.allowed_origins)}:e}function ee(e,t){const{controlPlaneTenantId:n,controlPlaneClientId:r}=t;return{...e,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r},connections:{...e.connections,get:async(a,o)=>{const s=await e.connections.get(a,o);if(!s||!n||a===n)return s;const i=await e.connections.list(n);return E(s,i.connections||[])},list:async(a,o)=>{const s=await e.connections.list(a,o);if(!n||a===n)return s;const i=await e.connections.list(n),u=s.connections.map(c=>E(c,i.connections||[]));return{...s,connections:u}}},clientConnections:{...e.clientConnections,listByClient:async(a,o)=>{let s=await e.clientConnections.listByClient(a,o);if(s.length===0&&(s=(await e.connections.list(a)).connections||[]),!n||a===n)return s;const i=await e.connections.list(n);return s.map(u=>E(u,i.connections||[]))}},clients:{...e.clients,get:async(a,o)=>{const s=await e.clients.get(a,o);if(!s)return null;if(!n||!r||a===n&&o===r)return s;const i=await e.clients.get(n,r);return V(s,i)},getByClientId:async a=>{const o=await e.clients.getByClientId(a);if(!o)return null;if(!n||!r||o.tenant_id===n&&o.client_id===r)return o;const s=await e.clients.get(n,r);return{...V(o,s),tenant_id:o.tenant_id}}},emailProviders:{...e.emailProviders,get:async a=>{const o=await e.emailProviders.get(a);return o||(!n||a===n?null:e.emailProviders.get(n))}},resourceServers:{...e.resourceServers,get:async(a,o)=>{const s=await e.resourceServers.get(a,o);if(!s||!n||a===n)return s;const u=(await e.resourceServers.list(n,{q:`identifier:${s.identifier}`,per_page:1})).resource_servers[0]??null;return Q(s,u)},list:async(a,o)=>{const s=await e.resourceServers.list(a,o);if(!n||a===n)return s;const i=await e.resourceServers.list(n),u=new Map(i.resource_servers.map(l=>[l.identifier,l])),c=s.resource_servers.map(l=>Q(l,u.get(l.identifier)??null));return{...s,resource_servers:c}}}}}function B(e,t){return ee(e,t)}function te(e){return async(t,n)=>{const r=t.var.user;return(r==null?void 0:r.tenant_id)===e&&r.org_name&&t.set("tenant_id",r.org_name),n()}}function ne(e){return async(t,n)=>{if(!e.accessControl)return n();const{controlPlaneTenantId:r}=e.accessControl,a=t.var.org_name,o=t.var.organization_id,s=a||o;let i=t.var.tenant_id;const u=t.var.user,l=(u!=null&&u.aud?Array.isArray(u.aud)?u.aud:[u.aud]:[]).includes(T.MANAGEMENT_API_AUDIENCE);if(!i&&s&&l&&(t.set("tenant_id",s),i=s),!i)throw new I(400,{message:"Tenant ID not found in request"});if(!X(o,i,r,a))throw new I(403,{message:`Access denied to tenant ${i}`});return n()}}function re(e){return async(t,n)=>{if(!e.subdomainRouting)return n();const{baseDomain:r,reservedSubdomains:a=[],resolveSubdomain:o}=e.subdomainRouting,s=t.req.header("x-forwarded-host")||t.req.header("host")||"";let i=null;if(s.endsWith(r)){const c=s.slice(0,-(r.length+1));c&&!c.includes(".")&&(i=c)}if(i&&a.includes(i)&&(i=null),!i)return e.accessControl&&t.set("tenant_id",e.accessControl.controlPlaneTenantId),n();let u=null;if(o)u=await o(i);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const c=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,i);c&&(u=c.id)}catch{}if(!u)throw new I(404,{message:`Tenant not found for subdomain: ${i}`});return t.set("tenant_id",u),n()}}function ae(e){return async(t,n)=>{if(!e.databaseIsolation)return n();const r=t.var.tenant_id;if(!r)throw new I(400,{message:"Tenant ID not found in request"});try{const a=await e.databaseIsolation.getAdapters(r);t.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${r}:`,a),new I(500,{message:"Failed to resolve tenant database"})}return n()}}function H(e){const t=re(e),n=ne(e),r=ae(e);return async(a,o)=>(await t(a,async()=>{}),await n(a,async()=>{}),await r(a,async()=>{}),o())}function he(e){const{dataAdapter:t,controlPlane:n,controlPlane:{tenantId:r="control_plane",clientId:a}={},sync:o={resourceServers:!0,roles:!0},defaultPermissions:s=["tenant:admin"],requireOrganizationMatch:i=!1,managementApiExtensions:u=[],entityHooks:c,getChildTenantIds:l,getAdapters:g,...d}=e;let f=t,m=t;n&&(f=B(t,{controlPlaneTenantId:r,controlPlaneClientId:a}),m={...t,multiTenancyConfig:{controlPlaneTenantId:r,controlPlaneClientId:a}});const w=o!==!1,b=w?{resourceServers:o.resourceServers??!0,roles:o.roles??!0}:{resourceServers:!1,roles:!1},A={controlPlaneTenantId:r,getChildTenantIds:l??(async()=>(await T.fetchAll(v=>f.tenants.list(v),"tenants",{cursorField:"id",pageSize:100})).filter(v=>v.id!==r).map(v=>v.id)),getAdapters:g??(async()=>f),getControlPlaneAdapters:async()=>f,sync:b},{entityHooks:S,tenantHooks:p}=Z(A),P={resourceServers:[S.resourceServers,...(c==null?void 0:c.resourceServers)??[]],roles:[S.roles,...(c==null?void 0:c.roles)??[]],connections:(c==null?void 0:c.connections)??[],tenants:(c==null?void 0:c.tenants)??[],rolePermissions:(c==null?void 0:c.rolePermissions)??[]},h=N({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:i,defaultPermissions:s}}),F=j({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:i,defaultPermissions:s}},{tenants:{async beforeCreate(z,v){return h.beforeCreate&&(v=await h.beforeCreate(z,v)),p.beforeCreate&&(v=await p.beforeCreate(z,v)),v},async afterCreate(z,v){var $,M;await(($=h.afterCreate)==null?void 0:$.call(h,z,v)),await((M=p.afterCreate)==null?void 0:M.call(p,z,v))},async beforeDelete(z,v){var $,M;await(($=h.beforeDelete)==null?void 0:$.call(h,z,v)),await((M=p.beforeDelete)==null?void 0:M.call(p,z,v))}}}),{app:q}=T.init({dataAdapter:f,managementDataAdapter:m,...d,entityHooks:P,managementApiExtensions:[...u,{path:"/tenants",router:F}]});return q.use("/api/v2/*",te(r)),w&&q.use("/api/v2/*",x()),{app:q,controlPlaneTenantId:r}}function ye(e){const t=O(e);return{name:"multi-tenancy",middleware:H(e),hooks:t,routes:[{path:"/management",handler:j(e,t)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),e.accessControl&&console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(" - Database isolation enabled")}}}function O(e){const t=e.accessControl?J(e.accessControl):{},n=e.databaseIsolation?Y(e.databaseIsolation):{},r=N(e);return{...t,...n,tenants:r}}function se(e){const t=new ce.Hono,n=O(e);return t.route("/tenants",j(e,n)),t}function ve(e){return{hooks:O(e),middleware:H(e),app:se(e),config:e,wrapAdapters:(t,n)=>{var r;return B(t,{controlPlaneTenantId:(r=e.accessControl)==null?void 0:r.controlPlaneTenantId,controlPlaneClientId:n==null?void 0:n.controlPlaneClientId})}}}exports.createAccessControlHooks=J;exports.createAccessControlMiddleware=ne;exports.createControlPlaneTenantMiddleware=te;exports.createDatabaseHooks=Y;exports.createDatabaseMiddleware=ae;exports.createMultiTenancy=se;exports.createMultiTenancyHooks=O;exports.createMultiTenancyMiddleware=H;exports.createMultiTenancyPlugin=ye;exports.createProtectSyncedMiddleware=x;exports.createProvisioningHooks=N;exports.createRuntimeFallbackAdapter=ee;exports.createSubdomainMiddleware=re;exports.createSyncHooks=Z;exports.createTenantsOpenAPIRouter=j;exports.initMultiTenant=he;exports.setupMultiTenancy=ve;exports.validateTenantAccess=X;exports.withRuntimeFallback=B;
@@ -2,7 +2,7 @@ var x = Object.defineProperty;
2
2
  var ee = (e, t, n) => t in e ? x(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
3
3
  var E = (e, t, n) => ee(e, typeof t != "symbol" ? t + "" : t, n);
4
4
  import { Hono as te } from "hono";
5
- import { MANAGEMENT_API_SCOPES as ne, MANAGEMENT_API_AUDIENCE as J, fetchAll as D, auth0QuerySchema as re, tenantSchema as q, tenantInsertSchema as U, deepMergePatch as ae, connectionSchema as se, connectionOptionsSchema as oe, init as ie } from "authhero";
5
+ import { MANAGEMENT_API_SCOPES as ne, MANAGEMENT_API_AUDIENCE as J, fetchAll as q, auth0QuerySchema as re, tenantSchema as D, tenantInsertSchema as U, deepMergePatch as ae, connectionSchema as se, connectionOptionsSchema as oe, init as ie } from "authhero";
6
6
  import { OpenAPIHono as ce, createRoute as M, z as S } from "@hono/zod-openapi";
7
7
  function le(e) {
8
8
  const { controlPlaneTenantId: t, requireOrganizationMatch: n = !0 } = e;
@@ -245,7 +245,7 @@ function L(e, t, n = () => !0) {
245
245
  async afterCreate(s, i) {
246
246
  if (i.id !== r)
247
247
  try {
248
- const u = await a(), c = await o(i.id), l = t(u), g = t(c), d = await D(
248
+ const u = await a(), c = await o(i.id), l = t(u), g = t(c), d = await q(
249
249
  (f) => l.listPaginated(r, f),
250
250
  l.listKey,
251
251
  { cursorField: "id", pageSize: 100 }
@@ -331,7 +331,7 @@ function pe(e) {
331
331
  if (w.id !== e.controlPlaneTenantId) {
332
332
  await ((C = l == null ? void 0 : l.afterCreate) == null ? void 0 : C.call(l, m, w));
333
333
  try {
334
- const y = await e.getControlPlaneAdapters(), _ = await e.getAdapters(w.id), b = await D(
334
+ const y = await e.getControlPlaneAdapters(), _ = await e.getAdapters(w.id), b = await q(
335
335
  (p) => y.roles.list(
336
336
  e.controlPlaneTenantId,
337
337
  p
@@ -471,7 +471,7 @@ function N(e, t) {
471
471
  content: {
472
472
  "application/json": {
473
473
  schema: S.object({
474
- tenants: S.array(q),
474
+ tenants: S.array(D),
475
475
  start: S.number().optional(),
476
476
  limit: S.number().optional(),
477
477
  length: S.number().optional()
@@ -501,7 +501,7 @@ function N(e, t) {
501
501
  }
502
502
  const d = ((C = e.accessControl) == null ? void 0 : C.controlPlaneTenantId) ?? ((y = r.env.data.multiTenancyConfig) == null ? void 0 : y.controlPlaneTenantId);
503
503
  if (d && (c != null && c.sub)) {
504
- const p = (await D(
504
+ const p = (await q(
505
505
  (R) => r.env.data.userOrganizations.listUserOrganizations(
506
506
  d,
507
507
  c.sub,
@@ -573,7 +573,7 @@ function N(e, t) {
573
573
  201: {
574
574
  content: {
575
575
  "application/json": {
576
- schema: q
576
+ schema: D
577
577
  }
578
578
  },
579
579
  description: "Tenant created"
@@ -642,7 +642,7 @@ function N(e, t) {
642
642
  throw new A(403, {
643
643
  message: "Cannot delete the control plane"
644
644
  });
645
- if (!(await D(
645
+ if (!(await q(
646
646
  (w) => r.env.data.userOrganizations.listUserOrganizations(
647
647
  o,
648
648
  d.sub,
@@ -683,7 +683,7 @@ function N(e, t) {
683
683
  200: {
684
684
  content: {
685
685
  "application/json": {
686
- schema: q
686
+ schema: D
687
687
  }
688
688
  },
689
689
  description: "Current tenant settings"
@@ -724,7 +724,7 @@ function N(e, t) {
724
724
  200: {
725
725
  content: {
726
726
  "application/json": {
727
- schema: q
727
+ schema: D
728
728
  }
729
729
  },
730
730
  description: "Updated tenant settings"
@@ -1047,7 +1047,7 @@ function Ae(e) {
1047
1047
  baseDomain: r,
1048
1048
  reservedSubdomains: a = [],
1049
1049
  resolveSubdomain: o
1050
- } = e.subdomainRouting, s = t.req.header("host") || "";
1050
+ } = e.subdomainRouting, s = t.req.header("x-forwarded-host") || t.req.header("host") || "";
1051
1051
  let i = null;
1052
1052
  if (s.endsWith(r)) {
1053
1053
  const c = s.slice(0, -(r.length + 1));
@@ -1137,7 +1137,7 @@ function je(e) {
1137
1137
  roles: o.roles ?? !0
1138
1138
  } : { resourceServers: !1, roles: !1 }, b = {
1139
1139
  controlPlaneTenantId: r,
1140
- getChildTenantIds: l ?? (async () => (await D(
1140
+ getChildTenantIds: l ?? (async () => (await q(
1141
1141
  (v) => f.tenants.list(v),
1142
1142
  "tenants",
1143
1143
  { cursorField: "id", pageSize: 100 }
@@ -1233,7 +1233,7 @@ function Se(e) {
1233
1233
  const t = new te(), n = B(e);
1234
1234
  return t.route("/tenants", N(e, n)), t;
1235
1235
  }
1236
- function De(e) {
1236
+ function qe(e) {
1237
1237
  return {
1238
1238
  hooks: B(e),
1239
1239
  middleware: Z(e),
@@ -1273,7 +1273,7 @@ export {
1273
1273
  pe as createSyncHooks,
1274
1274
  N as createTenantsOpenAPIRouter,
1275
1275
  je as initMultiTenant,
1276
- De as setupMultiTenancy,
1276
+ qe as setupMultiTenancy,
1277
1277
  ue as validateTenantAccess,
1278
1278
  Y as withRuntimeFallback
1279
1279
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGzC,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kCAAkC,CAChD,oBAAoB,EAAE,MAAM,GAC3B,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAUD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAsDD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAqED;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CA6BD;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAiBD;AAGD,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGzC,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kCAAkC,CAChD,oBAAoB,EAAE,MAAM,GAC3B,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAUD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAsDD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAsED;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CA6BD;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kBAAkB,GACzB,iBAAiB,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,qBAAqB,CAAC;CAClC,CAAC,CAiBD;AAGD,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAGjE,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/markusahlstrand/authhero"
13
13
  },
14
- "version": "14.16.0",
14
+ "version": "14.17.0",
15
15
  "description": "Multi-tenancy support for AuthHero with organization-based access control and per-tenant database isolation",
16
16
  "files": [
17
17
  "dist"
@@ -36,9 +36,9 @@
36
36
  "typescript": "^5.6.0",
37
37
  "vite": "^6.0.0",
38
38
  "vitest": "^2.1.0",
39
- "@authhero/kysely-adapter": "10.112.0",
40
- "@authhero/adapter-interfaces": "0.149.0",
41
- "authhero": "4.76.0"
39
+ "@authhero/kysely-adapter": "10.114.0",
40
+ "authhero": "4.80.0",
41
+ "@authhero/adapter-interfaces": "0.151.0"
42
42
  },
43
43
  "dependencies": {
44
44
  "zod": "^3.24.0"