Use case
Auth for B2B SaaS with organizations
B2B buyers ask the same questions in the same order: can our people sign in with our identity provider, can IT provision and remove them, and can one customer ever see another's data. This page maps those questions to theAuth.
The problem
Individual accounts stop working at the first company deal.
A user table with a role column covers a consumer app. A company customer wants members grouped under an organization, roles that differ per organization, sign-in through its own identity provider, and automatic deprovisioning when someone leaves.
Building that by hand takes longer than the first sale justifies, and the security review comes right after. Using a library you can read and run yourself keeps the data in your database and the review in your hands.
The theAuth path
Four building blocks, one library.
- Organizations and RBACorg config
- Create organizations, invite members, and check permissions. Four roles ship by default (
owner,admin,member,viewer) and you can add custom roles. - SSOSAML 2.0, OIDC
- SAML and OIDC connections link an organization to an identity provider and route by email domain, with just-in-time provisioning. Setup is in the SSO docs.
- SCIMscim plugin
- A SCIM 2.0 endpoint for users and groups, authenticated with a bearer token your customer's identity provider holds.
- Tenant taggingauth.tenant
- Tenants carry their own settings, and agents can be created with a
tenantIdand listed by it. theAuth does not check that boundary when it authorizes an action, so enforce tenant scoping in your own resource checks. - Self-hostingMIT
- theAuth is MIT licensed and runs on Postgres, MySQL, SQLite or Cloudflare D1 in the TypeScript core. Your customers' identities stay in your database.
Step by step
Organizations, SCIM and tenants in one setup.
Excerpts, checked against the source. SSO connection details depend on your identity provider, so they live in the docs.
-
Turn on organizations
The
orgconfig createsauth.org. It isnullwhen you leave the config out. Limits on members, organizations per user and invitation rate have defaults you can change.import { createTheAuth } from "@glinr/theauth"; export const auth = await createTheAuth({ database: { provider: "postgres", url: process.env.DATABASE_URL }, org: { maxMembers: 100, maxOrgsPerUser: 5, allowCustomRoles: true, }, }); -
Create an organization, invite, and check permissions
The creator becomes
owner. Check a permission at the point where the action happens, not just in the UI.const org = await auth.org.create({ name: "Acme Corp", slug: "acme-corp", ownerId: "user_abc", }); await auth.org.invite({ orgId: org.id, email: "alice@acme.example", role: "admin", invitedBy: "user_abc", }); const allowed = await auth.org.hasPermission(org.id, "user_xyz", "agents:create"); await auth.org.createRole(org.id, { name: "billing", permissions: ["invoices:read", "invoices:pay"], }); -
Accept SCIM provisioning
Add the plugin and give your customer's IT team the SCIM base URL and token. Use a long random secret.
Provisioning and deprovisioning behavior is configurable:
autoCreateUsersandautoDeactivateUsersboth default to true.import { scim } from "@glinr/theauth/auth"; export const auth = await createTheAuth({ database: { provider: "postgres", url: process.env.DATABASE_URL }, plugins: [scim({ bearerToken: process.env.SCIM_TOKEN! })], }); -
Isolate tenants
Slugs must be lowercase letters, numbers and hyphens, and unique. Pass
tenantIdwhen you create an agent and its authorization checks stay inside that tenant.const tenant = await auth.tenant.create({ name: "Acme Corp", slug: "acme", settings: { maxAgents: 200, auditRetentionDays: 365 }, }); const bot = await auth.agent.create({ ownerId: "user-456", name: "acme-data-bot", type: "autonomous", tenantId: tenant.id, permissions: [{ resource: "reports:*", actions: ["read", "export"] }], });
Checked against the package source for @glinr/theauth 0.5.0 on 2026-10-06. Organizations, tenants and SSO are separate modules in the library today, and the docs describe them side by side. Decide early how your own customer model maps onto organizations and tenants, since theAuth does not choose that for you. See multi-tenant and GDPR in the docs for export, deletion and anonymization.
Pitfalls
Where B2B auth projects slip.
- Treating organization and tenant as one thing.An organization groups members and roles. A tenant is a boundary for agents, settings and budget policies. Pick which one your customer maps to and be consistent.
- Checking roles only in the interface.Hide the button and also call
hasPermissionon the server route. - Skipping domain routing for SSO.SSO connections route by email domain. Verify that a customer owns the domain before you attach it to a connection.
- A shared SCIM token.Use a separate long random token per customer connection where you can, and rotate it when the IT contact changes.
- Forgetting offboarding.Test the deprovisioning path in staging with the customer's identity provider before launch. That is the flow they will test first.
- Assuming defaults fit.The default caps are 100 members per organization and 5 organizations per user. Raise them deliberately.
Keep reading
Get started
Give your first agent an identity.
Install the package, create an agent with scoped permissions, and read its first audit record. The core runs on Postgres, SQLite, MySQL or D1, and the Go module needs a single go get.
npm install @glinr/theauthgo get github.com/glincker/theauth-go