Skip to main content
This feature is available only on the Enterprise Plan of Portkey.
Portkey supports JWT-based authentication in addition to API key authentication. Clients send a JWT in x-portkey-api-key or Authorization: Bearer; the token is validated against JWKS configured on the organisation. Portkey Cloud validates JWTs on the control plane. Hybrid and air-gapped deployments can optionally enable gateway-local JWT authentication so the AI Gateway validates tokens locally instead of delegating every request to the control plane.

Validate JWT Token (Guardrail)

Optionally validate JWTs inside a config’s hook pipeline using the JWT guardrail plugin. This is separate from gateway-local JWT auth, which replaces API key validation at the gateway.

Configuring JWT Authentication

JWT authentication can be configured under Admin SettingsOrganisationAuthentication.

JWKS Configuration

To validate JWTs, you must configure one of the following:
  • JWKS URL: A URL from which the public keys will be dynamically fetched.
  • JWKS JSON: A static JSON containing public keys.

Hard Requirements (Read First)

JWT Header (JOSE Header)

  • alg: Must be RS256. Symmetric algorithms like HS256 are not accepted.
  • typ: Must be JWT.
  • kid: Required. The value in the JWT header must match a kid in your JWKS.

Key Requirements

  • Key type: RSA
  • Key size: 2048 bits or higher
  • Your JWKS must expose only the public key parameters (e.g., kty, n, e, use, alg, kid). Do not include private key material.

JWT Requirements

Supported Algorithm

  • JWTs must be signed using RS256 (RSA Signature with SHA-256).

Required Claims

Your JWT payload must contain the following claims:
Claim KeyDescription
portkey_oid / organisation_idUnique identifier for the organization.
portkey_workspace / workspace_slugIdentifier for the workspace.
scope / scopesPermissions granted by the token.
expExpiration time (as a UNIX timestamp, in seconds).
  • exp is mandatory. Tokens without exp or with expired exp are rejected. A small amount of clock skew is tolerated.
  • iat and/or nbf are recommended but optional.
On Portkey Cloud, portkey_oid and portkey_workspace are required. In gateway-local JWT auth, org and workspace can be resolved from deployment configuration when you use a standard IdP token - see Integration modes.

Optional Claims

Claim KeyDescription
defaultsObject containing default settings applied to all requests made with this token. See Embedding Default Configs below.
usage_limitsUsage limits to enforce on requests made with this token.
rate_limitsRate limits to enforce on requests made with this token.

User Identification

Portkey identifies users in the following order of precedence for logging and metrics:
  1. email_id
  2. sub
  3. uid

End-to-End Working Example (Generate → Configure JWKS → Sign → Call)

The following example uses Node.js and the jose library to:
  1. generate an RSA key pair,
  2. create a JWKS containing the public key,
  3. sign a JWT with the private key,
  4. call Portkey with the JWT.

1) Prerequisites

# Node 18+ recommended
npm init -y
npm install jose

2) Generate RSA Keys, Create JWKS, and Sign a JWT (NodeJS)

Create generate-and-sign-jwt.mjs:
import { generateKeyPair, exportJWK, SignJWT } from 'jose';
import { randomUUID } from 'node:crypto';
import fs from 'node:fs';

const { publicKey, privateKey } = await generateKeyPair('RS256');

// Create a public JWK for JWKS
const publicJwk = await exportJWK(publicKey);
publicJwk.kty = 'RSA';
publicJwk.use = 'sig';
publicJwk.alg = 'RS256';
publicJwk.kid = randomUUID();

const jwks = { keys: [publicJwk] };
fs.writeFileSync('jwks.json', JSON.stringify(jwks, null, 2));

const now = Math.floor(Date.now() / 1000);

// Sign a JWT with the private key
const jwt = await new SignJWT({
  portkey_oid: '<YOUR_ORG_ID>',
  portkey_workspace: '<YOUR_WORKSPACE_SLUG>',
  scope: ['completions.write', 'logs.view'],
  email_id: 'user@example.com',
  sub: '<YOUR_USER_ID>',
  // Optional: embed default config and metadata
  // defaults: { config_id: 'pc-your-config-id', metadata: { environment: 'production' } }
})
  .setProtectedHeader({ alg: 'RS256', kid: publicJwk.kid, typ: 'JWT' })
  .setIssuedAt(now)
  .setExpirationTime(now + 60 * 60) // 1 hour
  .sign(privateKey);

fs.writeFileSync('token.jwt', jwt);

console.log('JWKS written to jwks.json');
console.log('JWT written to token.jwt');
console.log('kid:', publicJwk.kid);
Run it:
node generate-and-sign-jwt.mjs
This produces:
  • jwks.json: A JWKS containing your public key (with a kid).
  • token.jwt: A signed JWT ready to use with Portkey.

3) Add Your Public Key to Portkey (JWKS)

In the Portkey Admin UI:
  • Navigate to Admin SettingsOrganisationAuthentication.
  • Choose either:
    • JWKS URL: Host jwks.json at a reachable HTTPS URL and paste that URL.
    • JWKS JSON: Paste the entire contents of your generated jwks.json.
  • Save changes.
Ensure the kid in your JWT header matches a key in the configured JWKS.

4) Call Portkey Using the Signed JWT

Send the JWT in the x-portkey-api-key header.
curl https://api.portkey.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "x-portkey-api-key: $(cat token.jwt)" \
  -H "x-portkey-provider: @<YOUR_PROVIDER_SLUG>" \
  -d '{
    "model": "your-model-id",
    "messages": [
      { "role": "user", "content": "Hello!" }
    ]
  }'
If your JWT, JWKS, and claims are correct, the request will authenticate and succeed.
If you prefer Python for signing, you can generate the RSA key pair using your preferred method, ensure the public key is present in your JWKS with a matching kid, and use a library like PyJWT to sign with RS256 while setting the header { "alg": "RS256", "typ": "JWT", "kid": "<your-kid>" }.

Authentication Process

  1. The client sends the JWT in x-portkey-api-key or Authorization: Bearer:
    x-portkey-api-key: <JWT_TOKEN>
    
    If x-portkey-api-key is absent, Authorization is used.
  2. The gateway or control plane validates the JWT:
    • Verifies the signature using the organisation JWKS.
    • Checks token expiry.
    • Ensures required claims are present.
  3. If valid, the request is authenticated and user details are extracted for authorization and logging.
  4. If invalid, the request is rejected. Common responses include 401 Unauthorized (invalid or expired token), 403 Forbidden (scope or workspace not allowed), 412 (usage limit exceeded), and 429 (rate limit exceeded).
On hybrid and air-gapped gateways with JWT_ENABLED=ON, validation runs locally on the gateway. Otherwise, JWT-shaped tokens are validated by the control plane.

Authorization & Scopes

Once the JWT is validated, the server checks for the required scope. Scopes can be provided in the JWT as either a single string or an array of strings using the scope or scopes claim. Scopes can also be prefixed with portkey. (e.g., portkey.completions.write).
JWT tokens with appropriate scopes function identically to workspace API keys, providing access to workspace-specific operations. They cannot be used as organization API keys, which have broader administrative permissions across all workspaces.

Example JWT Header

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "<YOUR_KID>"
}
  • This matches the signing example (.setProtectedHeader({ alg: 'RS256', kid: publicJwk.kid, typ: 'JWT' })).
  • Ensure kid exactly matches one key in your configured JWKS.

Embedding Default Configs in JWT

You can embed a default config directly in the JWT payload using the defaults claim. This works the same way as attaching default configs to API keys, but lets you control it per-token - useful when different users or services need different routing rules. The defaults object supports the following fields:
FieldDescription
config_idThe ID of the config to apply as the default for all requests made with this token.
metadataA JSON object with custom metadata to attach to all requests.
When a JWT with a defaults.config_id is used, Portkey validates that the config belongs to the same organization before applying it. If the config is not found or doesn’t belong to the org, the default is ignored.
This follows the same precedence rules as API key default configs - if a user explicitly passes a config ID in their request headers, it will override the JWT default unless config override is disabled at the workspace level.

Example JWT Payload with Defaults

{
  "portkey_oid": "3ed1b666-7e3d-416a-9260-da10e1610d1a",
  "portkey_workspace": "ws-shared-8622d1",
  "scope": ["completions.write", "logs.view"],
  "email_id": "user@example.com",
  "sub": "user-123",
  "exp": 1735689600,
  "defaults": {
    "config_id": "pc-your-config-id",
    "metadata": {
      "environment": "production",
      "team": "backend"
    }
  }
}

Example JWT Payload

{
  "portkey_oid": "3ed1b666-7e3d-416a-9260-da10e1610d1a",
  "portkey_workspace": "ws-shared-8622d1",
  "scope": ["completions.write", "logs.view"],
  "email_id": "user@example.com",
  "sub": "user-123",
  "exp": 1735689600
}

Making API Calls with JWT Authentication

Once you have a valid JWT token, you can use it to authenticate your API calls to Portkey. Below are examples showing how to use JWT authentication with different SDKs.
Install the Portkey SDK with npm
npm install portkey-ai
import Portkey from 'portkey-ai';

const client = new Portkey({
  apiKey: '<JWT_TOKEN>', // Use JWT token instead of API key
  provider: '@<YOUR_PROVIDER_SLUG>'

});

async function main() {
  const response = await client.chat.completions.create({
    messages: [{ role: "user", content: "Hello, how are you today?" }],
    model: "your-model-id",
  });

  console.log(response.choices[0].message.content);
}

main();

Troubleshooting “Invalid API Key” Errors

  • Wrong algorithm: Only RS256 is accepted; HS256 or others will fail.
  • Missing or mismatched kid: Your JWT header must include a kid that matches a key in the JWKS.
  • Incorrect header usage: Send the raw JWT in x-portkey-api-key, or use Authorization: Bearer <JWT>. Do not prefix the token with Bearer in x-portkey-api-key.
  • Expired or missing exp: The exp claim is required and must be in the future. Allow for small clock skew.
  • Private vs Public key mix-up: Your JWKS must contain only the public key parameters. The private key is used only for signing; never paste it into the JWKS JSON.
  • Wrong org/workspace identifiers: portkey_oid (or organisation_id) and portkey_workspace (or workspace_slug) must correspond to valid identifiers in your Portkey tenant.
  • Scopes missing for the API you call: E.g., chat completions needs completions.write.
  • Unreachable JWKS URL: If using a URL, it must be publicly reachable by Portkey. For static JSON, ensure the pasted JSON is valid and includes keys: [...].
All Invalid JWT errors are logged in the Audit Logs. Sample error message:
"jwtToken": "ey****bt",
"error": "Error Verifying JWT token: Signing Key Not Found",

Gateway-Local JWT Authentication

Gateway-local JWT authentication is available only on hybrid and air-gapped deployments.It requires gateway 2.5.0 or higher (Backend v1.13.0 or higher for air-gapped) with JWT_ENABLED=ON.
When enabled, your AI Gateway validates JWTs locally instead of sending every request to the control plane for authentication. This reduces latency and keeps authentication working even when the control plane is temporarily unreachable. If JWT_ENABLED is not ON, JWT-shaped tokens are validated by the control plane instead.

How it works

  1. The client sends a JWT in x-portkey-api-key or Authorization: Bearer.
  2. The gateway determines the organisation and workspace for the request.
  3. The gateway verifies the token signature against your configured JWKS.
  4. The gateway applies the same authorization, usage limits, rate limits, and policies as it would for a regular API key.

Integration modes

Choose the mode that matches how much control you have over your JWT issuer.

Mode A - You control the JWT issuer

You can add Portkey-specific claims to your tokens (portkey_oid, portkey_workspace, usage_limits, rate_limits, defaults, and others) and use the full JWT feature set described in this guide.

Mode B - Standard IdP token

Use this when tokens come from Okta, Auth0, Entra, Cognito, or another IdP whose token schema you cannot change. Portkey handles org, workspace, and budget configuration on the server side. If your IdP cannot include Portkey scopes in tokens, set JWT_LOCAL_AUTH_DEFAULT_SCOPES on the gateway instead.
What you needHow to configure it
OrganisationSet ORGANISATIONS_TO_SYNC to a single org UUID when the token does not include portkey_oid or organisation_id.
WorkspaceMap users to workspaces with deployment settings, restrict the deployment to specific workspaces, or set an org-level default workspace. See Workspace resolution.
ScopesRegister gateway scopes (e.g. completions.write) in your IdP, or set JWT_LOCAL_AUTH_DEFAULT_SCOPES on the gateway when IdP scope changes are not possible. Both completions.write and portkey.completions.write are accepted.
Usage and rate limitsConfigure limits on the workspace, or use workspace policies keyed off request metadata.
User attribution in logsThe gateway uses email_id, sub, or uid from the token for per-user logging and metrics.
JWKSPoint your org’s JWKS URL to your IdP’s discovery endpoint (e.g. https://<tenant>/.well-known/jwks.json) in Admin SettingsOrganisationAuthentication.

Enable gateway-local JWT

Gateway environment variables

VariableRequiredDescription
JWT_ENABLEDYesSet to ON to enable gateway-local JWT auth. Any other value uses control plane validation.
ORGANISATIONS_TO_SYNCYesComma-separated org UUIDs to sync from the control plane. Also restricts which orgs this gateway accepts.
PORTKEY_CLIENT_AUTHYesService auth token for the gateway to sync org, workspace, and deployment settings from the control plane.
JWT_LOCAL_AUTH_DEFAULT_SCOPESNoComma-separated gateway scopes to apply when the IdP token does not include scope or scopes. Use this when you cannot add Portkey scopes to tokens in your IdP.
Example:
JWT_LOCAL_AUTH_DEFAULT_SCOPES=completions.write,mcp.invoke
These variables are also documented in the hybrid deployment guides.

Organisation JWKS

Configure JWKS under Admin SettingsOrganisationAuthentication:
  • JWKS URL (recommended) - your IdP’s public key endpoint, or
  • JWKS JSON - a static public key set
Authentication fails if JWKS is not configured for the organisation.

Required JWT claims (gateway-local)

ClaimRequiredNotes
scope or scopesConditionalAt least one valid scope, unless JWT_LOCAL_AUTH_DEFAULT_SCOPES is set on the gateway.
portkey_oid or organisation_idConditionalRequired when ORGANISATIONS_TO_SYNC lists more than one org. With a single-org gateway (Mode B), the env var supplies the org.
subConditionalRequired when your deployment restricts which user IDs may authenticate.
ClaimPurposeMode B (locked IdP)
expToken expiryUsually already present on IdP tokens
email_id, sub, or uidUser identity for loggingsub is used automatically
portkey_workspace or workspace_slugTarget workspaceResolve via deployment settings or org default workspace
usage_limits, rate_limitsPer-token limitsConfigure on the workspace or via policies instead
defaultsDefault config and metadataSet default config on the workspace instead

Example token payloads

Mode A - full Portkey-aware token:
{
  "portkey_oid": "4f8d2c3b-1a9e-4b7c-8d2f-6a1b3c4d5e6f",
  "portkey_workspace": "engineering-workspace-slug",
  "sub": "user-42",
  "email_id": "alice@company.com",
  "scope": "completions.write mcp.invoke",
  "exp": 1735689600,
  "usage_limits": [
    {
      "id": "ul-1",
      "type": "cost",
      "credit_limit": 50,
      "status": "active"
    }
  ]
}
Mode B - minimal IdP token:
{
  "iss": "https://idp.example.com",
  "aud": "portkey-gateway",
  "sub": "user-42",
  "exp": 1735689600
}
If the token omits scope / scopes, set JWT_LOCAL_AUTH_DEFAULT_SCOPES on the gateway (e.g. completions.write,mcp.invoke). In Mode B, the gateway resolves the org from ORGANISATIONS_TO_SYNC and the workspace from your deployment configuration.

Gateway scopes

Scopes can be provided as a space-separated string (scope) or an array (scopes) in the token, or via JWT_LOCAL_AUTH_DEFAULT_SCOPES on the gateway. A portkey. prefix is optional.
ScopeUsed for
completions.readRead completions
completions.writeChat completions, embeddings, and most AI routes
logs.readRead logs
logs.writeWrite logs
logs.exportExport logs
prompts.renderRender prompts
virtual_keys.listList virtual keys
mcp.invokeMCP invocation
agents.invokeAgent invocation
guardrails.invokeGuardrail invocation
feedbacks.readRead feedbacks
feedbacks.writeWrite feedbacks
Each API route requires a matching scope. For example, chat completions requires completions.write.

Organisation resolution

The gateway determines which organisation a token belongs to using this priority order:
  1. portkey_oid claim in the token
  2. organisation_id claim in the token
  3. ORGANISATIONS_TO_SYNC - only when exactly one org UUID is configured
If ORGANISATIONS_TO_SYNC is set, the resolved org must be in that list.
ScenarioResult
Both portkey_oid and organisation_id are presentportkey_oid is used
No org in token, and exactly one org in ORGANISATIONS_TO_SYNCThat org is used
No org in token, and multiple orgs in ORGANISATIONS_TO_SYNCRequest rejected (401)
Token org is not in ORGANISATIONS_TO_SYNCRequest rejected (403)
Organisation not foundRequest rejected (401)
JWKS not configured for the orgRequest rejected (403)
After the org is determined, the gateway verifies the token signature against that org’s JWKS.

Workspace resolution

Workspace is determined after the token is verified, using token claims first and then deployment settings. From the token:
  1. portkey_workspace
  2. workspace_slug
From deployment settings (when the token has no workspace claim):
SettingEffect
User allowlistRestrict authentication to specific user IDs (sub values)
User-to-workspace mappingMap each user ID to a workspace slug
Workspace allowlistWhen enabled, only listed workspaces are allowed; the first listed workspace is used as the default
Org default workspaceUsed when no workspace is resolved from the token or deployment settings
Contact your Portkey account team to configure deployment-level JWT settings such as user allowlists, user-to-workspace mappings, and workspace allowlists.

Usage limits, rate limits, and policies

JWT-authenticated requests are subject to the same limit and policy checks as API key requests. Limits can come from three places:
SourceDescription
Token claimsusage_limits and rate_limits embedded in the JWT (Mode A)
Workspace settingsLimits configured on the resolved workspace
Workspace policiesConditional budget policies based on request context
Each unique JWT is tracked separately for token-level limits. Workspace limits and policies apply on top of any token-level limits - all must pass before the request proceeds. Guidance by mode:
  • Mode A: Put per-user or per-session budgets in the JWT usage_limits claim. Use workspace limits and policies for shared team budgets.
  • Mode B: Configure all budgets and rate limits on the workspace or as workspace policies. For per-user splits within a shared workspace, use policies keyed off user metadata. For hard per-user isolation, map each user to a dedicated workspace.

Authentication flow

Troubleshooting gateway-local JWT

SymptomLikely causeWhat to check
Organisation not foundToken missing org claim on a multi-org gatewayAdd portkey_oid to the token, or set a single org in ORGANISATIONS_TO_SYNC
Org not allowedOrg ID not in sync listAdd the org to ORGANISATIONS_TO_SYNC or fix the token claim
JWKS not configuredNo public keys set for the orgConfigure JWKS URL or JSON under Admin SettingsOrganisationAuthentication
JWT verification failedBad signature, expired token, or wrong keysVerify IdP signing config, exp, and JWKS
No valid scopesMissing scopes in token and no gateway defaultsAdd scopes to the token, or set JWT_LOCAL_AUTH_DEFAULT_SCOPES on the gateway
User not allowedUser ID restricted by deployment settingsUpdate deployment allowlist or token sub
Workspace forbiddenWorkspace not allowed for this deploymentUse an allowed workspace or update deployment settings

Quick setup checklist

Both modes:
  • Set JWT_ENABLED=ON, PORTKEY_CLIENT_AUTH, and ORGANISATIONS_TO_SYNC on the gateway
  • Configure JWKS for your org in the Portkey admin UI
  • Ensure the gateway cache is enabled (required for hybrid and air-gapped deployments)
Mode A:
  • Issue JWTs with required scopes and optionally portkey_oid, portkey_workspace, usage_limits, rate_limits, and defaults
Mode B:
  • Set ORGANISATIONS_TO_SYNC to a single org UUID
  • Configure scopes in your IdP, or set JWT_LOCAL_AUTH_DEFAULT_SCOPES on the gateway if IdP scope changes are not possible
  • Set up workspace resolution (user mapping, workspace allowlist, or org default workspace)
  • Configure workspace-level usage limits, rate limits, and policies in Portkey

Caching & Token Revocation

Validated JWTs are cached until they expire to reduce validation overhead. On gateway-local auth, org and workspace settings are also cached locally and refreshed during control plane sync. If you rotate signing keys:
  • Publish the new public key in JWKS with a new kid.
  • Start issuing tokens signed by the new private key (with the new kid).
  • Old tokens remain valid until their exp is reached.
Last modified on June 29, 2026