WorkOS Agent Auth 2026: 12ms Scoped AI Agent Tokens
Vol. 01 — 2026

WorkOS Agent Auth 2026: 12ms Scoped AI Agent Tokens

WorkOS Agent Auth 2026: 12ms Scoped AI Agent Tokens

Quick Answer: WorkOS Agent Auth, launched in early access on 2 September 2026, gives each AI agent its own identity and short-lived scoped tokens. A blueprint defines the permission ceiling, and every run receives the intersection of the agent's scope and the invoking user's scope. My middleware verifies those tokens in 12ms P95 on a 4GB VPS.

Author: Deepak Bagada — founder of SaaS Next in Junagadh, Gujarat. I build agents that write to CRMs, send messages, and read invoices for Indian SMEs. Every one of those agents needs a credential story before it needs another prompt.

Key Takeaways

  • Agents should not borrow sessions. Handing an agent a user's access token gives it every permission the user has, including permissions the task never needed.
  • Blueprints define the ceiling. WorkOS Agent Auth stores allowed permissions, invocable roles, and session lifetimes in a reusable blueprint, then mints a token per run.
  • The token carries delegation. The act claim identifies the user who started the run, while sub identifies the agent. That pair is what makes an audit trail possible.
  • Verification is cheap. With a cached JWKS and local signature checks, a Laravel or Node middleware validates the token in 12ms P95. The bottleneck is revocation propagation, not cryptography.

The Three Bad Credential Shapes

Before Agent Auth, most teams picked one of three options. Each one fails in a different place, and I have audited production systems that used all three.

Credential Bounded by Audit trail Failure I have seen
User-scoped API key The user's full permission set Good, the user is known Agent reads payroll because the user can
Org-scoped key with narrowed scopes The selected scopes Weak, no user principal Nobody knows who started a bad run
M2M application token The client's own grants None for human-triggered runs Shared by six services, rotated never
Agent Auth blueprint token Agent ceiling plus user scope Agent, user, org, scopes Requires a token-minting backend

The first row is the most common mistake. A founder tests an agent with their own admin token, the demo works, and the token ships to production. Three months later the agent has a tool that can export every customer record because the founder could export every customer record.

WorkOS closes that gap with two boundaries. The blueprint sets what the agent may ever do. The user's delegated token sets what this specific run may do. The minted token gets the intersection.

Creating a Blueprint

A blueprint is a reusable agent definition. It holds a permission ceiling, the roles allowed to invoke it, and the session lifetime. The API call is server-side and uses your WorkOS secret key, never the agent's credential.

# terminal — create the blueprint once per agent type
curl -X POST https://api.workos.com/agents/blueprints \
  -H "Authorization: Bearer $WORKOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Invoice Reconciliation Agent",
    "description": "Reads invoices and writes reconciliation notes.",
    "permissions": ["invoices:read", "notes:write"],
    "invocable_by": {
      "role_slugs": ["finance_manager"],
      "organization_ids": []
    },
    "session_lifetime_minutes": 15
  }'

The response includes a blueprint ID such as agent_blueprint_01EHWNCE74X7JSDV0X3SZ3KJNY. Store that in your application config, not in the agent prompt. The agent should never know its own ceiling; the backend enforces it.

Two details matter in production. First, keep permissions genuinely narrow. invoices:read is better than billing:* because a typo in a tool name cannot widen it. Second, set the session lifetime to the length of one task, not one workday. Fifteen minutes is enough for a reconciliation run and useless for a stolen token at 02:00.

Minting a Token Per Run

Your backend exchanges the user's access token for an agent token. The agent receives only the short-lived result. This is the part teams get wrong: they mint once at deploy time and reuse the token for every run, which defeats the whole model.

// app/api/agents/reconcile/route.ts
import { WorkOS } from "@workos-inc/node";

const workos = new WorkOS(process.env.WORKOS_API_KEY!);

export async function POST(request: Request) {
  const userToken = request.headers.get("authorization")?.replace("Bearer ", "");
  if (!userToken) {
    return Response.json({ error: "missing user token" }, { status: 401 });
  }

  const minted = await workos.agents.mintToken({
    blueprintId: process.env.INVOICE_AGENT_BLUEPRINT_ID!,
    type: "user_delegated",
    userAccessToken: userToken,
    intent: `reconcile-${crypto.randomUUID()}`,
  });

  return Response.json({
    agentToken: minted.accessToken,
    expiresAt: minted.expiresAt,
    scopes: minted.scopes,
  });
}

The intent field is worth more than it looks. When a reconciliation run goes wrong, the intent string tells you which business action the agent was attempting. I log it next to the token ID and the user ID, so a support ticket becomes a three-column lookup instead of a log archaeology project.

What the Token Actually Contains

The claims are the audit trail. I decode the token in staging and assert these fields in tests before shipping an agent.

Claim Example Why it matters
iss https://api.workos.com Rejects tokens from another environment
aud Your client ID or resource URI Prevents cross-app token reuse
sub agent_reg_01ABC... Identifies the agent, not the user
org_id org_01XYZ... Scopes the run to one tenant
scope invoices:read notes:write The effective permissions for this run
act {"sub":"user_01DEF..."} The human who authorized the run
jti Unique token ID Enables a deny list for instant revocation
exp 15 minutes after issue Caps the damage from a leaked token

The act claim follows the RFC 8693 delegation pattern. If your logs only store sub, you know an agent acted but not for whom. That is not enough for a client dispute.

Verifying in 12ms P95

Verification has two costs: fetching the signing keys and checking the signature. Cache the keys and the first cost disappears for almost every request. I measured 1,000 sequential verifications against a ₹2,400 VPS with 4GB RAM and a warm JWKS cache.

Step P50 P95 Notes
JWKS fetch (cold) 84ms 141ms Happens once per hour
Signature verify 0.9ms 3.1ms RS256, 2048-bit key
Claim checks 0.2ms 0.6ms Scope, audience, expiry
Revocation lookup 2.1ms 7.9ms Valkey GET on jti
Total warm path 3.4ms 12.0ms Includes middleware overhead

The revocation lookup dominates the warm path. You can skip it for read-only tools and accept a 15-minute revocation lag, or keep it for write tools and pay 8ms. I keep it for writes. A write that should have been revoked is the failure that ends a client relationship.

<?php
// app/Http/Middleware/VerifyAgentToken.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use RuntimeException;

final class VerifyAgentToken
{
    public function handle(Request $request, Closure $next)
    {
        $token = $request->bearerToken();
        if (!$token) {
            return response()->json(['error' => 'missing token'], 401);
        }

        $claims = $this->decode($token);

        if (($claims['aud'] ?? null) !== config('services.workos.client_id')) {
            return response()->json(['error' => 'wrong audience'], 401);
        }

        if (($claims['exp'] ?? 0) < time()) {
            return response()->json(['error' => 'expired'], 401);
        }

        if (Cache::has('agent:revoked:'.$claims['jti'])) {
            return response()->json(['error' => 'revoked'], 401);
        }

        $needed = $request->route()->getAction('agent_scope');
        if ($needed && !in_array($needed, explode(' ', $claims['scope'] ?? ''), true)) {
            return response()->json(['error' => 'insufficient scope'], 403);
        }

        $request->attributes->set('agent', $claims);
        return $next($request);
    }

    private function decode(string $token): array
    {
        [, $payload] = explode('.', $token);
        $json = base64_decode(strtr($payload, '-_', '+/'));
        $claims = json_decode($json, true);

        if (!is_array($claims)) {
            throw new RuntimeException('Malformed token payload');
        }

        return $claims;
    }
}

The decode function above is intentionally incomplete: it reads claims but does not verify the signature. In production, use the WorkOS SDK or a JWT library with the cached JWKS. I left the signature step out of the snippet because a copied half-implementation is worse than no implementation.

Production War Story: The Agent With Admin Rights

In June 2026 I inherited a support agent for a Rajkot distributor. The previous developer had stored the owner's personal access token in an environment variable. The agent could read orders, issue refunds, and change credit limits. It had never used the refund or credit tools, but the capability was there.

The audit that mattered was not a code review. It was a question: if this agent is prompt-injected through a customer email, what is the worst thing it can do? The answer was "refund every open order". We moved the agent to a blueprint with orders:read and notes:write, dropped the refund tool entirely, and added a human approval step for credit changes.

I deployed the replacement middleware in two days, most of it spent rewriting the tool adapters to accept a token instead of a global key. The agent's behaviour did not change. The blast radius did.

Production War Story: The Revocation That Arrived Late

A global client revoked an employee's access at 11:04. The employee's agent kept working until 11:19 because the token had a 15-minute lifetime and my middleware only checked expiry. Nothing was damaged, but the client's security team asked a fair question: why did a terminated session keep writing notes?

The fix was a jti deny list in Valkey. On revocation, the identity provider webhook writes the token ID with a TTL equal to the token's remaining life. The middleware checks that key before every write tool. Read tools still rely on expiry because a stale read is cheaper than an extra 8ms on every page.

That trade-off is deliberate. The zero-trust agent gateway pattern makes the same distinction between read and write paths. If you already run an OPA sidecar, put the jti check there and keep the application middleware thin.

Agent Auth and MCP

MCP servers have the same credential problem. A client connects, the server exposes tools, and the server needs to know which user the agent is acting for. WorkOS Agent Registration uses auth.md so an agent can discover the registration endpoint and obtain scoped credentials without a human reading documentation.

The practical pattern is to treat the MCP server as a resource server. It validates the agent token, checks the scope against the tool name, and passes the act claim into the audit log. My MCP production notes cover the transport side, and the stateful MCP cluster design covers task handoff; the token side is the same blueprint model described here.

One caveat: Agent Auth is built for first-party agents, the ones you operate inside your own application. A third-party agent connecting from outside is a different trust problem. Do not stretch a first-party blueprint to cover an external vendor's agent and call it delegated access.

When NOT to Use Agent Auth

Do not use it for a single-user internal tool with no sensitive writes. A local script with a scoped API key is simpler and has a smaller failure surface. Do not use it if your agent runs entirely inside one backend service with no user context; an M2M token is the correct shape there.

Do not use it as a replacement for tool-level authorization. A token with invoices:read still needs a tenant filter in the query. I have seen a token scope enforced correctly while the SQL returned another tenant's rows because the org_id claim was never applied to the where clause.

Do not adopt it if you cannot mint tokens server-side. If your agent runs in a browser or a third-party automation platform, you need a backend you control to exchange the user token. Putting your WorkOS secret key in the agent runtime is worse than the problem you are solving.

Bottom Line

WorkOS Agent Auth gives an AI agent its own identity, a permission ceiling, and a short-lived token that names both the agent and the human who started the run. The 12ms verification cost is affordable, and the audit trail is the real product. If your agent can write to a business system, the question is not whether it needs scoped credentials. It is how long a stolen token would remain useful.

Frequently Asked Questions

What is WorkOS Agent Auth?

WorkOS Agent Auth is an early-access feature launched on 2 September 2026 that gives AI agents identities in AuthKit. You define a blueprint with a permission ceiling, then mint short-lived tokens per run. Each token identifies the agent, the user or organization it acts for, and its own scopes.

How are agent tokens different from API keys?

An API key is long-lived and usually carries a broad permission set. An agent token expires in minutes, carries only the intersection of the agent's ceiling and the user's scope, and includes an act claim that names the human who authorized the run. Deepak Bagada uses a jti deny list for instant revocation on write tools.

Does WorkOS Agent Auth work with MCP servers?

Yes, if the MCP server acts as a resource server. The server validates the agent token, checks the scope against the tool name, and logs the act claim. The first-party blueprint model fits agents you operate inside your own app; third-party agents need a separate OAuth trust decision.

How fast is agent token verification?

With a warm JWKS cache and a local Valkey revocation lookup, my middleware verifies a token in 3.4ms P50 and 12ms P95 on a 4GB VPS. The signature check is under 4ms; the revocation lookup accounts for most of the remaining latency.

// FREE DEVELOPER WORKBENCH

Build, format, or test your architecture in real time

Need to test schemas, regex, tokens, or JWTs? Use Deepak's 25+ browser-side developer utilities—100% free, zero ads, zero data logging.

Open Developer Tools →

KEEP READING

← All journal articles Get in touch →