> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portkey.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Identity Forwarding

> Pass authenticated user identity to MCP servers.

Identity forwarding sends authenticated user information to MCP servers. The server can use this for authorization, logging, or personalization—without implementing its own authentication.

## When to Use

MCP servers often need to know who is making requests—for access controls based on user roles, audit logging, or personalized responses.

Without identity forwarding, the MCP server sees requests coming from Prisma AIRS AI Gateway, not from individual users. Identity forwarding bridges that gap.

Common scenarios:

* **Audit logging.** MCP server logs user identity for compliance
* **Authorization.** MCP server enforces per-user permissions
* **Multi-tenancy.** MCP server scopes data by user or organisation
* **Analytics.** Track usage patterns by user or team
* **Personalization.** Customize responses based on user context

***

## How It Works

```
1. User authenticates to Portkey (API key, OAuth, or external IdP)
2. Portkey extracts user claims from the authentication
3. Portkey forwards claims to the MCP server (as configured)
4. MCP server uses claims for authorization/logging/personalization
```

The MCP server doesn't need to validate tokens or implement OAuth. It receives trusted user identity from the AI Gateway.

***

## Forwarding Methods

### Claims Header

Send user claims as a JSON header. Simple to parse, no cryptographic verification needed.

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "claims_header",
    "include_claims": ["sub", "email", "workspace_id"],
    "header_name": "X-User-Claims"
  }
}
```

The MCP server receives:

```
X-User-Claims: {"sub":"user123","email":"user@example.com","workspace_id":"ws_abc"}
```

Parse the JSON to get user identity:

<Tabs>
  <Tab title="Python">
    ```python theme={"system"}
    import json

    def get_user_identity(request):
        claims_header = request.headers.get("X-User-Claims")
        if claims_header:
            return json.loads(claims_header)
        return None
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"system"}
    function getUserIdentity(request: Request) {
      const claimsHeader = request.headers.get("X-User-Claims");
      if (claimsHeader) {
        return JSON.parse(claimsHeader);
      }
      return null;
    }
    ```
  </Tab>
</Tabs>

**Best for:** Internal MCP servers in trusted networks that trust the AI Gateway's claims without cryptographic verification.

### Bearer Token Passthrough

Forward the original access token unchanged.

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "bearer"
  }
}
```

The MCP server receives:

```
Authorization: Bearer <original-token>
```

The MCP server validates the token itself against the same IdP that issued it.

**Best for:** When the MCP server already has token validation infrastructure and uses the same IdP.

### Signed JWT

The AI Gateway generates a new JWT containing user claims, signed with the AI Gateway's private key.

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "jwt_header",
    "include_claims": ["sub", "email", "workspace_id", "organisation_id"],
    "header_name": "X-User-JWT",
    "jwt_expiry_seconds": 300
  }
}
```

The MCP server receives:

```
X-User-JWT: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9...
```

The JWT contains:

* User claims (filtered by `include_claims`)
* `iss`: `"portkey-mcp-gateway"`
* `iat`: Issued at timestamp
* `exp`: Expiration timestamp

**Best for:** Cryptographic proof that AI Gateway issued the claims. MCP servers verify the signature without trusting the network.

***

## Configuration Options

| Field                | Type      | Default         | Description                                      |
| -------------------- | --------- | --------------- | ------------------------------------------------ |
| `method`             | string    | Required        | `"claims_header"`, `"bearer"`, or `"jwt_header"` |
| `include_claims`     | string\[] | See below       | Claims to include                                |
| `header_name`        | string    | Method-specific | Custom header name                               |
| `jwt_expiry_seconds` | number    | `300`           | JWT expiry (only for `jwt_header`)               |

### Default Header Names

| Method          | Default Header  |
| --------------- | --------------- |
| `claims_header` | `X-User-Claims` |
| `bearer`        | `Authorization` |
| `jwt_header`    | `X-User-JWT`    |

### Default Claims

If you don't specify `include_claims`, the AI Gateway includes:

| Claim             | Description               |
| ----------------- | ------------------------- |
| `sub`             | Subject (user identifier) |
| `email`           | User's email address      |
| `username`        | Username                  |
| `user_id`         | AI Gateway user ID        |
| `workspace_id`    | Workspace identifier      |
| `organisation_id` | Organisation identifier   |
| `scope`           | OAuth scopes              |
| `client_id`       | OAuth client ID           |

These defaults cover common authorization and logging needs. Customize `include_claims` to add or restrict which claims are forwarded.

***

## Verifying Signed JWTs

For `jwt_header`, MCP servers verify tokens using the AI Gateway's public keys.

### Fetch Public Keys

```
GET https://aigw.portkey.ai/m/.well-known/jwks.json
```

Response:

```json theme={"system"}
{
  "keys": [
    {
      "kty": "RSA",
      "n": "0vx7agoebGcQSuu...",
      "e": "AQAB",
      "kid": "key-id-123",
      "use": "sig",
      "alg": "RS256"
    }
  ]
}
```

### Verify in Your MCP Server

Standard JWT libraries fetch and cache these keys automatically:

<Tabs>
  <Tab title="Python">
    ```python theme={"system"}
    import jwt
    from jwt import PyJWKClient

    # Initialize JWKS client (caches keys automatically)
    jwks_client = PyJWKClient("https://aigw.portkey.ai/m/.well-known/jwks.json")

    def verify_user_jwt(request):
        token = request.headers.get("X-User-JWT")
        if not token:
            return None

        try:
            signing_key = jwks_client.get_signing_key_from_jwt(token)
            claims = jwt.decode(
                token,
                signing_key.key,
                algorithms=["RS256"],
                issuer="portkey-mcp-gateway"
            )
            return claims
        except jwt.InvalidTokenError:
            return None
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import * as jose from 'jose';

    const JWKS = jose.createRemoteJWKSet(
      new URL('https://aigw.portkey.ai/m/.well-known/jwks.json')
    );

    async function verifyUserJwt(request: Request) {
      const token = request.headers.get('X-User-JWT');
      if (!token) return null;

      try {
        const { payload } = await jose.jwtVerify(token, JWKS, {
          issuer: 'portkey-mcp-gateway',
          algorithms: ['RS256']
        });
        return payload;
      } catch {
        return null;
      }
    }
    ```
  </Tab>
</Tabs>

***

## Security

### Identity Headers Are Protected

The AI Gateway adds identity headers (`X-User-Claims`, `X-User-JWT`) to the protected headers list. This means:

* **Clients cannot spoof identity.** If an agent sends a fake `X-User-Claims` header, it's stripped before processing.
* **Identity headers have highest priority.** They override any other headers with the same name.
* **Header forwarding cannot bypass this.** Even with `forward_headers: { mode: "all-except", headers: [] }`, identity headers from clients are blocked.

This ensures MCP servers can trust identity information from the AI Gateway.

### JWT Security Properties

Signed JWTs provide:

* **Integrity**: Claims cannot be modified without invalidating the signature
* **Authenticity**: Only the AI Gateway can sign with its private key
* **Expiry**: Short-lived tokens (default 5 minutes) limit replay window

The signing key is RSA-2048, and JWTs use RS256 algorithm.

***

## Performance

### JWT Caching

Signing JWTs involves expensive cryptographic operations. The AI Gateway caches signed JWTs to avoid this overhead:

| Scenario             | Latency  |
| -------------------- | -------- |
| Cache hit            | \~0.01ms |
| Cache miss (signing) | \~1ms    |

Cache details:

* Max 10,000 cached entries
* LRU eviction when full
* Cache key includes user identity and included claims
* Cache entries expire with the JWT

### JWKS Caching on MCP Server

Cache the AI Gateway's JWKS on the MCP server. Most JWT libraries handle this automatically with configurable TTL.

***

## Self-Hosted Setup

Self-hosted AI Gateway deployments using `jwt_header` require a signing key.

### Generate Key Pair

```bash theme={"system"}
# Generate RSA private key
openssl genrsa -out private.pem 2048

# Extract public key
openssl rsa -in private.pem -pubout -out public.pem
```

### Configure Environment

```bash theme={"system"}
export JWT_PRIVATE_KEY="$(cat private.pem)"
```

The AI Gateway automatically exposes the public key at `/.well-known/jwks.json` for MCP servers to verify.

***

## Examples

### Basic Claims Forwarding

Forward essential identity for logging:

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "claims_header",
    "include_claims": ["sub", "email"]
  }
}
```

### Signed JWT with Custom Expiry

MCP servers needing cryptographic verification with longer validity:

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "jwt_header",
    "include_claims": ["sub", "email", "workspace_id", "organisation_id", "groups"],
    "jwt_expiry_seconds": 600
  }
}
```

### Combined with External OAuth

Authenticate via IdP and forward validated claims to MCP servers:

```json theme={"system"}
{
  "jwt_validation": {
    "jwksUri": "https://your-idp.com/.well-known/jwks.json",
    "requiredClaims": ["sub", "email", "groups"]
  },
  "user_identity_forwarding": {
    "method": "claims_header",
    "include_claims": ["sub", "email", "groups"]
  }
}
```

Flow:

1. User authenticates with your IdP, gets token
2. User sends request to the AI Gateway with IdP token
3. The AI Gateway validates token against your IdP
4. The AI Gateway extracts claims and forwards to MCP server
5. MCP server uses claims for authorization

***

## Use Case: Per-User Authorization

An MCP server exposes project management tools. Users should only access their own projects.

**Configuration:**

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "jwt_header",
    "include_claims": ["sub", "email", "org_id"]
  }
}
```

**MCP Server Implementation:**

```python theme={"system"}
async def list_projects(request):
    claims = verify_user_jwt(request)
    if not claims:
        raise Unauthorized("Missing or invalid user identity")

    # Scope query to user's organization
    projects = await db.query(
        "SELECT * FROM projects WHERE org_id = ?",
        claims["org_id"]
    )
    return projects
```

***

## Use Case: Audit Logging

Log every tool call with user identity:

**Configuration:**

```json theme={"system"}
{
  "user_identity_forwarding": {
    "method": "claims_header",
    "include_claims": ["sub", "email", "workspace_id"]
  }
}
```

**MCP Server Logging:**

```python theme={"system"}
import json
import logging

def log_tool_call(request, tool_name, params):
    claims = json.loads(request.headers.get("X-User-Claims", "{}"))
    logging.info(
        "Tool called",
        extra={
            "tool": tool_name,
            "params": params,
            "user_sub": claims.get("sub"),
            "user_email": claims.get("email"),
            "workspace_id": claims.get("workspace_id")
        }
    )
```

***

## Related

| Topic                                                                             | Description                                 |
| --------------------------------------------------------------------------------- | ------------------------------------------- |
| [External OAuth](/docs/aigw/product/mcp-gateway/authentication/external-oauth)         | Use your own IdP for gateway authentication |
| [JWT Validation](/docs/aigw/product/mcp-gateway/authentication/jwt)                    | Validate tokens from external IdPs          |
| [Forwarding Headers](/docs/aigw/product/mcp-gateway/authentication/forwarding-headers) | Pass request headers to MCP servers         |


## Related topics

- [Authentication](/docs/aigw/product/mcp-gateway/authentication.md)
- [Forwarding Headers](/docs/aigw/product/mcp-gateway/authentication/forwarding-headers.md)
- [Bring Your Own Auth](/docs/aigw/product/mcp-gateway/authentication/external-oauth.md)
- [JWT Validation](/docs/aigw/product/mcp-gateway/authentication/jwt.md)
- [Custom Auth](/docs/aigw/product/mcp-gateway/authentication/custom-auth.md)
