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 tenantId and 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.
SSO connections link an organization to an identity provider over SAML 2.0 or OIDC, and a SCIM 2.0 endpoint takes users and groups from the customer's directory. Agents can carry a tenantId so you can list them by tenant; enforcing tenant boundaries stays in your own resource checks.

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.

  1. Turn on organizations

    The org config creates auth.org. It is null when you leave the config out. Limits on members, organizations per user and invitation rate have defaults you can change.

    auth.ts
    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,
      },
    });
  2. 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.

    orgs.ts
    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"],
    });
  3. 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: autoCreateUsers and autoDeactivateUsers both default to true.

    auth.ts
    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! })],
    });
  4. Isolate tenants

    Slugs must be lowercase letters, numbers and hyphens, and unique. Pass tenantId when you create an agent and its authorization checks stay inside that tenant.

    tenants.ts
    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 hasPermission on 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.

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/theauth
  • go get github.com/glincker/theauth-go
Or skip hosting with theAuth Cloud