@authhero/multi-tenancy 13.6.0 → 13.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/multi-tenancy.cjs +1 -1
- package/dist/multi-tenancy.d.ts +68 -3
- package/dist/multi-tenancy.mjs +723 -388
- package/package.json +2 -2
package/dist/multi-tenancy.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var W=Object.defineProperty;var G=(e,n,s)=>n in e?W(e,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[n]=s;var z=(e,n,s)=>G(e,typeof n!="symbol"?n+"":n,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("hono"),S=require("authhero"),Q=require("zod"),$=require("@authhero/adapter-interfaces");function q(e){const{mainTenantId:n,requireOrganizationMatch:s=!0}=e;return{async onTenantAccessValidation(t,a){if(a===n)return!0;if(s){const o=t.var.organization_id;return o?o===a:!1}return!0}}}function P(e,n,s){return n===s?!0:e?e===n:!1}function D(e){return{async resolveDataAdapters(n){try{return await e.getAdapters(n)}catch(s){console.error(`Failed to resolve data adapters for tenant ${n}:`,s);return}}}}function F(e){return{async afterCreate(n,s){const{accessControl:t,databaseIsolation:a,settingsInheritance:o}=e;t&&n.ctx&&await V(n,s,t),a!=null&&a.onProvision&&await a.onProvision(s.id),(o==null?void 0:o.inheritFromMain)!==!1&&n.ctx&&await Y(n,s,e)},async beforeDelete(n,s){const{accessControl:t,databaseIsolation:a}=e;if(t)try{const d=(await n.adapters.organizations.list(t.mainTenantId)).organizations.find(i=>i.name===s);d&&await n.adapters.organizations.remove(t.mainTenantId,d.id)}catch(o){console.warn(`Failed to remove organization for tenant ${s}:`,o)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(s)}catch(o){console.warn(`Failed to deprovision database for tenant ${s}:`,o)}}}}async function V(e,n,s){const{mainTenantId:t,defaultPermissions:a,defaultRoles:o,issuer:d,adminRoleName:i="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:r=!0}=s;await e.adapters.organizations.create(t,{id:n.id,name:n.id,display_name:n.friendly_name||n.id});let l=null;if(d&&(l=await L(e,t,d,i,c)),r&&e.ctx){const f=e.ctx.var.user;if(f!=null&&f.sub)try{await e.adapters.userOrganizations.create(t,{user_id:f.sub,organization_id:n.id}),l&&await e.adapters.userRoles.create(t,f.sub,l,n.id)}catch(u){console.warn(`Failed to add creator ${f.sub} to organization ${n.id}:`,u)}}o&&o.length>0&&console.log(`Would assign roles ${o.join(", ")} to organization ${n.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${n.id}`)}async function L(e,n,s,t,a){const d=(await e.adapters.roles.list(n,{})).roles.find(l=>l.name===t);if(d)return d.id;const i=await e.adapters.roles.create(n,{name:t,description:a}),c=`${s}api/v2/`,r=S.MANAGEMENT_API_SCOPES.map(l=>({role_id:i.id,resource_server_identifier:c,permission_name:l.value}));return await e.adapters.rolePermissions.assign(n,i.id,r),i.id}async function Y(e,n,s){const{accessControl:t,settingsInheritance:a}=s;if(!t)return;const o=await e.adapters.tenants.get(t.mainTenantId);if(!o)return;let d={...o};const i=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const c of i)delete d[c];if(a!=null&&a.inheritedKeys){const c={};for(const r of a.inheritedKeys)r in o&&!i.includes(r)&&(c[r]=o[r]);d=c}if(a!=null&&a.excludedKeys)for(const c of a.excludedKeys)delete d[c];a!=null&&a.transformSettings&&(d=a.transformSettings(d,n.id)),Object.keys(d).length>0&&await e.adapters.tenants.update(n.id,d)}async function R(e,n,s={}){const{cursorField:t="id",sortOrder:a="asc",pageSize:o=100,maxItems:d=1e4,q:i}=s,c=[];let r,l=!0;for(;l;){let f=i||"";if(r){const w=`${t}:${a==="asc"?">":"<"}${r}`;f=f?`(${f}) AND ${w}`:w}const u={per_page:o,page:0,sort:{sort_by:t,sort_order:a},...f&&{q:f}},_=(await e(u))[n]||[];if(_.length===0)l=!1;else{c.push(..._);const g=_[_.length-1];if(g&&typeof g=="object"){const w=g[t];w!=null&&(r=String(w))}_.length<o&&(l=!1),d!==-1&&c.length>=d&&(console.warn(`fetchAll: Reached maxItems limit (${d}). There may be more items.`),l=!1)}}return c}function j(e){const{mainTenantId:n,getChildTenantIds:s,getAdapters:t,shouldSync:a=()=>!0,transformForSync:o}=e;async function d(r,l,f){return(await r.resourceServers.list(l,{q:`identifier:${f}`,per_page:1})).resource_servers[0]??null}async function i(r,l){const f=await s();await Promise.all(f.map(async u=>{try{const m=await t(u),g={...o?o(r,u):{name:r.name,identifier:r.identifier,scopes:r.scopes,signing_alg:r.signing_alg,signing_secret:r.signing_secret,token_lifetime:r.token_lifetime,token_lifetime_for_web:r.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:r.skip_consent_for_verifiable_first_party_clients,allow_offline_access:r.allow_offline_access,verificationKey:r.verificationKey,options:r.options},is_system:!0};if(l==="create"){const w=await d(m,u,r.identifier);w&&w.id?await m.resourceServers.update(u,w.id,g):await m.resourceServers.create(u,g)}else{const w=await d(m,u,r.identifier);w&&w.id&&await m.resourceServers.update(u,w.id,g)}}catch(m){console.error(`Failed to sync resource server "${r.identifier}" to tenant "${u}":`,m)}}))}async function c(r){const l=await s();await Promise.all(l.map(async f=>{try{const u=await t(f),m=await d(u,f,r);m&&m.id&&await u.resourceServers.remove(f,m.id)}catch(u){console.error(`Failed to delete resource server "${r}" from tenant "${f}":`,u)}}))}return{afterCreate:async(r,l)=>{r.tenantId===n&&a(l)&&await i(l,"create")},afterUpdate:async(r,l,f)=>{r.tenantId===n&&a(f)&&await i(f,"update")},afterDelete:async(r,l)=>{r.tenantId===n&&await c(l)}}}function U(e){const{mainTenantId:n,getMainTenantAdapters:s,getAdapters:t,shouldSync:a=()=>!0,transformForSync:o}=e;return{async afterCreate(d,i){if(i.id!==n)try{const c=await s(),r=await t(i.id),l=await R(f=>c.resourceServers.list(n,f),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(l.filter(f=>a(f)).map(async f=>{const u=f;try{const m=o?o(u,i.id):{name:u.name,identifier:u.identifier,scopes:u.scopes,signing_alg:u.signing_alg,signing_secret:u.signing_secret,token_lifetime:u.token_lifetime,token_lifetime_for_web:u.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:u.skip_consent_for_verifiable_first_party_clients,allow_offline_access:u.allow_offline_access,verificationKey:u.verificationKey,options:u.options};await r.resourceServers.create(i.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync resource server "${u.identifier}" to new tenant "${i.id}":`,m)}}))}catch(c){console.error(`Failed to sync resource servers to new tenant "${i.id}":`,c)}}}}var p=class extends Error{constructor(n=500,s){super(s==null?void 0:s.message,{cause:s==null?void 0:s.cause});z(this,"res");z(this,"status");this.res=s==null?void 0:s.res,this.status=n}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(e,n){const s=new I.Hono;return s.get("/",async t=>{var f,u;const a=$.auth0QuerySchema.parse(t.req.query()),{page:o,per_page:d,include_totals:i,q:c}=a,r=t.var.user;if(e.accessControl&&(r!=null&&r.sub)){const m=e.accessControl.mainTenantId,g=(await t.env.data.userOrganizations.listUserOrganizations(m,r.sub,{})).organizations.map(y=>y.id);g.includes(m)||g.push(m);const w=await t.env.data.tenants.list({page:o,per_page:d,include_totals:i,q:c}),A=w.tenants.filter(y=>g.includes(y.id));return i?t.json({tenants:A,start:((f=w.totals)==null?void 0:f.start)??0,limit:((u=w.totals)==null?void 0:u.limit)??d,length:A.length}):t.json(A)}const l=await t.env.data.tenants.list({page:o,per_page:d,include_totals:i,q:c});return i?t.json(l):t.json(l.tenants)}),s.get("/:id",async t=>{const a=t.req.param("id");if(e.accessControl){const d=t.var.user,i=e.accessControl.mainTenantId;if(a!==i){if(!(d!=null&&d.sub))throw new p(401,{message:"Authentication required"});if(!(await t.env.data.userOrganizations.listUserOrganizations(i,d.sub,{})).organizations.some(l=>l.id===a))throw new p(403,{message:"Access denied to this tenant"})}}const o=await t.env.data.tenants.get(a);if(!o)throw new p(404,{message:"Tenant not found"});return t.json(o)}),s.post("/",async t=>{var a,o,d;try{const i=t.var.user;if(!(i!=null&&i.sub))throw new p(401,{message:"Authentication required to create tenants"});let c=$.tenantInsertSchema.parse(await t.req.json());const r={adapters:t.env.data,ctx:t};(a=n.tenants)!=null&&a.beforeCreate&&(c=await n.tenants.beforeCreate(r,c));const l=await t.env.data.tenants.create(c);return(o=n.tenants)!=null&&o.afterCreate&&await n.tenants.afterCreate(r,l),t.json(l,201)}catch(i){throw i instanceof Q.z.ZodError?new p(400,{message:"Validation error",cause:i}):i instanceof Error&&("code"in i&&i.code==="SQLITE_CONSTRAINT_PRIMARYKEY"||(d=i.message)!=null&&d.includes("UNIQUE constraint failed"))?new p(409,{message:"Tenant with this ID already exists"}):i}}),s.patch("/:id",async t=>{var u,m;const a=t.req.param("id");if(e.accessControl){const _=t.var.user;if(!(_!=null&&_.sub))throw new p(401,{message:"Authentication required to update tenants"});const g=e.accessControl.mainTenantId;if(a!==g&&!(await t.env.data.userOrganizations.listUserOrganizations(g,_.sub,{})).organizations.some(y=>y.id===a))throw new p(403,{message:"Access denied to update this tenant"})}const o=$.tenantInsertSchema.partial().parse(await t.req.json()),{id:d,...i}=o;if(!await t.env.data.tenants.get(a))throw new p(404,{message:"Tenant not found"});const r={adapters:t.env.data,ctx:t};let l=i;(u=n.tenants)!=null&&u.beforeUpdate&&(l=await n.tenants.beforeUpdate(r,a,i)),await t.env.data.tenants.update(a,l);const f=await t.env.data.tenants.get(a);if(!f)throw new p(404,{message:"Tenant not found after update"});return(m=n.tenants)!=null&&m.afterUpdate&&await n.tenants.afterUpdate(r,f),t.json(f)}),s.delete("/:id",async t=>{var i,c;const a=t.req.param("id");if(e.accessControl&&a===e.accessControl.mainTenantId)throw new p(400,{message:"Cannot delete the main tenant"});if(e.accessControl){const r=t.var.user;if(!(r!=null&&r.sub))throw new p(401,{message:"Authentication required to delete tenants"});const l=e.accessControl.mainTenantId;if(!(await t.env.data.userOrganizations.listUserOrganizations(l,r.sub,{})).organizations.some(m=>m.id===a))throw new p(403,{message:"Access denied to delete this tenant"})}if(!await t.env.data.tenants.get(a))throw new p(404,{message:"Tenant not found"});const d={adapters:t.env.data,ctx:t};return(i=n.tenants)!=null&&i.beforeDelete&&await n.tenants.beforeDelete(d,a),await t.env.data.tenants.remove(a),(c=n.tenants)!=null&&c.afterDelete&&await n.tenants.afterDelete(d,a),t.body(null,204)}),s}function B(e){const n=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:s,type:t}of n){const a=e.match(s);if(a&&a[1])return{type:t,id:a[1]}}return null}async function Z(e,n,s){try{switch(s.type){case"resource_server":{const t=await e.resourceServers.get(n,s.id);return(t==null?void 0:t.is_system)===!0}case"role":{const t=await e.roles.get(n,s.id);return(t==null?void 0:t.is_system)===!0}case"connection":{const t=await e.connections.get(n,s.id);return(t==null?void 0:t.is_system)===!0}default:return!1}}catch{return!1}}function J(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function E(){return async(e,n)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return n();const s=B(e.req.path);if(!s)return n();const t=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!t)return n();if(await Z(e.env.data,t,s))throw new p(403,{message:`This ${J(s.type)} is a system resource and cannot be modified. Make changes in the main tenant instead.`});return n()}}function N(e){return async(n,s)=>{if(!e.accessControl)return s();const t=n.var.tenant_id,a=n.var.organization_id;if(!t)throw new p(400,{message:"Tenant ID not found in request"});if(!P(a,t,e.accessControl.mainTenantId))throw new p(403,{message:`Access denied to tenant ${t}`});return s()}}function k(e){return async(n,s)=>{if(!e.subdomainRouting)return s();const{baseDomain:t,reservedSubdomains:a=[],resolveSubdomain:o}=e.subdomainRouting,d=n.req.header("host")||"";let i=null;if(d.endsWith(t)){const r=d.slice(0,-(t.length+1));r&&!r.includes(".")&&(i=r)}if(i&&a.includes(i)&&(i=null),!i)return e.accessControl&&n.set("tenant_id",e.accessControl.mainTenantId),s();let c=null;if(o)c=await o(i);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const r=await n.env.data.organizations.get(e.accessControl.mainTenantId,i);r&&(c=r.id)}catch{}if(!c)throw new p(404,{message:`Tenant not found for subdomain: ${i}`});return n.set("tenant_id",c),s()}}function K(e){return async(n,s)=>{if(!e.databaseIsolation)return s();const t=n.var.tenant_id;if(!t)throw new p(400,{message:"Tenant ID not found in request"});try{const a=await e.databaseIsolation.getAdapters(t);n.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${t}:`,a),new p(500,{message:"Failed to resolve tenant database"})}return s()}}function O(e){const n=k(e),s=N(e),t=K(e);return async(a,o)=>(await n(a,async()=>{}),await s(a,async()=>{}),await t(a,async()=>{}),o())}function X(e){const n=C(e);return{name:"multi-tenancy",middleware:O(e),hooks:n,routes:[{path:"/management",handler:M(e,n)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),e.accessControl&&console.log(` - Access control enabled (main tenant: ${e.accessControl.mainTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(" - Database isolation enabled")}}}function C(e){const n=e.accessControl?q(e.accessControl):{},s=e.databaseIsolation?D(e.databaseIsolation):{},t=F(e);return{...n,...s,tenants:t}}function H(e){const n=new I.Hono,s=C(e);return n.route("/tenants",M(e,s)),n}function x(e){return{hooks:C(e),middleware:O(e),app:H(e),config:e}}function ee(e){const{mainTenantId:n="main",syncResourceServers:s=!0,multiTenancy:t,entityHooks:a,...o}=e,d={...t,accessControl:{mainTenantId:n,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...t==null?void 0:t.accessControl}},i=C(d);let c,r;s&&(c=j({mainTenantId:n,getChildTenantIds:async()=>(await R(h=>e.dataAdapter.tenants.list(h),"tenants",{cursorField:"id",pageSize:100})).filter(h=>h.id!==n).map(h=>h.id),getAdapters:async y=>e.dataAdapter}),r=U({mainTenantId:n,getMainTenantAdapters:async()=>e.dataAdapter,getAdapters:async y=>e.dataAdapter}));const l=async(y,h,...T)=>{const v=[];if(y)try{await y(...T)}catch(b){v.push(b instanceof Error?b:new Error(String(b)))}if(h)try{await h(...T)}catch(b){v.push(b instanceof Error?b:new Error(String(b)))}if(v.length===1)throw v[0];if(v.length>1)throw new AggregateError(v,`Multiple hook errors: ${v.map(b=>b.message).join("; ")}`)},f={...a,resourceServers:c?{...a==null?void 0:a.resourceServers,afterCreate:async(y,h)=>{var T;await l((T=a==null?void 0:a.resourceServers)==null?void 0:T.afterCreate,c==null?void 0:c.afterCreate,y,h)},afterUpdate:async(y,h,T)=>{var v;await l((v=a==null?void 0:a.resourceServers)==null?void 0:v.afterUpdate,c==null?void 0:c.afterUpdate,y,h,T)},afterDelete:async(y,h)=>{var T;await l((T=a==null?void 0:a.resourceServers)==null?void 0:T.afterDelete,c==null?void 0:c.afterDelete,y,h)}}:a==null?void 0:a.resourceServers,tenants:r?{...a==null?void 0:a.tenants,afterCreate:async(y,h)=>{var T;await l((T=a==null?void 0:a.tenants)==null?void 0:T.afterCreate,r==null?void 0:r.afterCreate,y,h)}}:a==null?void 0:a.tenants},u=S.init({...o,entityHooks:f}),{app:m,managementApp:_,...g}=u,w=new I.Hono;w.use("/api/v2/*",E()),w.route("/",m);const A=M(d,i);return w.route("/api/v2/tenants",A),{app:w,managementApp:_,...g,multiTenancyConfig:d,multiTenancyHooks:i}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>S.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>S.seed});exports.createAccessControlHooks=q;exports.createAccessControlMiddleware=N;exports.createDatabaseHooks=D;exports.createDatabaseMiddleware=K;exports.createMultiTenancy=H;exports.createMultiTenancyHooks=C;exports.createMultiTenancyMiddleware=O;exports.createMultiTenancyPlugin=X;exports.createProtectSyncedMiddleware=E;exports.createProvisioningHooks=F;exports.createResourceServerSyncHooks=j;exports.createSubdomainMiddleware=k;exports.createTenantResourceServerSyncHooks=U;exports.createTenantsRouter=M;exports.fetchAll=R;exports.init=ee;exports.setupMultiTenancy=x;exports.validateTenantAccess=P;
|
|
1
|
+
"use strict";var V=Object.defineProperty;var W=(a,n,s)=>n in a?V(a,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):a[n]=s;var I=(a,n,s)=>W(a,typeof n!="symbol"?n+"":n,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("hono"),S=require("authhero"),G=require("zod"),C=require("@authhero/adapter-interfaces"),_=require("@hono/zod-openapi");function $(a){const{mainTenantId:n,requireOrganizationMatch:s=!0}=a;return{async onTenantAccessValidation(e,t){if(t===n)return!0;if(s){const c=e.var.organization_id;return c?c===t:!1}return!0}}}function P(a,n,s){return n===s?!0:a?a===n:!1}function D(a){return{async resolveDataAdapters(n){try{return await a.getAdapters(n)}catch(s){console.error(`Failed to resolve data adapters for tenant ${n}:`,s);return}}}}function U(a){return{async beforeCreate(n,s){return!s.audience&&s.id?{...s,audience:S.getTenantAudience(s.id)}:s},async afterCreate(n,s){const{accessControl:e,databaseIsolation:t,settingsInheritance:c}=a;e&&n.ctx&&await L(n,s,e),t!=null&&t.onProvision&&await t.onProvision(s.id),(c==null?void 0:c.inheritFromMain)!==!1&&n.ctx&&await k(n,s,a)},async beforeDelete(n,s){const{accessControl:e,databaseIsolation:t}=a;if(e)try{const o=(await n.adapters.organizations.list(e.mainTenantId)).organizations.find(i=>i.name===s);o&&await n.adapters.organizations.remove(e.mainTenantId,o.id)}catch(c){console.warn(`Failed to remove organization for tenant ${s}:`,c)}if(t!=null&&t.onDeprovision)try{await t.onDeprovision(s)}catch(c){console.warn(`Failed to deprovision database for tenant ${s}:`,c)}}}}async function L(a,n,s){const{mainTenantId:e,defaultPermissions:t,defaultRoles:c,issuer:o,adminRoleName:i="Tenant Admin",adminRoleDescription:d="Full access to all tenant management operations",addCreatorToOrganization:r=!0}=s;await a.adapters.organizations.create(e,{id:n.id,name:n.id,display_name:n.friendly_name||n.id});let l=null;if(o&&(l=await Y(a,e,o,i,d)),r&&a.ctx){const f=a.ctx.var.user;if(f!=null&&f.sub)try{await a.adapters.userOrganizations.create(e,{user_id:f.sub,organization_id:n.id}),l&&await a.adapters.userRoles.create(e,f.sub,l,n.id)}catch(u){console.warn(`Failed to add creator ${f.sub} to organization ${n.id}:`,u)}}c&&c.length>0&&console.log(`Would assign roles ${c.join(", ")} to organization ${n.id}`),t&&t.length>0&&console.log(`Would grant permissions ${t.join(", ")} to organization ${n.id}`)}async function Y(a,n,s,e,t){const o=(await a.adapters.roles.list(n,{})).roles.find(l=>l.name===e);if(o)return o.id;const i=await a.adapters.roles.create(n,{name:e,description:t}),d=`${s}api/v2/`,r=S.MANAGEMENT_API_SCOPES.map(l=>({role_id:i.id,resource_server_identifier:d,permission_name:l.value}));return await a.adapters.rolePermissions.assign(n,i.id,r),i.id}async function k(a,n,s){const{accessControl:e,settingsInheritance:t}=s;if(!e)return;const c=await a.adapters.tenants.get(e.mainTenantId);if(!c)return;let o={...c};const i=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const d of i)delete o[d];if(t!=null&&t.inheritedKeys){const d={};for(const r of t.inheritedKeys)r in c&&!i.includes(r)&&(d[r]=c[r]);o=d}if(t!=null&&t.excludedKeys)for(const d of t.excludedKeys)delete o[d];t!=null&&t.transformSettings&&(o=t.transformSettings(o,n.id)),Object.keys(o).length>0&&await a.adapters.tenants.update(n.id,o)}async function O(a,n,s={}){const{cursorField:e="id",sortOrder:t="asc",pageSize:c=100,maxItems:o=1e4,q:i}=s,d=[];let r,l=!0;for(;l;){let f=i||"";if(r){const w=`${e}:${t==="asc"?">":"<"}${r}`;f=f?`(${f}) AND ${w}`:w}const u={per_page:c,page:0,sort:{sort_by:e,sort_order:t},...f&&{q:f}},T=(await a(u))[n]||[];if(T.length===0)l=!1;else{d.push(...T);const g=T[T.length-1];if(g&&typeof g=="object"){const w=g[e];w!=null&&(r=String(w))}T.length<c&&(l=!1),o!==-1&&d.length>=o&&(console.warn(`fetchAll: Reached maxItems limit (${o}). There may be more items.`),l=!1)}}return d}function F(a){const{mainTenantId:n,getChildTenantIds:s,getAdapters:e,shouldSync:t=()=>!0,transformForSync:c}=a;async function o(r,l,f){return(await r.resourceServers.list(l,{q:`identifier:${f}`,per_page:1})).resource_servers[0]??null}async function i(r,l){const f=await s();await Promise.all(f.map(async u=>{try{const m=await e(u),g={...c?c(r,u):{name:r.name,identifier:r.identifier,scopes:r.scopes,signing_alg:r.signing_alg,signing_secret:r.signing_secret,token_lifetime:r.token_lifetime,token_lifetime_for_web:r.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:r.skip_consent_for_verifiable_first_party_clients,allow_offline_access:r.allow_offline_access,verificationKey:r.verificationKey,options:r.options},is_system:!0};if(l==="create"){const w=await o(m,u,r.identifier);w&&w.id?await m.resourceServers.update(u,w.id,g):await m.resourceServers.create(u,g)}else{const w=await o(m,u,r.identifier);w&&w.id&&await m.resourceServers.update(u,w.id,g)}}catch(m){console.error(`Failed to sync resource server "${r.identifier}" to tenant "${u}":`,m)}}))}async function d(r){const l=await s();await Promise.all(l.map(async f=>{try{const u=await e(f),m=await o(u,f,r);m&&m.id&&await u.resourceServers.remove(f,m.id)}catch(u){console.error(`Failed to delete resource server "${r}" from tenant "${f}":`,u)}}))}return{afterCreate:async(r,l)=>{r.tenantId===n&&t(l)&&await i(l,"create")},afterUpdate:async(r,l,f)=>{r.tenantId===n&&t(f)&&await i(f,"update")},afterDelete:async(r,l)=>{r.tenantId===n&&await d(l)}}}function E(a){const{mainTenantId:n,getMainTenantAdapters:s,getAdapters:e,shouldSync:t=()=>!0,transformForSync:c}=a;return{async afterCreate(o,i){if(i.id!==n)try{const d=await s(),r=await e(i.id),l=await O(f=>d.resourceServers.list(n,f),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(l.filter(f=>t(f)).map(async f=>{const u=f;try{const m=c?c(u,i.id):{name:u.name,identifier:u.identifier,scopes:u.scopes,signing_alg:u.signing_alg,signing_secret:u.signing_secret,token_lifetime:u.token_lifetime,token_lifetime_for_web:u.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:u.skip_consent_for_verifiable_first_party_clients,allow_offline_access:u.allow_offline_access,verificationKey:u.verificationKey,options:u.options};await r.resourceServers.create(i.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync resource server "${u.identifier}" to new tenant "${i.id}":`,m)}}))}catch(d){console.error(`Failed to sync resource servers to new tenant "${i.id}":`,d)}}}}var p=class extends Error{constructor(n=500,s){super(s==null?void 0:s.message,{cause:s==null?void 0:s.cause});I(this,"res");I(this,"status");this.res=s==null?void 0:s.res,this.status=n}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function R(a,n){const s=new M.Hono;return s.get("/",async e=>{var f,u;const t=C.auth0QuerySchema.parse(e.req.query()),{page:c,per_page:o,include_totals:i,q:d}=t,r=e.var.user;if(a.accessControl&&(r!=null&&r.sub)){const m=a.accessControl.mainTenantId,g=(await e.env.data.userOrganizations.listUserOrganizations(m,r.sub,{})).organizations.map(h=>h.id);g.includes(m)||g.push(m);const w=await e.env.data.tenants.list({page:c,per_page:o,include_totals:i,q:d}),b=w.tenants.filter(h=>g.includes(h.id));return i?e.json({tenants:b,start:((f=w.totals)==null?void 0:f.start)??0,limit:((u=w.totals)==null?void 0:u.limit)??o,length:b.length}):e.json(b)}const l=await e.env.data.tenants.list({page:c,per_page:o,include_totals:i,q:d});return i?e.json(l):e.json(l.tenants)}),s.get("/:id",async e=>{const t=e.req.param("id");if(a.accessControl){const o=e.var.user,i=a.accessControl.mainTenantId;if(t!==i){if(!(o!=null&&o.sub))throw new p(401,{message:"Authentication required"});if(!(await e.env.data.userOrganizations.listUserOrganizations(i,o.sub,{})).organizations.some(l=>l.id===t))throw new p(403,{message:"Access denied to this tenant"})}}const c=await e.env.data.tenants.get(t);if(!c)throw new p(404,{message:"Tenant not found"});return e.json(c)}),s.post("/",async e=>{var t,c,o;try{const i=e.var.user;if(!(i!=null&&i.sub))throw new p(401,{message:"Authentication required to create tenants"});let d=C.tenantInsertSchema.parse(await e.req.json());const r={adapters:e.env.data,ctx:e};(t=n.tenants)!=null&&t.beforeCreate&&(d=await n.tenants.beforeCreate(r,d));const l=await e.env.data.tenants.create(d);return(c=n.tenants)!=null&&c.afterCreate&&await n.tenants.afterCreate(r,l),e.json(l,201)}catch(i){throw i instanceof G.z.ZodError?new p(400,{message:"Validation error",cause:i}):i instanceof Error&&("code"in i&&i.code==="SQLITE_CONSTRAINT_PRIMARYKEY"||(o=i.message)!=null&&o.includes("UNIQUE constraint failed"))?new p(409,{message:"Tenant with this ID already exists"}):i}}),s.patch("/:id",async e=>{var u,m;const t=e.req.param("id");if(a.accessControl){const T=e.var.user;if(!(T!=null&&T.sub))throw new p(401,{message:"Authentication required to update tenants"});const g=a.accessControl.mainTenantId;if(t!==g&&!(await e.env.data.userOrganizations.listUserOrganizations(g,T.sub,{})).organizations.some(h=>h.id===t))throw new p(403,{message:"Access denied to update this tenant"})}const c=C.tenantInsertSchema.partial().parse(await e.req.json()),{id:o,...i}=c;if(!await e.env.data.tenants.get(t))throw new p(404,{message:"Tenant not found"});const r={adapters:e.env.data,ctx:e};let l=i;(u=n.tenants)!=null&&u.beforeUpdate&&(l=await n.tenants.beforeUpdate(r,t,i)),await e.env.data.tenants.update(t,l);const f=await e.env.data.tenants.get(t);if(!f)throw new p(404,{message:"Tenant not found after update"});return(m=n.tenants)!=null&&m.afterUpdate&&await n.tenants.afterUpdate(r,f),e.json(f)}),s.delete("/:id",async e=>{var i,d;const t=e.req.param("id");if(a.accessControl&&t===a.accessControl.mainTenantId)throw new p(400,{message:"Cannot delete the main tenant"});if(a.accessControl){const r=e.var.user;if(!(r!=null&&r.sub))throw new p(401,{message:"Authentication required to delete tenants"});const l=a.accessControl.mainTenantId;if(!(await e.env.data.userOrganizations.listUserOrganizations(l,r.sub,{})).organizations.some(m=>m.id===t))throw new p(403,{message:"Access denied to delete this tenant"})}if(!await e.env.data.tenants.get(t))throw new p(404,{message:"Tenant not found"});const o={adapters:e.env.data,ctx:e};return(i=n.tenants)!=null&&i.beforeDelete&&await n.tenants.beforeDelete(o,t),await e.env.data.tenants.remove(t),(d=n.tenants)!=null&&d.afterDelete&&await n.tenants.afterDelete(o,t),e.body(null,204)}),s}function Z(a,n){const s=new _.OpenAPIHono;return s.openapi(_.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:C.auth0QuerySchema},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:_.z.object({tenants:_.z.array(C.tenantSchema),start:_.z.number().optional(),limit:_.z.number().optional(),length:_.z.number().optional()})}},description:"List of tenants"}}}),async e=>{var f,u,m,T;const t=e.req.valid("query"),{page:c,per_page:o,include_totals:i,q:d}=t,r=e.var.user;if(a.accessControl&&(r!=null&&r.sub)){const g=a.accessControl.mainTenantId,b=(await O(v=>e.env.data.userOrganizations.listUserOrganizations(g,r.sub,v),"organizations")).map(v=>v.id);b.includes(g)||b.push(g);const h=await e.env.data.tenants.list({page:c,per_page:o,include_totals:i,q:d}),y=h.tenants.filter(v=>b.includes(v.id));return i?e.json({tenants:y,start:((f=h.totals)==null?void 0:f.start)??0,limit:((u=h.totals)==null?void 0:u.limit)??o,length:y.length}):e.json({tenants:y})}const l=await e.env.data.tenants.list({page:c,per_page:o,include_totals:i,q:d});return i?e.json({tenants:l.tenants,start:((m=l.totals)==null?void 0:m.start)??0,limit:((T=l.totals)==null?void 0:T.limit)??o,length:l.tenants.length}):e.json({tenants:l.tenants})}),s.openapi(_.createRoute({tags:["tenants"],method:"get",path:"/{id}",request:{params:_.z.object({id:_.z.string()})},security:[{Bearer:["auth:read"]}],responses:{200:{content:{"application/json":{schema:C.tenantSchema}},description:"Tenant details"},404:{description:"Tenant not found"}}}),async e=>{const{id:t}=e.req.valid("param");if(a.accessControl){const o=e.var.user,i=a.accessControl.mainTenantId;if(t!==i){if(!(o!=null&&o.sub))throw new p(401,{message:"Authentication required"});if(!(await O(l=>e.env.data.userOrganizations.listUserOrganizations(i,o.sub,l),"organizations")).some(l=>l.id===t))throw new p(403,{message:"Access denied to this tenant"})}}const c=await e.env.data.tenants.get(t);if(!c)throw new p(404,{message:"Tenant not found"});return e.json(c)}),s.openapi(_.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:C.tenantInsertSchema}}}},security:[{Bearer:["auth:write"]}],responses:{201:{content:{"application/json":{schema:C.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"}}}),async e=>{var d,r;const t=e.var.user;if(!(t!=null&&t.sub))throw new p(401,{message:"Authentication required to create tenants"});let c=e.req.valid("json");const o={adapters:e.env.data,ctx:e};(d=n.tenants)!=null&&d.beforeCreate&&(c=await n.tenants.beforeCreate(o,c));const i=await e.env.data.tenants.create(c);return(r=n.tenants)!=null&&r.afterCreate&&await n.tenants.afterCreate(o,i),e.json(i,201)}),s.openapi(_.createRoute({tags:["tenants"],method:"patch",path:"/{id}",request:{params:_.z.object({id:_.z.string()}),body:{content:{"application/json":{schema:_.z.object(C.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["auth:write"]}],responses:{200:{content:{"application/json":{schema:C.tenantSchema}},description:"Tenant updated"},403:{description:"Access denied"},404:{description:"Tenant not found"}}}),async e=>{var l,f;const{id:t}=e.req.valid("param");if(a.accessControl){const u=e.var.user,m=a.accessControl.mainTenantId;if(!(u!=null&&u.sub))throw new p(401,{message:"Authentication required"});if(t!==m&&!(await O(w=>e.env.data.userOrganizations.listUserOrganizations(m,u.sub,w),"organizations")).some(w=>w.id===t))throw new p(403,{message:"Access denied to this tenant"})}if(!await e.env.data.tenants.get(t))throw new p(404,{message:"Tenant not found"});const o=e.req.valid("json"),i={adapters:e.env.data,ctx:e};let d=o;(l=n.tenants)!=null&&l.beforeUpdate&&(d=await n.tenants.beforeUpdate(i,t,o)),await e.env.data.tenants.update(t,d);const r=await e.env.data.tenants.get(t);if(!r)throw new p(500,{message:"Failed to retrieve updated tenant"});return(f=n.tenants)!=null&&f.afterUpdate&&await n.tenants.afterUpdate(i,r),e.json(r)}),s.openapi(_.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:_.z.object({id:_.z.string()})},security:[{Bearer:["auth:write"]}],responses:{204:{description:"Tenant deleted"},403:{description:"Access denied or cannot delete main tenant"},404:{description:"Tenant not found"}}}),async e=>{var i,d;const{id:t}=e.req.valid("param");if(a.accessControl){const r=e.var.user,l=a.accessControl.mainTenantId;if(!(r!=null&&r.sub))throw new p(401,{message:"Authentication required"});if(t===l)throw new p(403,{message:"Cannot delete the main tenant"});if(!(await O(m=>e.env.data.userOrganizations.listUserOrganizations(l,r.sub,m),"organizations")).some(m=>m.id===t))throw new p(403,{message:"Access denied to this tenant"})}if(!await e.env.data.tenants.get(t))throw new p(404,{message:"Tenant not found"});const o={adapters:e.env.data,ctx:e};return(i=n.tenants)!=null&&i.beforeDelete&&await n.tenants.beforeDelete(o,t),await e.env.data.tenants.remove(t),(d=n.tenants)!=null&&d.afterDelete&&await n.tenants.afterDelete(o,t),e.body(null,204)}),s}function J(a){const n=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:s,type:e}of n){const t=a.match(s);if(t&&t[1])return{type:e,id:t[1]}}return null}async function X(a,n,s){try{switch(s.type){case"resource_server":{const e=await a.resourceServers.get(n,s.id);return(e==null?void 0:e.is_system)===!0}case"role":{const e=await a.roles.get(n,s.id);return(e==null?void 0:e.is_system)===!0}case"connection":{const e=await a.connections.get(n,s.id);return(e==null?void 0:e.is_system)===!0}default:return!1}}catch{return!1}}function x(a){return{resource_server:"resource server",role:"role",connection:"connection"}[a]}function N(){return async(a,n)=>{if(!["PATCH","PUT","DELETE"].includes(a.req.method))return n();const s=J(a.req.path);if(!s)return n();const e=a.var.tenant_id||a.req.header("x-tenant-id")||a.req.header("tenant-id");if(!e)return n();if(await X(a.env.data,e,s))throw new p(403,{message:`This ${x(s.type)} is a system resource and cannot be modified. Make changes in the main tenant instead.`});return n()}}function K(a){return async(n,s)=>{if(!a.accessControl)return s();const e=n.var.tenant_id,t=n.var.organization_id;if(!e)throw new p(400,{message:"Tenant ID not found in request"});if(!P(t,e,a.accessControl.mainTenantId))throw new p(403,{message:`Access denied to tenant ${e}`});return s()}}function H(a){return async(n,s)=>{if(!a.subdomainRouting)return s();const{baseDomain:e,reservedSubdomains:t=[],resolveSubdomain:c}=a.subdomainRouting,o=n.req.header("host")||"";let i=null;if(o.endsWith(e)){const r=o.slice(0,-(e.length+1));r&&!r.includes(".")&&(i=r)}if(i&&t.includes(i)&&(i=null),!i)return a.accessControl&&n.set("tenant_id",a.accessControl.mainTenantId),s();let d=null;if(c)d=await c(i);else if(a.subdomainRouting.useOrganizations!==!1&&a.accessControl)try{const r=await n.env.data.organizations.get(a.accessControl.mainTenantId,i);r&&(d=r.id)}catch{}if(!d)throw new p(404,{message:`Tenant not found for subdomain: ${i}`});return n.set("tenant_id",d),s()}}function B(a){return async(n,s)=>{if(!a.databaseIsolation)return s();const e=n.var.tenant_id;if(!e)throw new p(400,{message:"Tenant ID not found in request"});try{const t=await a.databaseIsolation.getAdapters(e);n.env.data=t}catch(t){throw console.error(`Failed to resolve database for tenant ${e}:`,t),new p(500,{message:"Failed to resolve tenant database"})}return s()}}function j(a){const n=H(a),s=K(a),e=B(a);return async(t,c)=>(await n(t,async()=>{}),await s(t,async()=>{}),await e(t,async()=>{}),c())}function ee(a){const n=q(a);return{name:"multi-tenancy",middleware:j(a),hooks:n,routes:[{path:"/management",handler:R(a,n)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),a.accessControl&&console.log(` - Access control enabled (main tenant: ${a.accessControl.mainTenantId})`),a.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${a.subdomainRouting.baseDomain})`),a.databaseIsolation&&console.log(" - Database isolation enabled")}}}function q(a){const n=a.accessControl?$(a.accessControl):{},s=a.databaseIsolation?D(a.databaseIsolation):{},e=U(a);return{...n,...s,tenants:e}}function Q(a){const n=new M.Hono,s=q(a);return n.route("/tenants",R(a,s)),n}function te(a){return{hooks:q(a),middleware:j(a),app:Q(a),config:a}}function ae(a){const{mainTenantId:n="main",syncResourceServers:s=!0,multiTenancy:e,entityHooks:t,...c}=a,o={...e,accessControl:{mainTenantId:n,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...e==null?void 0:e.accessControl}},i=q(o);let d,r;s&&(d=F({mainTenantId:n,getChildTenantIds:async()=>(await O(y=>a.dataAdapter.tenants.list(y),"tenants",{cursorField:"id",pageSize:100})).filter(y=>y.id!==n).map(y=>y.id),getAdapters:async h=>a.dataAdapter}),r=E({mainTenantId:n,getMainTenantAdapters:async()=>a.dataAdapter,getAdapters:async h=>a.dataAdapter}));const l=async(h,y,...v)=>{const A=[];if(h)try{await h(...v)}catch(z){A.push(z instanceof Error?z:new Error(String(z)))}if(y)try{await y(...v)}catch(z){A.push(z instanceof Error?z:new Error(String(z)))}if(A.length===1)throw A[0];if(A.length>1)throw new AggregateError(A,`Multiple hook errors: ${A.map(z=>z.message).join("; ")}`)},f={...t,resourceServers:d?{...t==null?void 0:t.resourceServers,afterCreate:async(h,y)=>{var v;await l((v=t==null?void 0:t.resourceServers)==null?void 0:v.afterCreate,d==null?void 0:d.afterCreate,h,y)},afterUpdate:async(h,y,v)=>{var A;await l((A=t==null?void 0:t.resourceServers)==null?void 0:A.afterUpdate,d==null?void 0:d.afterUpdate,h,y,v)},afterDelete:async(h,y)=>{var v;await l((v=t==null?void 0:t.resourceServers)==null?void 0:v.afterDelete,d==null?void 0:d.afterDelete,h,y)}}:t==null?void 0:t.resourceServers,tenants:r?{...t==null?void 0:t.tenants,afterCreate:async(h,y)=>{var v;await l((v=t==null?void 0:t.tenants)==null?void 0:v.afterCreate,r==null?void 0:r.afterCreate,h,y)}}:t==null?void 0:t.tenants},u=Z(o,i),m=S.init({...c,entityHooks:f,managementApiExtensions:[...c.managementApiExtensions||[],{path:"/tenants",router:u}]}),{app:T,managementApp:g,...w}=m,b=new M.Hono;return b.use("/api/v2/*",N()),b.route("/",T),{app:b,managementApp:g,...w,multiTenancyConfig:o,multiTenancyHooks:i}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>S.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>S.seed});exports.createAccessControlHooks=$;exports.createAccessControlMiddleware=K;exports.createDatabaseHooks=D;exports.createDatabaseMiddleware=B;exports.createMultiTenancy=Q;exports.createMultiTenancyHooks=q;exports.createMultiTenancyMiddleware=j;exports.createMultiTenancyPlugin=ee;exports.createProtectSyncedMiddleware=N;exports.createProvisioningHooks=U;exports.createResourceServerSyncHooks=F;exports.createSubdomainMiddleware=H;exports.createTenantResourceServerSyncHooks=E;exports.createTenantsRouter=R;exports.fetchAll=O;exports.init=ae;exports.setupMultiTenancy=te;exports.validateTenantAccess=P;
|
package/dist/multi-tenancy.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
-
import { z } from '@hono/zod-openapi';
|
|
3
|
+
import { OpenAPIHono, z } from '@hono/zod-openapi';
|
|
4
4
|
import { Context, Hono, MiddlewareHandler } from 'hono';
|
|
5
5
|
import { FC } from 'hono/jsx';
|
|
6
6
|
import { CountryCode } from 'libphonenumber-js';
|
|
@@ -9543,6 +9543,22 @@ export interface EntityHooksConfig {
|
|
|
9543
9543
|
connections?: EntityHooks<Connection, ConnectionInsert>;
|
|
9544
9544
|
tenants?: EntityHooks<Tenant, CreateTenantParams>;
|
|
9545
9545
|
}
|
|
9546
|
+
/**
|
|
9547
|
+
* Route extension for the management API.
|
|
9548
|
+
*
|
|
9549
|
+
* Allows registering additional OpenAPI routes that go through the full
|
|
9550
|
+
* middleware chain (caching, tenant resolution, auth, entity hooks).
|
|
9551
|
+
*/
|
|
9552
|
+
export interface ManagementApiExtension {
|
|
9553
|
+
/** The path prefix for the routes (e.g., "/tenants") */
|
|
9554
|
+
path: string;
|
|
9555
|
+
/**
|
|
9556
|
+
* The OpenAPI router to mount at the path.
|
|
9557
|
+
* Use `any` to allow routers with extended Bindings/Variables types
|
|
9558
|
+
* (e.g., from multi-tenancy package).
|
|
9559
|
+
*/
|
|
9560
|
+
router: OpenAPIHono<any, any, any>;
|
|
9561
|
+
}
|
|
9546
9562
|
export interface AuthHeroConfig {
|
|
9547
9563
|
dataAdapter: DataAdapters;
|
|
9548
9564
|
allowedOrigins?: string[];
|
|
@@ -9562,6 +9578,31 @@ export interface AuthHeroConfig {
|
|
|
9562
9578
|
* Use these to implement cross-tenant sync, audit logging, webhooks, etc.
|
|
9563
9579
|
*/
|
|
9564
9580
|
entityHooks?: EntityHooksConfig;
|
|
9581
|
+
/**
|
|
9582
|
+
* Additional routes to mount on the management API.
|
|
9583
|
+
*
|
|
9584
|
+
* These routes go through the full middleware chain:
|
|
9585
|
+
* - CORS
|
|
9586
|
+
* - Data hooks & caching
|
|
9587
|
+
* - Client info extraction
|
|
9588
|
+
* - Tenant resolution
|
|
9589
|
+
* - Authentication (reads OpenAPI security definitions)
|
|
9590
|
+
* - Entity hooks
|
|
9591
|
+
*
|
|
9592
|
+
* @example
|
|
9593
|
+
* ```typescript
|
|
9594
|
+
* import { init } from "authhero";
|
|
9595
|
+
* import { createTenantsOpenAPIRouter } from "@authhero/multi-tenancy";
|
|
9596
|
+
*
|
|
9597
|
+
* const { app } = init({
|
|
9598
|
+
* dataAdapter,
|
|
9599
|
+
* managementApiExtensions: [
|
|
9600
|
+
* { path: "/tenants", router: createTenantsOpenAPIRouter(config, hooks) }
|
|
9601
|
+
* ]
|
|
9602
|
+
* });
|
|
9603
|
+
* ```
|
|
9604
|
+
*/
|
|
9605
|
+
managementApiExtensions?: ManagementApiExtension[];
|
|
9565
9606
|
}
|
|
9566
9607
|
export type SendEmailParams = {
|
|
9567
9608
|
emailProvider: EmailProvider;
|
|
@@ -9684,9 +9725,17 @@ export interface SeedOptions {
|
|
|
9684
9725
|
*/
|
|
9685
9726
|
tenantName?: string;
|
|
9686
9727
|
/**
|
|
9687
|
-
* The audience URL for the tenant
|
|
9728
|
+
* The audience URL for the tenant.
|
|
9729
|
+
* For the main/management tenant, defaults to `urn:authhero:management`.
|
|
9730
|
+
* For child tenants, use `getTenantAudience(tenantId)` to generate `urn:authhero:tenant:{tenantId}`.
|
|
9688
9731
|
*/
|
|
9689
9732
|
audience?: string;
|
|
9733
|
+
/**
|
|
9734
|
+
* Whether this is the main/management tenant.
|
|
9735
|
+
* If true, the audience will default to `urn:authhero:management`.
|
|
9736
|
+
* @default true
|
|
9737
|
+
*/
|
|
9738
|
+
isMainTenant?: boolean;
|
|
9690
9739
|
/**
|
|
9691
9740
|
* The default client ID (defaults to "default")
|
|
9692
9741
|
*/
|
|
@@ -19331,6 +19380,7 @@ export interface DatabaseFactory {
|
|
|
19331
19380
|
* Creates hooks for tenant provisioning and deprovisioning.
|
|
19332
19381
|
*
|
|
19333
19382
|
* This handles:
|
|
19383
|
+
* - Setting the correct audience for new tenants (urn:authhero:tenant:{id})
|
|
19334
19384
|
* - Creating organizations on the main tenant when a new tenant is created
|
|
19335
19385
|
* - Provisioning databases for new tenants
|
|
19336
19386
|
* - Cleaning up organizations and databases when tenants are deleted
|
|
@@ -19834,7 +19884,7 @@ export declare function setupMultiTenancy(config: MultiTenancyConfig): {
|
|
|
19834
19884
|
/**
|
|
19835
19885
|
* Configuration for multi-tenant AuthHero initialization.
|
|
19836
19886
|
*/
|
|
19837
|
-
export interface MultiTenantAuthHeroConfig extends Omit<AuthHeroConfig, "entityHooks"> {
|
|
19887
|
+
export interface MultiTenantAuthHeroConfig extends Omit<AuthHeroConfig, "entityHooks" | "managementApiExtensions"> {
|
|
19838
19888
|
/**
|
|
19839
19889
|
* The main tenant ID that manages all other tenants.
|
|
19840
19890
|
* This tenant can create, update, and delete other tenants.
|
|
@@ -19859,6 +19909,11 @@ export interface MultiTenantAuthHeroConfig extends Omit<AuthHeroConfig, "entityH
|
|
|
19859
19909
|
* Resource server and tenant hooks will be merged with the sync hooks.
|
|
19860
19910
|
*/
|
|
19861
19911
|
entityHooks?: AuthHeroConfig["entityHooks"];
|
|
19912
|
+
/**
|
|
19913
|
+
* Additional routes to mount on the management API.
|
|
19914
|
+
* Note: The tenant CRUD routes are automatically added by multi-tenancy.
|
|
19915
|
+
*/
|
|
19916
|
+
managementApiExtensions?: ManagementApiExtension[];
|
|
19862
19917
|
}
|
|
19863
19918
|
/**
|
|
19864
19919
|
* Initializes a multi-tenant AuthHero server.
|
|
@@ -20385,6 +20440,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20385
20440
|
grant_type: "client_credentials";
|
|
20386
20441
|
scope?: string | undefined;
|
|
20387
20442
|
audience?: string | undefined;
|
|
20443
|
+
organization?: string | undefined;
|
|
20388
20444
|
client_id?: string | undefined;
|
|
20389
20445
|
client_secret?: string | undefined;
|
|
20390
20446
|
} | {
|
|
@@ -20419,6 +20475,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20419
20475
|
grant_type: "client_credentials";
|
|
20420
20476
|
scope?: string | undefined;
|
|
20421
20477
|
audience?: string | undefined;
|
|
20478
|
+
organization?: string | undefined;
|
|
20422
20479
|
client_id?: string | undefined;
|
|
20423
20480
|
client_secret?: string | undefined;
|
|
20424
20481
|
} | {
|
|
@@ -20458,6 +20515,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20458
20515
|
grant_type: "client_credentials";
|
|
20459
20516
|
scope?: string | undefined;
|
|
20460
20517
|
audience?: string | undefined;
|
|
20518
|
+
organization?: string | undefined;
|
|
20461
20519
|
client_id?: string | undefined;
|
|
20462
20520
|
client_secret?: string | undefined;
|
|
20463
20521
|
} | {
|
|
@@ -20492,6 +20550,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20492
20550
|
grant_type: "client_credentials";
|
|
20493
20551
|
scope?: string | undefined;
|
|
20494
20552
|
audience?: string | undefined;
|
|
20553
|
+
organization?: string | undefined;
|
|
20495
20554
|
client_id?: string | undefined;
|
|
20496
20555
|
client_secret?: string | undefined;
|
|
20497
20556
|
} | {
|
|
@@ -20539,6 +20598,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20539
20598
|
grant_type: "client_credentials";
|
|
20540
20599
|
scope?: string | undefined;
|
|
20541
20600
|
audience?: string | undefined;
|
|
20601
|
+
organization?: string | undefined;
|
|
20542
20602
|
client_id?: string | undefined;
|
|
20543
20603
|
client_secret?: string | undefined;
|
|
20544
20604
|
} | {
|
|
@@ -20573,6 +20633,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20573
20633
|
grant_type: "client_credentials";
|
|
20574
20634
|
scope?: string | undefined;
|
|
20575
20635
|
audience?: string | undefined;
|
|
20636
|
+
organization?: string | undefined;
|
|
20576
20637
|
client_id?: string | undefined;
|
|
20577
20638
|
client_secret?: string | undefined;
|
|
20578
20639
|
} | {
|
|
@@ -20615,6 +20676,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20615
20676
|
grant_type: "client_credentials";
|
|
20616
20677
|
scope?: string | undefined;
|
|
20617
20678
|
audience?: string | undefined;
|
|
20679
|
+
organization?: string | undefined;
|
|
20618
20680
|
client_id?: string | undefined;
|
|
20619
20681
|
client_secret?: string | undefined;
|
|
20620
20682
|
} | {
|
|
@@ -20649,6 +20711,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20649
20711
|
grant_type: "client_credentials";
|
|
20650
20712
|
scope?: string | undefined;
|
|
20651
20713
|
audience?: string | undefined;
|
|
20714
|
+
organization?: string | undefined;
|
|
20652
20715
|
client_id?: string | undefined;
|
|
20653
20716
|
client_secret?: string | undefined;
|
|
20654
20717
|
} | {
|
|
@@ -20691,6 +20754,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20691
20754
|
grant_type: "client_credentials";
|
|
20692
20755
|
scope?: string | undefined;
|
|
20693
20756
|
audience?: string | undefined;
|
|
20757
|
+
organization?: string | undefined;
|
|
20694
20758
|
client_id?: string | undefined;
|
|
20695
20759
|
client_secret?: string | undefined;
|
|
20696
20760
|
} | {
|
|
@@ -20725,6 +20789,7 @@ export declare function init(config: MultiTenantAuthHeroConfig): {
|
|
|
20725
20789
|
grant_type: "client_credentials";
|
|
20726
20790
|
scope?: string | undefined;
|
|
20727
20791
|
audience?: string | undefined;
|
|
20792
|
+
organization?: string | undefined;
|
|
20728
20793
|
client_id?: string | undefined;
|
|
20729
20794
|
client_secret?: string | undefined;
|
|
20730
20795
|
} | {
|