Skip to content

Multi-Tenant Mapping

The user_id column on {prefix}accounts links an auth account to your own user record without forcing you into a new user table.

Why

  • You already have a users or tenants table
  • You want auth accounts to reference your existing IDs
  • You don't want to migrate to a different schema

How

Pass your user ID as the third argument to register:

const user = await db.insert(users).values({ email, tenant_id }).returning();
const account = await req.auth.register(email, password, user.id);

If you omit the user ID, a UUID is generated automatically:

const account = await req.auth.register(email, password);
// account.user_id is a UUID like "550e8400-e29b-41d4-a716-446655440000"

With OAuth

The createUser hook in AuthConfig runs when a new OAuth user signs in and no matching account exists. Create your user record and return the ID:

const authConfig = {
  db: pool,
  createUser: async (userData) => {
    const user = await db.insert(users).values({
      email: userData.email,
      name: userData.name,
      tenant_id: defaultTenantId,
    }).returning();
    return user.id;
  },
};

The returned ID is stored as user_id on the auth account. If you don't provide createUser, a UUID is generated.

Example: Tenant-Scoped Users

-- your schema
CREATE TABLE tenants (id uuid PRIMARY KEY, name text);
CREATE TABLE users (id uuid PRIMARY KEY, tenant_id uuid REFERENCES tenants(id), email text UNIQUE);
app.post("/register", async (req, res) => {
  const user = await db.insert(users).values({
    email: req.body.email,
    tenant_id: req.body.tenantId,
  }).returning();

  const account = await req.auth.register(req.body.email, req.body.password, user.id);
  res.json({ account });
});

Auth handles sessions, roles, and MFA. Your application handles tenant scoping, permissions, and business logic. The user_id is the bridge between the two.