identifiabl

user-scoped identity and authentication for llm and agentic apps (apps sdk + mcp)

until now, most llm systems have relied on shared api keys — not real user identity.

the shift toward agentic systems, personal data, and enterprise governance requires every model call to be tied to a verified user, tenant, and scope.

identifiabl defines this layer.

at a glance

identifiabl is a user-scoped identity and authentication gateway for llm apps.
it lets you:

📦 implementation: ai-auth-gateway (published)

why now?

as organizations adopt agentic systems and user-specific ai workflows, identity, policy, and governance become mandatory. shared api keys cannot support enterprise-grade access control or compliance.

a new layer is required — the user-scoped trust and governance gateway.

it sits between applications (or agents) and model providers, ensuring that every request is authenticated, authorized, observable, and governed.

gatewaystack defines this layer. identifiabl is the user-scoped identity and authentication module.

example use cases

a healthcare saas with 10,000+ doctors needs to ensure every ai-assisted diagnosis is tied to the licensed physician who requested it, with full audit trails for hipaa and internal review.

today, most of their ai calls run through a shared openai key:

with identifiabl in front of the model provider:

the result: every ai diagnosis is user-bound, tenant-aware, and fully auditable, without the app having to manually thread identity through every model call.

a global enterprise rolls out an internal copilot that can search confluence, jira, google drive, and internal apis. employees authenticate with sso (okta / entra / auth0), but today the copilot usually calls the llm and tools with a shared api key.

with identifiabl, every request is bound to a specific employee:

this lets security teams enforce “only finance analysts can run this tool”, “legal can see these repositories but not those”, and keep a full identity-level audit trail for every copilot interaction.

a multi-tenant saas platform offers ai features across free, pro, and enterprise tiers. today, most of the ai usage runs through a single openai key per environment — making it impossible to answer basic questions like:

with identifiabl, each model call is tied to a user and tenant:

this turns “one big shared key” into per-tenant, per-user accountability without changing your app’s business logic.

competitive landscape

today, teams solve this problem in a few different ways:

identifiabl is different:

you can still run it alongside traditional api gateways — identifiabl is the user-scoped identity slice of the stack.

designing user identity and authentication for ai

identifiabl is responsible for establishing the trust chain at the very start of every model call.

it validates identity tokens, extracts normalized user metadata, and binds that identity to all downstream governance modules.

within the shared requestcontext

all gatewaystack modules operate on a shared RequestContext object.

identifiabl is responsible for:

the identity object becomes the foundation for all downstream governance decisions in transformabl, validatabl, limitabl, proxyabl, and explicabl.

what identifiabl does

identity becomes a piece of runtime metadata that other gatewaystack modules rely on — policy checks (validatabl), routing (proxyabl), cost/budget enforcement (limitabl), and full audit trails (explicabl) all start from this canonical identity object.

identifiabl works with

the core functions

1. verifyToken — verify oidc / apps sdk identity tokens
validates rs256 jwts, audiences, issuers, expirations, and nonce.

a minimal typescript implementation:

// auth/verifyToken.ts
import { createRemoteJWKSet, jwtVerify, JWTPayload } from 'jose';

const ISSUER = process.env.AUTH_ISSUER!;
const AUDIENCE = process.env.AUTH_AUDIENCE!;
const JWKS_URI = process.env.AUTH_JWKS_URI!;

const jwks = createRemoteJWKSet(new URL(JWKS_URI));

export type VerifiedIdentity = {
  user_id: string;
  org_id?: string;
  tenant?: string;
  roles: string[];
  scopes: string[];
  raw: JWTPayload;
};

export async function verifyToken(authorizationHeader: string | undefined): Promise<VerifiedIdentity> {
  if (!authorizationHeader?.startsWith('Bearer ')) {
    throw new Error('missing or invalid bearer token');
  }

  const token = authorizationHeader.slice('Bearer '.length).trim();

  const { payload } = await jwtVerify(token, jwks, {
    issuer: ISSUER,
    audience: AUDIENCE,
  });

  return extractIdentity(payload);
}

function extractIdentity(payload: JWTPayload): VerifiedIdentity {
  const scopes =
    typeof payload.scope === 'string'
      ? payload.scope.split(' ').filter(Boolean)
      : Array.isArray(payload.scope)
      ? payload.scope.map(String)
      : [];

  return {
    user_id: String(payload.sub ?? ''),
    org_id: (payload['org_id'] as string) ?? undefined,
    tenant: (payload['tenant'] as string) ?? undefined,
    roles: (payload['roles'] as string[]) ?? [],
    scopes,
    raw: payload,
  };
}

2. extractIdentity — normalize user/org/tenant metadata
implemented inside verifyToken above, it returns a canonical structure:

{ user_id, org_id, tenant, roles, scopes }

3. attachIdentity — bind identity to model request metadata
injects identity into headers or context fields for downstream modules.

// middleware/attachIdentity.ts
import type { Request, Response, NextFunction } from 'express';
import { verifyToken, VerifiedIdentity } from '../auth/verifyToken';

declare module 'express-serve-static-core' {
  interface Request {
    identity?: VerifiedIdentity;
  }
}

export async function attachIdentity(req: Request, res: Response, next: NextFunction) {
  try {
    const authHeader = req.headers['authorization'] as string | undefined;

    const identity = await verifyToken(authHeader);

    // attach to request for downstream handlers / modules
    req.identity = identity;

    // inject normalized identity headers for downstream services / proxies
    req.headers['x-user-id'] = identity.user_id;
    if (identity.org_id) req.headers['x-org-id'] = identity.org_id;
    if (identity.tenant) req.headers['x-tenant'] = identity.tenant;
    req.headers['x-user-scopes'] = identity.scopes.join(' ');

    return next();
  } catch (err) {
    return res.status(401).json({
      error: 'unauthorized',
      reason: (err as Error).message ?? 'token validation failed',
    });
  }
}

then wire it up in your gateway server:

import express from 'express';
import { attachIdentity } from './middleware/attachIdentity';

const app = express();

app.use(attachIdentity);

// all downstream routes now see req.identity and identity headers

4. assertIdentity — enforce presence of user identity
guarantees that no anonymous or shared-key requests pass through.

// middleware/assertIdentity.ts
import type { Request, Response, NextFunction } from 'express';

export function assertIdentity(req: Request, res: Response, next: NextFunction) {
  if (!req.identity) {
    return res.status(401).json({ error: 'unauthorized', reason: 'missing user identity' });
  }
  return next();
}

5. logIdentity — produce identity-level audit events
emits structured logs for compliance, analytics, and debugging.

// inside your request pipeline
logger.info('identity_event', {
  user_id: req.identity?.user_id,
  org_id: req.identity?.org_id,
  scopes: req.identity?.scopes,
  path: req.path,
  action: 'model_request',
});

end to end flow

user
   → identifiabl       (who is calling?)
   → transformabl      (prepare, clean, classify, anonymize)
   → validatabl        (is this allowed?)
   → limitabl          (how much can they use? pre-flight constraints)
   → proxyabl          (where does it go? execute)
   → llm provider      (model call)
   → [limitabl]        (deduct actual usage, update quotas/budgets)
   → explicabl         (what happened?)
   → response

each module intercepts the request, adds or checks metadata, and guarantees that the call is:

identified, transformed, validated, constrained, routed, and audited.

this is the foundation of user-scoped ai.

integrates with your existing stack

identifiabl plugs into gatewaystack and your existing llm stack without requiring application-level changes. it exposes http middleware and sdk hooks for:

getting started

for implementation details:
ai-auth-gateway quickstart
auth0 setup guide
integration examples

want to explore the full gatewaystack architecture?
view the gatewaystack github repo

want to contact us for enterprise deployments?
reducibl applied ai studio

app / agent
chat ui · internal tool · agent runtime
gatewaystack
user-scoped trust & governance gateway
identifiabl transformabl validatabl limitabl proxyabl explicabl
llm providers
openai · anthropic · internal models

every request flows from your app through gatewaystack's modules before it reaches an llm provider — identified, transformed, validated, constrained, routed, and audited.