@authhero/multi-tenancy 14.1.0 → 14.2.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 x=Object.defineProperty;var ee=(t,e,n)=>e in t?x(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var $=(t,e,n)=>ee(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const te=require("hono"),I=require("authhero"),_=require("@hono/zod-openapi"),S=require("@authhero/adapter-interfaces");function G(t){const{controlPlaneTenantId:e,requireOrganizationMatch:n=!0}=t;return{async onTenantAccessValidation(a,r){if(r===e)return!0;if(n){const i=a.var.org_name,o=a.var.organization_id,s=i||o;return s?s.toLowerCase()===r.toLowerCase():!1}return!0}}}function U(t,e,n,a){if(e===n)return!0;const r=a||t;return r?r.toLowerCase()===e.toLowerCase():!1}function L(t){return{async resolveDataAdapters(e){try{return await t.getAdapters(e)}catch(n){console.error(`Failed to resolve data adapters for tenant ${e}:`,n);return}}}}function ne(t){return`urn:authhero:tenant:${t.toLowerCase()}`}function B(t){return{async beforeCreate(e,n){return!n.audience&&n.id?{...n,audience:ne(n.id)}:n},async afterCreate(e,n){const{accessControl:a,databaseIsolation:r}=t;a&&e.ctx&&await ae(e,n,a),r!=null&&r.onProvision&&await r.onProvision(n.id)},async beforeDelete(e,n){const{accessControl:a,databaseIsolation:r}=t;if(a)try{const o=(await e.adapters.organizations.list(a.controlPlaneTenantId)).organizations.find(s=>s.name===n);o&&await e.adapters.organizations.remove(a.controlPlaneTenantId,o.id)}catch(i){console.warn(`Failed to remove organization for tenant ${n}:`,i)}if(r!=null&&r.onDeprovision)try{await r.onDeprovision(n)}catch(i){console.warn(`Failed to deprovision database for tenant ${n}:`,i)}}}}async function ae(t,e,n){const{controlPlaneTenantId:a,defaultPermissions:r,defaultRoles:i,issuer:o,adminRoleName:s="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:m=!0}=n,l=await t.adapters.organizations.create(a,{name:e.id,display_name:e.friendly_name||e.id});let f;if(o&&(f=await re(t,a,s,c)),m&&t.ctx){const d=t.ctx.var.user;if(d!=null&&d.sub&&!await se(t,a,d.sub))try{await t.adapters.userOrganizations.create(a,{user_id:d.sub,organization_id:l.id}),f&&await t.adapters.userRoles.create(a,d.sub,f,l.id)}catch(p){console.warn(`Failed to add creator ${d.sub} to organization ${l.id}:`,p)}}i&&i.length>0&&console.log(`Would assign roles ${i.join(", ")} to organization ${l.id}`),r&&r.length>0&&console.log(`Would grant permissions ${r.join(", ")} to organization ${l.id}`)}async function se(t,e,n){const a=await t.adapters.userRoles.list(e,n,void 0,"");for(const r of a)if((await t.adapters.rolePermissions.list(e,r.id,{per_page:1e3})).some(s=>s.permission_name==="admin:organizations"))return!0;return!1}async function re(t,e,n,a){const i=(await t.adapters.roles.list(e,{})).roles.find(m=>m.name===n);if(i)return i.id;const o=await t.adapters.roles.create(e,{name:n,description:a}),s=I.MANAGEMENT_API_AUDIENCE,c=I.MANAGEMENT_API_SCOPES.map(m=>({role_id:o.id,resource_server_identifier:s,permission_name:m.value}));return await t.adapters.rolePermissions.assign(e,o.id,c),o.id}function j(t,e,n=()=>!0){const{controlPlaneTenantId:a,getChildTenantIds:r,getAdapters:i}=t,o=new Map;async function s(l,f,d){return(await e(l).list(f,{q:`name:${d}`,per_page:1}))[0]??null}async function c(l){const f=await r(),d=e(await i(a));await Promise.all(f.map(async u=>{try{const p=await i(u),g=e(p),w={...d.transform(l),is_system:!0},C=await s(p,u,l.name),v=C?g.getId(C):void 0;if(C&&v){const y=g.preserveOnUpdate?g.preserveOnUpdate(C,w):w;await g.update(u,v,y)}else await g.create(u,w)}catch(p){console.error(`Failed to sync ${d.listKey} "${l.name}" to tenant "${u}":`,p)}}))}async function m(l){const f=await r();await Promise.all(f.map(async d=>{try{const u=await i(d),p=e(u),g=await s(u,d,l),h=g?p.getId(g):void 0;g&&h&&await p.remove(d,h)}catch(u){console.error(`Failed to delete entity "${l}" from tenant "${d}":`,u)}}))}return{afterCreate:async(l,f)=>{l.tenantId===a&&n(f)&&await c(f)},afterUpdate:async(l,f,d)=>{l.tenantId===a&&n(d)&&await c(d)},beforeDelete:async(l,f)=>{if(l.tenantId!==a)return;const u=await e(l.adapters).get(l.tenantId,f);u&&n(u)&&o.set(f,u)},afterDelete:async(l,f)=>{if(l.tenantId!==a)return;const d=o.get(f);d&&(o.delete(f),await m(d.name))}}}function E(t,e,n=()=>!0){const{controlPlaneTenantId:a,getControlPlaneAdapters:r,getAdapters:i}=t;return{async afterCreate(o,s){if(s.id!==a)try{const c=await r(),m=await i(s.id),l=e(c),f=e(m),d=await I.fetchAll(u=>l.listPaginated(a,u),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(d.filter(u=>n(u)).map(async u=>{try{const p=l.transform(u);await f.create(s.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync entity to new tenant "${s.id}":`,p)}}))}catch(c){console.error(`Failed to sync entities to new tenant "${s.id}":`,c)}}}}const N=t=>({list:async(e,n)=>(await t.resourceServers.list(e,n)).resource_servers,listPaginated:(e,n)=>t.resourceServers.list(e,n),get:(e,n)=>t.resourceServers.get(e,n),create:(e,n)=>t.resourceServers.create(e,n),update:(e,n,a)=>t.resourceServers.update(e,n,a),remove:(e,n)=>t.resourceServers.remove(e,n),listKey:"resource_servers",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,identifier:e.identifier,scopes:e.scopes,signing_alg:e.signing_alg,token_lifetime:e.token_lifetime,token_lifetime_for_web:e.token_lifetime_for_web})}),H=t=>({list:async(e,n)=>(await t.roles.list(e,n)).roles,listPaginated:(e,n)=>t.roles.list(e,n),get:(e,n)=>t.roles.get(e,n),create:(e,n)=>t.roles.create(e,n),update:(e,n,a)=>t.roles.update(e,n,a),remove:(e,n)=>t.roles.remove(e,n),listKey:"roles",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})});function K(t){const{sync:e={},filters:n={}}=t,a=e.resourceServers??!0,r=e.roles??!0,i=a?j(t,N,n.resourceServers):void 0,o=r?j(t,H,n.roles):void 0,s=a?E(t,N,n.resourceServers):void 0,c=r?E(t,H,n.roles):void 0,m=r?{async afterCreate(d,u){var p;if(u.id!==t.controlPlaneTenantId){await((p=c==null?void 0:c.afterCreate)==null?void 0:p.call(c,d,u));try{const g=await t.getControlPlaneAdapters(),h=await t.getAdapters(u.id),w=await I.fetchAll(v=>g.roles.list(t.controlPlaneTenantId,v),"roles",{cursorField:"id",pageSize:100}),C=new Map;for(const v of w.filter(y=>{var T;return((T=n.roles)==null?void 0:T.call(n,y))??!0})){const y=await l(h,u.id,v.name);y&&C.set(v.name,y.id)}for(const v of w.filter(y=>{var T;return((T=n.roles)==null?void 0:T.call(n,y))??!0})){const y=C.get(v.name);if(y)try{const T=await g.rolePermissions.list(t.controlPlaneTenantId,v.id,{});T.length>0&&await h.rolePermissions.assign(u.id,y,T.map(A=>({role_id:y,resource_server_identifier:A.resource_server_identifier,permission_name:A.permission_name})))}catch(T){console.error(`Failed to sync permissions for role "${v.name}" to tenant "${u.id}":`,T)}}}catch(g){console.error(`Failed to sync role permissions to tenant "${u.id}":`,g)}}}}:void 0;async function l(d,u,p){return(await d.roles.list(u,{q:`name:${p}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:i,roles:o},tenantHooks:{async afterCreate(d,u){const p=[s==null?void 0:s.afterCreate,(m==null?void 0:m.afterCreate)??(c==null?void 0:c.afterCreate)],g=[];for(const h of p)if(h)try{await h(d,u)}catch(w){g.push(w instanceof Error?w:new Error(String(w)))}if(g.length===1)throw g[0];if(g.length>1)throw new AggregateError(g,g.map(h=>h.message).join("; "))}}}}var b=class extends Error{constructor(e=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});$(this,"res");$(this,"status");this.res=n==null?void 0:n.res,this.status=e}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function M(t,e){const n=new _.OpenAPIHono;return n.openapi(_.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:S.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:_.z.object({tenants:_.z.array(S.tenantSchema),start:_.z.number().optional(),limit:_.z.number().optional(),length:_.z.number().optional()})}},description:"List of tenants"}}}),async a=>{var u,p,g,h;const r=a.req.valid("query"),{page:i,per_page:o,include_totals:s,q:c}=r,m=a.var.user,l=(m==null?void 0:m.permissions)||[];if(l.includes("auth:read")||l.includes("admin:organizations")){const w=await a.env.data.tenants.list({page:i,per_page:o,include_totals:s,q:c});return s?a.json({tenants:w.tenants,start:((u=w.totals)==null?void 0:u.start)??0,limit:((p=w.totals)==null?void 0:p.limit)??o,length:w.tenants.length}):a.json({tenants:w.tenants})}if(t.accessControl&&(m!=null&&m.sub)){const w=t.accessControl.controlPlaneTenantId,v=(await I.fetchAll(P=>a.env.data.userOrganizations.listUserOrganizations(w,m.sub,P),"organizations")).map(P=>P.name);if(v.length===0)return s?a.json({tenants:[],start:0,limit:o??50,length:0}):a.json({tenants:[]});const y=v.length,T=i??0,A=o??50,z=T*A,D=v.slice(z,z+A);if(D.length===0)return s?a.json({tenants:[],start:z,limit:A,length:y}):a.json({tenants:[]});const F=D.map(P=>`id:${P}`).join(" OR "),Z=c?`(${F}) AND (${c})`:F,k=await a.env.data.tenants.list({q:Z,per_page:A,include_totals:!1});return s?a.json({tenants:k.tenants,start:z,limit:A,length:y}):a.json({tenants:k.tenants})}const d=await a.env.data.tenants.list({page:i,per_page:o,include_totals:s,q:c});return s?a.json({tenants:d.tenants,start:((g=d.totals)==null?void 0:g.start)??0,limit:((h=d.totals)==null?void 0:h.limit)??o,length:d.tenants.length}):a.json({tenants:d.tenants})}),n.openapi(_.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:S.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:S.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async a=>{var c,m;const r=a.var.user;if(!(r!=null&&r.sub))throw new b(401,{message:"Authentication required to create tenants"});let i=a.req.valid("json");const o={adapters:a.env.data,ctx:a};(c=e.tenants)!=null&&c.beforeCreate&&(i=await e.tenants.beforeCreate(o,i));const s=await a.env.data.tenants.create(i);return(m=e.tenants)!=null&&m.afterCreate&&await e.tenants.afterCreate(o,s),a.json(s,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 a=>{var s,c;const{id:r}=a.req.valid("param");if(t.accessControl){const m=a.var.user,l=t.accessControl.controlPlaneTenantId;if(!(m!=null&&m.sub))throw new b(401,{message:"Authentication required"});if(r===l)throw new b(403,{message:"Cannot delete the control plane"});if(!(await I.fetchAll(u=>a.env.data.userOrganizations.listUserOrganizations(l,m.sub,u),"organizations")).some(u=>u.name===r))throw new b(403,{message:"Access denied to this tenant"})}if(!await a.env.data.tenants.get(r))throw new b(404,{message:"Tenant not found"});const o={adapters:a.env.data,ctx:a};return(s=e.tenants)!=null&&s.beforeDelete&&await e.tenants.beforeDelete(o,r),await a.env.data.tenants.remove(r),(c=e.tenants)!=null&&c.afterDelete&&await e.tenants.afterDelete(o,r),a.body(null,204)}),n}function oe(t){const e=[{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:a}of e){const r=t.match(n);if(r&&r[1])return{type:a,id:r[1]}}return null}async function ie(t,e,n){try{switch(n.type){case"resource_server":{const a=await t.resourceServers.get(e,n.id);return(a==null?void 0:a.is_system)===!0}case"role":{const a=await t.roles.get(e,n.id);return(a==null?void 0:a.is_system)===!0}case"connection":{const a=await t.connections.get(e,n.id);return(a==null?void 0:a.is_system)===!0}default:return!1}}catch{return!1}}function ce(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function W(){return async(t,e)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return e();const n=oe(t.req.path);if(!n)return e();const a=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!a)return e();if(await ie(t.env.data,a,n))throw new b(403,{message:`This ${ce(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return e()}}function O(t,e){const{controlPlaneTenantId:n,controlPlaneClientId:a}=e;return{...t,legacyClients:{...t.legacyClients,get:async r=>{var f;const i=await t.legacyClients.get(r);if(!i)return null;const o=a?await t.legacyClients.get(a):void 0,s=await t.connections.list(i.tenant.id),c=n?await t.connections.list(n):{connections:[]},m=s.connections.map(d=>{var g;const u=(g=c.connections)==null?void 0:g.find(h=>h.strategy===d.strategy);if(!(u!=null&&u.options))return d;const p=S.connectionSchema.parse({...u||{},...d});return p.options=S.connectionOptionsSchema.parse({...u.options||{},...d.options}),p}).filter(d=>d),l={...(o==null?void 0:o.tenant)||{},...i.tenant};return!i.tenant.audience&&((f=o==null?void 0:o.tenant)!=null&&f.audience)&&(l.audience=o.tenant.audience),{...i,web_origins:[...(o==null?void 0:o.web_origins)||[],...i.web_origins||[]],allowed_logout_urls:[...(o==null?void 0:o.allowed_logout_urls)||[],...i.allowed_logout_urls||[]],callbacks:[...(o==null?void 0:o.callbacks)||[],...i.callbacks||[]],connections:m,tenant:l}}},connections:{...t.connections,get:async(r,i)=>{var l;const o=await t.connections.get(r,i);if(!o||!n||r===n)return o;const c=(l=(await t.connections.list(n)).connections)==null?void 0:l.find(f=>f.strategy===o.strategy);if(!(c!=null&&c.options))return o;const m=S.connectionSchema.parse({...c,...o});return m.options=S.connectionOptionsSchema.parse({...c.options||{},...o.options}),m},list:async(r,i)=>{const o=await t.connections.list(r,i);if(!n||r===n)return o;const s=await t.connections.list(n),c=o.connections.map(m=>{var d;const l=(d=s.connections)==null?void 0:d.find(u=>u.strategy===m.strategy);if(!(l!=null&&l.options))return m;const f=S.connectionSchema.parse({...l,...m});return f.options=S.connectionOptionsSchema.parse({...l.options||{},...m.options}),f});return{...o,connections:c}}}}}function Q(t,e){return O(t,e)}const le=O,de=Q;function V(t){return async(e,n)=>{if(!t.accessControl)return n();const{controlPlaneTenantId:a}=t.accessControl,r=e.var.org_name,i=e.var.organization_id,o=r||i;let s=e.var.tenant_id;const c=e.var.user,l=(c!=null&&c.aud?Array.isArray(c.aud)?c.aud:[c.aud]:[]).includes(I.MANAGEMENT_API_AUDIENCE);if(!s&&o&&l&&(e.set("tenant_id",o),s=o),!s)throw new b(400,{message:"Tenant ID not found in request"});if(!U(i,s,a,r))throw new b(403,{message:`Access denied to tenant ${s}`});return n()}}function J(t){return async(e,n)=>{if(!t.subdomainRouting)return n();const{baseDomain:a,reservedSubdomains:r=[],resolveSubdomain:i}=t.subdomainRouting,o=e.req.header("host")||"";let s=null;if(o.endsWith(a)){const m=o.slice(0,-(a.length+1));m&&!m.includes(".")&&(s=m)}if(s&&r.includes(s)&&(s=null),!s)return t.accessControl&&e.set("tenant_id",t.accessControl.controlPlaneTenantId),n();let c=null;if(i)c=await i(s);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const m=await e.env.data.organizations.get(t.accessControl.controlPlaneTenantId,s);m&&(c=m.id)}catch{}if(!c)throw new b(404,{message:`Tenant not found for subdomain: ${s}`});return e.set("tenant_id",c),n()}}function X(t){return async(e,n)=>{if(!t.databaseIsolation)return n();const a=e.var.tenant_id;if(!a)throw new b(400,{message:"Tenant ID not found in request"});try{const r=await t.databaseIsolation.getAdapters(a);e.env.data=r}catch(r){throw console.error(`Failed to resolve database for tenant ${a}:`,r),new b(500,{message:"Failed to resolve tenant database"})}return n()}}function q(t){const e=J(t),n=V(t),a=X(t);return async(r,i)=>(await e(r,async()=>{}),await n(r,async()=>{}),await a(r,async()=>{}),i())}function ue(t){const{dataAdapter:e,controlPlaneTenantId:n="control_plane",sync:a={resourceServers:!0,roles:!0},defaultPermissions:r=["tenant:admin"],requireOrganizationMatch:i=!1,managementApiExtensions:o=[],entityHooks:s,getChildTenantIds:c,getAdapters:m,...l}=t,f=a!==!1,d=f?{resourceServers:a.resourceServers??!0,roles:a.roles??!0}:{resourceServers:!1,roles:!1},g={controlPlaneTenantId:n,getChildTenantIds:c??(async()=>(await I.fetchAll(A=>e.tenants.list(A),"tenants",{cursorField:"id",pageSize:100})).filter(A=>A.id!==n).map(A=>A.id)),getAdapters:m??(async()=>e),getControlPlaneAdapters:async()=>e,sync:d},{entityHooks:h,tenantHooks:w}=K(g),C={resourceServers:[h.resourceServers,...(s==null?void 0:s.resourceServers)??[]],roles:[h.roles,...(s==null?void 0:s.roles)??[]],connections:(s==null?void 0:s.connections)??[],tenants:(s==null?void 0:s.tenants)??[],rolePermissions:(s==null?void 0:s.rolePermissions)??[]},v=M({accessControl:{controlPlaneTenantId:n,requireOrganizationMatch:i,defaultPermissions:r}},{tenants:w}),{app:y}=I.init({dataAdapter:e,...l,entityHooks:C,managementApiExtensions:[...o,{path:"/tenants",router:v}]});return f&&y.use("/api/v2/*",W()),{app:y,controlPlaneTenantId:n}}function me(t){const e=R(t);return{name:"multi-tenancy",middleware:q(t),hooks:e,routes:[{path:"/management",handler:M(t,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),t.accessControl&&console.log(` - Access control enabled (control plane: ${t.accessControl.controlPlaneTenantId})`),t.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${t.subdomainRouting.baseDomain})`),t.databaseIsolation&&console.log(" - Database isolation enabled")}}}function R(t){const e=t.accessControl?G(t.accessControl):{},n=t.databaseIsolation?L(t.databaseIsolation):{},a=B(t);return{...e,...n,tenants:a}}function Y(t){const e=new te.Hono,n=R(t);return e.route("/tenants",M(t,n)),e}function fe(t){return{hooks:R(t),middleware:q(t),app:Y(t),config:t}}exports.createAccessControlHooks=G;exports.createAccessControlMiddleware=V;exports.createDatabaseHooks=L;exports.createDatabaseMiddleware=X;exports.createMultiTenancy=Y;exports.createMultiTenancyHooks=R;exports.createMultiTenancyMiddleware=q;exports.createMultiTenancyPlugin=me;exports.createProtectSyncedMiddleware=W;exports.createProvisioningHooks=B;exports.createRuntimeFallbackAdapter=O;exports.createSettingsInheritanceAdapter=le;exports.createSubdomainMiddleware=J;exports.createSyncHooks=K;exports.createTenantsOpenAPIRouter=M;exports.initMultiTenant=ue;exports.setupMultiTenancy=fe;exports.validateTenantAccess=U;exports.withRuntimeFallback=Q;exports.withSettingsInheritance=de;
1
+ "use strict";var ee=Object.defineProperty;var te=(t,e,n)=>e in t?ee(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var $=(t,e,n)=>te(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ne=require("hono"),P=require("authhero"),_=require("@hono/zod-openapi"),S=require("@authhero/adapter-interfaces");function G(t){const{controlPlaneTenantId:e,requireOrganizationMatch:n=!0}=t;return{async onTenantAccessValidation(a,s){if(s===e)return!0;if(n){const i=a.var.org_name,o=a.var.organization_id,r=i||o;return r?r.toLowerCase()===s.toLowerCase():!1}return!0}}}function U(t,e,n,a){if(e===n)return!0;const s=a||t;return s?s.toLowerCase()===e.toLowerCase():!1}function L(t){return{async resolveDataAdapters(e){try{return await t.getAdapters(e)}catch(n){console.error(`Failed to resolve data adapters for tenant ${e}:`,n);return}}}}function ae(t){return`urn:authhero:tenant:${t.toLowerCase()}`}function B(t){return{async beforeCreate(e,n){return!n.audience&&n.id?{...n,audience:ae(n.id)}:n},async afterCreate(e,n){const{accessControl:a,databaseIsolation:s}=t;a&&e.ctx&&await re(e,n,a),s!=null&&s.onProvision&&await s.onProvision(n.id)},async beforeDelete(e,n){const{accessControl:a,databaseIsolation:s}=t;if(a)try{const o=(await e.adapters.organizations.list(a.controlPlaneTenantId)).organizations.find(r=>r.name===n);o&&await e.adapters.organizations.remove(a.controlPlaneTenantId,o.id)}catch(i){console.warn(`Failed to remove organization for tenant ${n}:`,i)}if(s!=null&&s.onDeprovision)try{await s.onDeprovision(n)}catch(i){console.warn(`Failed to deprovision database for tenant ${n}:`,i)}}}}async function re(t,e,n){const{controlPlaneTenantId:a,defaultPermissions:s,defaultRoles:i,issuer:o,adminRoleName:r="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:m=!0}=n,l=await t.adapters.organizations.create(a,{name:e.id,display_name:e.friendly_name||e.id});let f;if(o&&(f=await oe(t,a,r,c)),m&&t.ctx){const d=t.ctx.var.user;if(d!=null&&d.sub&&!await se(t,a,d.sub))try{await t.adapters.userOrganizations.create(a,{user_id:d.sub,organization_id:l.id}),f&&await t.adapters.userRoles.create(a,d.sub,f,l.id)}catch(p){console.warn(`Failed to add creator ${d.sub} to organization ${l.id}:`,p)}}i&&i.length>0&&console.log(`Would assign roles ${i.join(", ")} to organization ${l.id}`),s&&s.length>0&&console.log(`Would grant permissions ${s.join(", ")} to organization ${l.id}`)}async function se(t,e,n){const a=await t.adapters.userRoles.list(e,n,void 0,"");for(const s of a)if((await t.adapters.rolePermissions.list(e,s.id,{per_page:1e3})).some(r=>r.permission_name==="admin:organizations"))return!0;return!1}async function oe(t,e,n,a){const i=(await t.adapters.roles.list(e,{})).roles.find(m=>m.name===n);if(i)return i.id;const o=await t.adapters.roles.create(e,{name:n,description:a}),r=P.MANAGEMENT_API_AUDIENCE,c=P.MANAGEMENT_API_SCOPES.map(m=>({role_id:o.id,resource_server_identifier:r,permission_name:m.value}));return await t.adapters.rolePermissions.assign(e,o.id,c),o.id}function j(t,e,n=()=>!0){const{controlPlaneTenantId:a,getChildTenantIds:s,getAdapters:i}=t,o=new Map;async function r(l,f,d){return(await e(l).list(f,{q:`name:${d}`,per_page:1}))[0]??null}async function c(l){const f=await s(),d=e(await i(a));await Promise.all(f.map(async u=>{try{const p=await i(u),g=e(p),w={...d.transform(l),is_system:!0},C=await r(p,u,l.name),v=C?g.getId(C):void 0;if(C&&v){const h=g.preserveOnUpdate?g.preserveOnUpdate(C,w):w;await g.update(u,v,h)}else await g.create(u,w)}catch(p){console.error(`Failed to sync ${d.listKey} "${l.name}" to tenant "${u}":`,p)}}))}async function m(l){const f=await s();await Promise.all(f.map(async d=>{try{const u=await i(d),p=e(u),g=await r(u,d,l),y=g?p.getId(g):void 0;g&&y&&await p.remove(d,y)}catch(u){console.error(`Failed to delete entity "${l}" from tenant "${d}":`,u)}}))}return{afterCreate:async(l,f)=>{l.tenantId===a&&n(f)&&await c(f)},afterUpdate:async(l,f,d)=>{l.tenantId===a&&n(d)&&await c(d)},beforeDelete:async(l,f)=>{if(l.tenantId!==a)return;const u=await e(l.adapters).get(l.tenantId,f);u&&n(u)&&o.set(f,u)},afterDelete:async(l,f)=>{if(l.tenantId!==a)return;const d=o.get(f);d&&(o.delete(f),await m(d.name))}}}function E(t,e,n=()=>!0){const{controlPlaneTenantId:a,getControlPlaneAdapters:s,getAdapters:i}=t;return{async afterCreate(o,r){if(r.id!==a)try{const c=await s(),m=await i(r.id),l=e(c),f=e(m),d=await P.fetchAll(u=>l.listPaginated(a,u),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(d.filter(u=>n(u)).map(async u=>{try{const p=l.transform(u);await f.create(r.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync entity to new tenant "${r.id}":`,p)}}))}catch(c){console.error(`Failed to sync entities to new tenant "${r.id}":`,c)}}}}const N=t=>({list:async(e,n)=>(await t.resourceServers.list(e,n)).resource_servers,listPaginated:(e,n)=>t.resourceServers.list(e,n),get:(e,n)=>t.resourceServers.get(e,n),create:(e,n)=>t.resourceServers.create(e,n),update:(e,n,a)=>t.resourceServers.update(e,n,a),remove:(e,n)=>t.resourceServers.remove(e,n),listKey:"resource_servers",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,identifier:e.identifier,scopes:e.scopes,signing_alg:e.signing_alg,token_lifetime:e.token_lifetime,token_lifetime_for_web:e.token_lifetime_for_web})}),H=t=>({list:async(e,n)=>(await t.roles.list(e,n)).roles,listPaginated:(e,n)=>t.roles.list(e,n),get:(e,n)=>t.roles.get(e,n),create:(e,n)=>t.roles.create(e,n),update:(e,n,a)=>t.roles.update(e,n,a),remove:(e,n)=>t.roles.remove(e,n),listKey:"roles",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})});function K(t){const{sync:e={},filters:n={}}=t,a=e.resourceServers??!0,s=e.roles??!0,i=a?j(t,N,n.resourceServers):void 0,o=s?j(t,H,n.roles):void 0,r=a?E(t,N,n.resourceServers):void 0,c=s?E(t,H,n.roles):void 0,m=s?{async afterCreate(d,u){var p;if(u.id!==t.controlPlaneTenantId){await((p=c==null?void 0:c.afterCreate)==null?void 0:p.call(c,d,u));try{const g=await t.getControlPlaneAdapters(),y=await t.getAdapters(u.id),w=await P.fetchAll(v=>g.roles.list(t.controlPlaneTenantId,v),"roles",{cursorField:"id",pageSize:100}),C=new Map;for(const v of w.filter(h=>{var A;return((A=n.roles)==null?void 0:A.call(n,h))??!0})){const h=await l(y,u.id,v.name);h&&C.set(v.name,h.id)}for(const v of w.filter(h=>{var A;return((A=n.roles)==null?void 0:A.call(n,h))??!0})){const h=C.get(v.name);if(h)try{const A=await g.rolePermissions.list(t.controlPlaneTenantId,v.id,{});A.length>0&&await y.rolePermissions.assign(u.id,h,A.map(T=>({role_id:h,resource_server_identifier:T.resource_server_identifier,permission_name:T.permission_name})))}catch(A){console.error(`Failed to sync permissions for role "${v.name}" to tenant "${u.id}":`,A)}}}catch(g){console.error(`Failed to sync role permissions to tenant "${u.id}":`,g)}}}}:void 0;async function l(d,u,p){return(await d.roles.list(u,{q:`name:${p}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:i,roles:o},tenantHooks:{async afterCreate(d,u){const p=[r==null?void 0:r.afterCreate,(m==null?void 0:m.afterCreate)??(c==null?void 0:c.afterCreate)],g=[];for(const y of p)if(y)try{await y(d,u)}catch(w){g.push(w instanceof Error?w:new Error(String(w)))}if(g.length===1)throw g[0];if(g.length>1)throw new AggregateError(g,g.map(y=>y.message).join("; "))}}}}var b=class extends Error{constructor(e=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});$(this,"res");$(this,"status");this.res=n==null?void 0:n.res,this.status=e}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function z(t,e){const n=new _.OpenAPIHono;return n.openapi(_.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:S.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:_.z.object({tenants:_.z.array(S.tenantSchema),start:_.z.number().optional(),limit:_.z.number().optional(),length:_.z.number().optional()})}},description:"List of tenants"}}}),async a=>{var u,p,g,y;const s=a.req.valid("query"),{page:i,per_page:o,include_totals:r,q:c}=s,m=a.var.user,l=(m==null?void 0:m.permissions)||[];if(l.includes("auth:read")||l.includes("admin:organizations")){const w=await a.env.data.tenants.list({page:i,per_page:o,include_totals:r,q:c});return r?a.json({tenants:w.tenants,start:((u=w.totals)==null?void 0:u.start)??0,limit:((p=w.totals)==null?void 0:p.limit)??o,length:w.tenants.length}):a.json({tenants:w.tenants})}if(t.accessControl&&(m!=null&&m.sub)){const w=t.accessControl.controlPlaneTenantId,v=(await P.fetchAll(I=>a.env.data.userOrganizations.listUserOrganizations(w,m.sub,I),"organizations")).map(I=>I.name);if(v.length===0)return r?a.json({tenants:[],start:0,limit:o??50,length:0}):a.json({tenants:[]});const h=v.length,A=i??0,T=o??50,M=A*T,D=v.slice(M,M+T);if(D.length===0)return r?a.json({tenants:[],start:M,limit:T,length:h}):a.json({tenants:[]});const F=D.map(I=>`id:${I}`).join(" OR "),x=c?`(${F}) AND (${c})`:F,k=await a.env.data.tenants.list({q:x,per_page:T,include_totals:!1});return r?a.json({tenants:k.tenants,start:M,limit:T,length:h}):a.json({tenants:k.tenants})}const d=await a.env.data.tenants.list({page:i,per_page:o,include_totals:r,q:c});return r?a.json({tenants:d.tenants,start:((g=d.totals)==null?void 0:g.start)??0,limit:((y=d.totals)==null?void 0:y.limit)??o,length:d.tenants.length}):a.json({tenants:d.tenants})}),n.openapi(_.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:S.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:S.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async a=>{var c,m;const s=a.var.user;if(!(s!=null&&s.sub))throw new b(401,{message:"Authentication required to create tenants"});let i=a.req.valid("json");const o={adapters:a.env.data,ctx:a};(c=e.tenants)!=null&&c.beforeCreate&&(i=await e.tenants.beforeCreate(o,i));const r=await a.env.data.tenants.create(i);return(m=e.tenants)!=null&&m.afterCreate&&await e.tenants.afterCreate(o,r),a.json(r,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 a=>{var r,c;const{id:s}=a.req.valid("param");if(t.accessControl){const m=a.var.user,l=t.accessControl.controlPlaneTenantId;if(!(m!=null&&m.sub))throw new b(401,{message:"Authentication required"});if(s===l)throw new b(403,{message:"Cannot delete the control plane"});if(!(await P.fetchAll(u=>a.env.data.userOrganizations.listUserOrganizations(l,m.sub,u),"organizations")).some(u=>u.name===s))throw new b(403,{message:"Access denied to this tenant"})}if(!await a.env.data.tenants.get(s))throw new b(404,{message:"Tenant not found"});const o={adapters:a.env.data,ctx:a};return(r=e.tenants)!=null&&r.beforeDelete&&await e.tenants.beforeDelete(o,s),await a.env.data.tenants.remove(s),(c=e.tenants)!=null&&c.afterDelete&&await e.tenants.afterDelete(o,s),a.body(null,204)}),n}function ie(t){const e=[{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:a}of e){const s=t.match(n);if(s&&s[1])return{type:a,id:s[1]}}return null}async function ce(t,e,n){try{switch(n.type){case"resource_server":{const a=await t.resourceServers.get(e,n.id);return(a==null?void 0:a.is_system)===!0}case"role":{const a=await t.roles.get(e,n.id);return(a==null?void 0:a.is_system)===!0}case"connection":{const a=await t.connections.get(e,n.id);return(a==null?void 0:a.is_system)===!0}default:return!1}}catch{return!1}}function le(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function W(){return async(t,e)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return e();const n=ie(t.req.path);if(!n)return e();const a=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!a)return e();if(await ce(t.env.data,a,n))throw new b(403,{message:`This ${le(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return e()}}function O(t,e){const{controlPlaneTenantId:n,controlPlaneClientId:a}=e;return{...t,legacyClients:{...t.legacyClients,get:async s=>{var f;const i=await t.legacyClients.get(s);if(!i)return null;const o=a?await t.legacyClients.get(a):void 0,r=await t.connections.list(i.tenant.id),c=n?await t.connections.list(n):{connections:[]},m=r.connections.map(d=>{var g;const u=(g=c.connections)==null?void 0:g.find(y=>y.strategy===d.strategy);if(!(u!=null&&u.options))return d;const p=S.connectionSchema.parse({...u||{},...d});return p.options=S.connectionOptionsSchema.parse({...u.options||{},...d.options}),p}).filter(d=>d),l={...(o==null?void 0:o.tenant)||{},...i.tenant};return!i.tenant.audience&&((f=o==null?void 0:o.tenant)!=null&&f.audience)&&(l.audience=o.tenant.audience),{...i,web_origins:[...(o==null?void 0:o.web_origins)||[],...i.web_origins||[]],allowed_logout_urls:[...(o==null?void 0:o.allowed_logout_urls)||[],...i.allowed_logout_urls||[]],callbacks:[...(o==null?void 0:o.callbacks)||[],...i.callbacks||[]],connections:m,tenant:l}}},connections:{...t.connections,get:async(s,i)=>{var l;const o=await t.connections.get(s,i);if(!o||!n||s===n)return o;const c=(l=(await t.connections.list(n)).connections)==null?void 0:l.find(f=>f.strategy===o.strategy);if(!(c!=null&&c.options))return o;const m=S.connectionSchema.parse({...c,...o});return m.options=S.connectionOptionsSchema.parse({...c.options||{},...o.options}),m},list:async(s,i)=>{const o=await t.connections.list(s,i);if(!n||s===n)return o;const r=await t.connections.list(n),c=o.connections.map(m=>{var d;const l=(d=r.connections)==null?void 0:d.find(u=>u.strategy===m.strategy);if(!(l!=null&&l.options))return m;const f=S.connectionSchema.parse({...l,...m});return f.options=S.connectionOptionsSchema.parse({...l.options||{},...m.options}),f});return{...o,connections:c}}}}}function Q(t,e){return O(t,e)}const de=O,ue=Q;function V(t){return async(e,n)=>{const a=e.var.user;return(a==null?void 0:a.tenant_id)===t&&a.org_name&&e.set("tenant_id",a.org_name),n()}}function J(t){return async(e,n)=>{if(!t.accessControl)return n();const{controlPlaneTenantId:a}=t.accessControl,s=e.var.org_name,i=e.var.organization_id,o=s||i;let r=e.var.tenant_id;const c=e.var.user,l=(c!=null&&c.aud?Array.isArray(c.aud)?c.aud:[c.aud]:[]).includes(P.MANAGEMENT_API_AUDIENCE);if(!r&&o&&l&&(e.set("tenant_id",o),r=o),!r)throw new b(400,{message:"Tenant ID not found in request"});if(!U(i,r,a,s))throw new b(403,{message:`Access denied to tenant ${r}`});return n()}}function X(t){return async(e,n)=>{if(!t.subdomainRouting)return n();const{baseDomain:a,reservedSubdomains:s=[],resolveSubdomain:i}=t.subdomainRouting,o=e.req.header("host")||"";let r=null;if(o.endsWith(a)){const m=o.slice(0,-(a.length+1));m&&!m.includes(".")&&(r=m)}if(r&&s.includes(r)&&(r=null),!r)return t.accessControl&&e.set("tenant_id",t.accessControl.controlPlaneTenantId),n();let c=null;if(i)c=await i(r);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const m=await e.env.data.organizations.get(t.accessControl.controlPlaneTenantId,r);m&&(c=m.id)}catch{}if(!c)throw new b(404,{message:`Tenant not found for subdomain: ${r}`});return e.set("tenant_id",c),n()}}function Y(t){return async(e,n)=>{if(!t.databaseIsolation)return n();const a=e.var.tenant_id;if(!a)throw new b(400,{message:"Tenant ID not found in request"});try{const s=await t.databaseIsolation.getAdapters(a);e.env.data=s}catch(s){throw console.error(`Failed to resolve database for tenant ${a}:`,s),new b(500,{message:"Failed to resolve tenant database"})}return n()}}function q(t){const e=X(t),n=J(t),a=Y(t);return async(s,i)=>(await e(s,async()=>{}),await n(s,async()=>{}),await a(s,async()=>{}),i())}function me(t){const{dataAdapter:e,controlPlaneTenantId:n="control_plane",sync:a={resourceServers:!0,roles:!0},defaultPermissions:s=["tenant:admin"],requireOrganizationMatch:i=!1,managementApiExtensions:o=[],entityHooks:r,getChildTenantIds:c,getAdapters:m,...l}=t,f=a!==!1,d=f?{resourceServers:a.resourceServers??!0,roles:a.roles??!0}:{resourceServers:!1,roles:!1},g={controlPlaneTenantId:n,getChildTenantIds:c??(async()=>(await P.fetchAll(T=>e.tenants.list(T),"tenants",{cursorField:"id",pageSize:100})).filter(T=>T.id!==n).map(T=>T.id)),getAdapters:m??(async()=>e),getControlPlaneAdapters:async()=>e,sync:d},{entityHooks:y,tenantHooks:w}=K(g),C={resourceServers:[y.resourceServers,...(r==null?void 0:r.resourceServers)??[]],roles:[y.roles,...(r==null?void 0:r.roles)??[]],connections:(r==null?void 0:r.connections)??[],tenants:(r==null?void 0:r.tenants)??[],rolePermissions:(r==null?void 0:r.rolePermissions)??[]},v=z({accessControl:{controlPlaneTenantId:n,requireOrganizationMatch:i,defaultPermissions:s}},{tenants:w}),{app:h}=P.init({dataAdapter:e,...l,entityHooks:C,managementApiExtensions:[...o,{path:"/tenants",router:v}]});return h.use("/api/v2/*",V(n)),f&&h.use("/api/v2/*",W()),{app:h,controlPlaneTenantId:n}}function fe(t){const e=R(t);return{name:"multi-tenancy",middleware:q(t),hooks:e,routes:[{path:"/management",handler:z(t,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),t.accessControl&&console.log(` - Access control enabled (control plane: ${t.accessControl.controlPlaneTenantId})`),t.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${t.subdomainRouting.baseDomain})`),t.databaseIsolation&&console.log(" - Database isolation enabled")}}}function R(t){const e=t.accessControl?G(t.accessControl):{},n=t.databaseIsolation?L(t.databaseIsolation):{},a=B(t);return{...e,...n,tenants:a}}function Z(t){const e=new ne.Hono,n=R(t);return e.route("/tenants",z(t,n)),e}function ge(t){return{hooks:R(t),middleware:q(t),app:Z(t),config:t}}exports.createAccessControlHooks=G;exports.createAccessControlMiddleware=J;exports.createControlPlaneTenantMiddleware=V;exports.createDatabaseHooks=L;exports.createDatabaseMiddleware=Y;exports.createMultiTenancy=Z;exports.createMultiTenancyHooks=R;exports.createMultiTenancyMiddleware=q;exports.createMultiTenancyPlugin=fe;exports.createProtectSyncedMiddleware=W;exports.createProvisioningHooks=B;exports.createRuntimeFallbackAdapter=O;exports.createSettingsInheritanceAdapter=de;exports.createSubdomainMiddleware=X;exports.createSyncHooks=K;exports.createTenantsOpenAPIRouter=z;exports.initMultiTenant=me;exports.setupMultiTenancy=ge;exports.validateTenantAccess=U;exports.withRuntimeFallback=Q;exports.withSettingsInheritance=ue;
@@ -40988,6 +40988,29 @@ export declare const createSettingsInheritanceAdapter: typeof createRuntimeFallb
40988
40988
  * @deprecated Use `withRuntimeFallback` instead
40989
40989
  */
40990
40990
  export declare const withSettingsInheritance: typeof withRuntimeFallback;
40991
+ /**
40992
+ * Creates middleware that resolves tenant_id from org_name for control plane users.
40993
+ *
40994
+ * When a user authenticates to the control plane tenant and has an org_name claim,
40995
+ * this middleware sets the tenant_id to the org_name, allowing them to access
40996
+ * that child tenant's resources.
40997
+ *
40998
+ * @param controlPlaneTenantId - The ID of the control plane tenant
40999
+ * @returns Hono middleware handler
41000
+ *
41001
+ * @example
41002
+ * ```typescript
41003
+ * import { createControlPlaneTenantMiddleware } from "@authhero/multi-tenancy";
41004
+ *
41005
+ * const middleware = createControlPlaneTenantMiddleware("control_plane");
41006
+ *
41007
+ * app.use("/api/*", middleware);
41008
+ * ```
41009
+ */
41010
+ export declare function createControlPlaneTenantMiddleware(controlPlaneTenantId: string): MiddlewareHandler<{
41011
+ Bindings: MultiTenancyBindings;
41012
+ Variables: MultiTenancyVariables;
41013
+ }>;
40991
41014
  /**
40992
41015
  * Creates middleware for validating organization-based tenant access.
40993
41016
  *
@@ -2,7 +2,7 @@ var W = Object.defineProperty;
2
2
  var Q = (t, e, n) => e in t ? W(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
3
  var $ = (t, e, n) => Q(t, typeof e != "symbol" ? e + "" : e, n);
4
4
  import { Hono as V } from "hono";
5
- import { MANAGEMENT_API_SCOPES as J, MANAGEMENT_API_AUDIENCE as L, fetchAll as P, init as X } from "authhero";
5
+ import { MANAGEMENT_API_SCOPES as J, MANAGEMENT_API_AUDIENCE as L, fetchAll as I, init as X } from "authhero";
6
6
  import { OpenAPIHono as Y, createRoute as z, z as C } from "@hono/zod-openapi";
7
7
  import { auth0QuerySchema as Z, tenantSchema as k, tenantInsertSchema as x, connectionSchema as R, connectionOptionsSchema as O } from "@authhero/adapter-interfaces";
8
8
  function ee(t) {
@@ -246,7 +246,7 @@ function N(t, e, n = () => !0) {
246
246
  async afterCreate(o, r) {
247
247
  if (r.id !== s)
248
248
  try {
249
- const c = await a(), m = await i(r.id), l = e(c), f = e(m), d = await P(
249
+ const c = await a(), m = await i(r.id), l = e(c), f = e(m), d = await I(
250
250
  (u) => l.listPaginated(s, u),
251
251
  l.listKey,
252
252
  { cursorField: "id", pageSize: 100 }
@@ -332,7 +332,7 @@ function ce(t) {
332
332
  if (u.id !== t.controlPlaneTenantId) {
333
333
  await ((p = c == null ? void 0 : c.afterCreate) == null ? void 0 : p.call(c, d, u));
334
334
  try {
335
- const g = await t.getControlPlaneAdapters(), y = await t.getAdapters(u.id), w = await P(
335
+ const g = await t.getControlPlaneAdapters(), y = await t.getAdapters(u.id), w = await I(
336
336
  (v) => g.roles.list(
337
337
  t.controlPlaneTenantId,
338
338
  v
@@ -501,14 +501,14 @@ function M(t, e) {
501
501
  }) : s.json({ tenants: w.tenants });
502
502
  }
503
503
  if (t.accessControl && (m != null && m.sub)) {
504
- const w = t.accessControl.controlPlaneTenantId, v = (await P(
505
- (I) => s.env.data.userOrganizations.listUserOrganizations(
504
+ const w = t.accessControl.controlPlaneTenantId, v = (await I(
505
+ (P) => s.env.data.userOrganizations.listUserOrganizations(
506
506
  w,
507
507
  m.sub,
508
- I
508
+ P
509
509
  ),
510
510
  "organizations"
511
- )).map((I) => I.name);
511
+ )).map((P) => P.name);
512
512
  if (v.length === 0)
513
513
  return r ? s.json({
514
514
  tenants: [],
@@ -524,7 +524,7 @@ function M(t, e) {
524
524
  limit: _,
525
525
  length: h
526
526
  }) : s.json({ tenants: [] });
527
- const F = D.map((I) => `id:${I}`).join(" OR "), K = c ? `(${F}) AND (${c})` : F, j = await s.env.data.tenants.list({
527
+ const F = D.map((P) => `id:${P}`).join(" OR "), K = c ? `(${F}) AND (${c})` : F, j = await s.env.data.tenants.list({
528
528
  q: K,
529
529
  per_page: _,
530
530
  include_totals: !1
@@ -642,7 +642,7 @@ function M(t, e) {
642
642
  throw new b(403, {
643
643
  message: "Cannot delete the control plane"
644
644
  });
645
- if (!(await P(
645
+ if (!(await I(
646
646
  (u) => s.env.data.userOrganizations.listUserOrganizations(
647
647
  l,
648
648
  m.sub,
@@ -841,8 +841,14 @@ function U(t, e) {
841
841
  function fe(t, e) {
842
842
  return U(t, e);
843
843
  }
844
- const Ae = U, Ce = fe;
844
+ const Ce = U, Pe = fe;
845
845
  function ge(t) {
846
+ return async (e, n) => {
847
+ const s = e.var.user;
848
+ return (s == null ? void 0 : s.tenant_id) === t && s.org_name && e.set("tenant_id", s.org_name), n();
849
+ };
850
+ }
851
+ function pe(t) {
846
852
  return async (e, n) => {
847
853
  if (!t.accessControl)
848
854
  return n();
@@ -865,7 +871,7 @@ function ge(t) {
865
871
  return n();
866
872
  };
867
873
  }
868
- function pe(t) {
874
+ function we(t) {
869
875
  return async (e, n) => {
870
876
  if (!t.subdomainRouting)
871
877
  return n();
@@ -900,7 +906,7 @@ function pe(t) {
900
906
  return e.set("tenant_id", c), n();
901
907
  };
902
908
  }
903
- function we(t) {
909
+ function ye(t) {
904
910
  return async (e, n) => {
905
911
  if (!t.databaseIsolation)
906
912
  return n();
@@ -924,7 +930,7 @@ function we(t) {
924
930
  };
925
931
  }
926
932
  function B(t) {
927
- const e = pe(t), n = ge(t), s = we(t);
933
+ const e = we(t), n = pe(t), s = ye(t);
928
934
  return async (a, i) => (await e(a, async () => {
929
935
  }), await n(a, async () => {
930
936
  }), await s(a, async () => {
@@ -947,7 +953,7 @@ function Ie(t) {
947
953
  roles: s.roles ?? !0
948
954
  } : { resourceServers: !1, roles: !1 }, g = {
949
955
  controlPlaneTenantId: n,
950
- getChildTenantIds: c ?? (async () => (await P(
956
+ getChildTenantIds: c ?? (async () => (await I(
951
957
  (_) => e.tenants.list(_),
952
958
  "tenants",
953
959
  { cursorField: "id", pageSize: 100 }
@@ -982,9 +988,9 @@ function Ie(t) {
982
988
  { path: "/tenants", router: v }
983
989
  ]
984
990
  });
985
- return f && h.use("/api/v2/*", me()), { app: h, controlPlaneTenantId: n };
991
+ return h.use("/api/v2/*", ge(n)), f && h.use("/api/v2/*", me()), { app: h, controlPlaneTenantId: n };
986
992
  }
987
- function Pe(t) {
993
+ function Se(t) {
988
994
  const e = q(t);
989
995
  return {
990
996
  name: "multi-tenancy",
@@ -1017,37 +1023,38 @@ function q(t) {
1017
1023
  tenants: s
1018
1024
  };
1019
1025
  }
1020
- function ye(t) {
1026
+ function he(t) {
1021
1027
  const e = new V(), n = q(t);
1022
1028
  return e.route("/tenants", M(t, n)), e;
1023
1029
  }
1024
- function Se(t) {
1030
+ function $e(t) {
1025
1031
  return {
1026
1032
  hooks: q(t),
1027
1033
  middleware: B(t),
1028
- app: ye(t),
1034
+ app: he(t),
1029
1035
  config: t
1030
1036
  };
1031
1037
  }
1032
1038
  export {
1033
1039
  ee as createAccessControlHooks,
1034
- ge as createAccessControlMiddleware,
1040
+ pe as createAccessControlMiddleware,
1041
+ ge as createControlPlaneTenantMiddleware,
1035
1042
  ne as createDatabaseHooks,
1036
- we as createDatabaseMiddleware,
1037
- ye as createMultiTenancy,
1043
+ ye as createDatabaseMiddleware,
1044
+ he as createMultiTenancy,
1038
1045
  q as createMultiTenancyHooks,
1039
1046
  B as createMultiTenancyMiddleware,
1040
- Pe as createMultiTenancyPlugin,
1047
+ Se as createMultiTenancyPlugin,
1041
1048
  me as createProtectSyncedMiddleware,
1042
1049
  re as createProvisioningHooks,
1043
1050
  U as createRuntimeFallbackAdapter,
1044
- Ae as createSettingsInheritanceAdapter,
1045
- pe as createSubdomainMiddleware,
1051
+ Ce as createSettingsInheritanceAdapter,
1052
+ we as createSubdomainMiddleware,
1046
1053
  ce as createSyncHooks,
1047
1054
  M as createTenantsOpenAPIRouter,
1048
1055
  Ie as initMultiTenant,
1049
- Se as setupMultiTenancy,
1056
+ $e as setupMultiTenancy,
1050
1057
  te as validateTenantAccess,
1051
1058
  fe as withRuntimeFallback,
1052
- Ce as withSettingsInheritance
1059
+ Pe as withSettingsInheritance
1053
1060
  };
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.1.0",
14
+ "version": "14.2.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"
@@ -37,8 +37,8 @@
37
37
  "typescript": "^5.6.0",
38
38
  "vite": "^6.0.0",
39
39
  "vitest": "^2.1.0",
40
- "@authhero/kysely-adapter": "10.77.0",
41
- "authhero": "4.0.0"
40
+ "authhero": "4.1.0",
41
+ "@authhero/kysely-adapter": "10.77.0"
42
42
  },
43
43
  "dependencies": {
44
44
  "zod": "^3.24.0",