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

# Session identity tokens

> Give Agent Sessions short-lived OIDC identity tokens for keyless access to AWS, Google Cloud, Vault, and other relying parties.

<Badge color="blue" size="lg" shape="pill" stroke="true">Feature available with the [Enterprise Plan](https://orq.ai/solutions/enterprise)</Badge>

<Badge>Beta</Badge>

<Note>
  **Agent Sessions** is in private preview. The feature is enabled per workspace by the **Orq.ai** team and the API is not part of the public SDKs yet.
</Note>

## Use cases

* Exchange the session token for temporary cloud credentials without storing a cloud access key in **Orq.ai**.
* Restrict a relying party to one workspace, **Factory**, repository, agent, or isolation tier.

## Overview

Session identity gives each session an RS256-signed OIDC token. The token identifies the workspace, **Factory**, session, repository, requester, agent, and isolation tier. A relying party validates the token against the **Orq.ai** federation issuer, then applies its own access policy. Session identity uses the same SSO entitlement as workload federation and is disabled by default.

## Enable session identity

Open **Managed Agents** > **Factories**, select a **Factory**, then open **Configuration** > **Governance**. Turn on **Enable session identity tokens** and enter one audience per line.

The same setting is available through the private Factories API:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X PATCH https://my.orq.ai/v2/factories/$FACTORY_ID \
    -H "Authorization: Bearer $ORQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "identity": {
        "enabled": true,
        "audiences": ["orq-agent-sessions", "vault"]
      }
    }'
  ```
</CodeGroup>

## Configuration

| Field                | Type         | Default                  | Description                                                                                                   |
| -------------------- | ------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `identity.enabled`   | boolean      | `false`                  | Mints a session token and sends it to the session computer.                                                   |
| `identity.audiences` | string array | `["orq-agent-sessions"]` | Accepted relying-party audiences. Supports at most 10 unique values. Each value contains 1 to 255 characters. |

An empty audience list uses `orq-agent-sessions`. Set a provider-specific or **Factory**-specific audience when the relying party requires a narrower trust boundary.

## Claims

| Claim                      | Value                                                                      |
| -------------------------- | -------------------------------------------------------------------------- |
| `iss`                      | Configured **Orq.ai** federation issuer, ending in `/federation`.          |
| `sub`                      | `orq:ws_<workspace_id>:session:<session_id>`                               |
| `aud`                      | The audiences configured on the **Factory**.                               |
| `workspace_id`             | Workspace identifier.                                                      |
| `factory_id`               | **Factory** identifier.                                                    |
| `session_id`               | Session identifier.                                                        |
| `repository`               | Repository URL from the session.                                           |
| `requested_by`             | Account or user identifier that created the session.                       |
| `agent`                    | Session agent value.                                                       |
| `isolation`                | Computer isolation value, such as `runc` or `gvisor`.                      |
| `iat`, `nbf`, `exp`, `jti` | Standard issue time, not-before time, expiry, and token identifier claims. |

The token lifetime is one hour. Trust policies should match stable claims such as `workspace_id`, `factory_id`, and the `sub` prefix instead of one session id.

## AWS IAM

Create an IAM OIDC provider for the **Orq.ai** federation issuer and register a distinct client ID such as `orq-factory-<factory_id>` for each trusted **Factory**. Add that value to the **Factory** audiences. Replace the issuer condition-key prefix with the issuer URL without `https://`.

AWS STS exposes standard OIDC claims such as `aud` and `sub` as [IAM condition keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_iam-condition-keys.html#condition-keys-wif) but ignores arbitrary custom claims. The JWT `factory_id` claim cannot be used directly in an IAM trust policy. A distinct audience per **Factory** provides the factory boundary instead.

```json JSON theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<account_id>:oidc-provider/my.orq.ai/federation"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "my.orq.ai/federation:aud": "orq-factory-<factory_id>"
        },
        "StringLike": {
          "my.orq.ai/federation:sub": "orq:ws_<workspace_id>:session:*"
        }
      }
    }
  ]
}
```

Set the standard AWS web identity variables in a computer blueprint. The `initialize` commands then inherit short-lived credentials from the session token.

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
env:
  AWS_WEB_IDENTITY_TOKEN_FILE: /run/orq/identity-token
  AWS_ROLE_ARN: arn:aws:iam::<account_id>:role/<role_name>
initialize:
  - aws sts get-caller-identity
```

## Google Cloud Workload Identity Federation

Create an OIDC provider in a Workload Identity Pool. The default **Factory** audience works when the provider allows `orq-agent-sessions`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
gcloud iam workload-identity-pools providers create-oidc orq-agent-sessions \
  --location=global \
  --workload-identity-pool="$POOL_ID" \
  --issuer-uri="$ORQ_FEDERATION_ISSUER" \
  --allowed-audiences=orq-agent-sessions \
  --attribute-mapping="google.subject=assertion.sub,attribute.workspace_id=assertion.workspace_id,attribute.factory_id=assertion.factory_id,attribute.session_id=assertion.session_id,attribute.repository=assertion.repository,attribute.requested_by=assertion.requested_by,attribute.agent=assertion.agent,attribute.isolation=assertion.isolation"
```

Bind access to a **Factory** attribute, then create an external-account credential file that reads the rotating token:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
gcloud iam service-accounts add-iam-policy-binding "$SERVICE_ACCOUNT" \
  --role=roles/iam.workloadIdentityUser \
  --member="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$POOL_ID/attribute.factory_id/fac_<factory_id>"

gcloud iam workload-identity-pools create-cred-config \
  "projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$POOL_ID/providers/orq-agent-sessions" \
  --service-account="$SERVICE_ACCOUNT" \
  --credential-source-file=/run/orq/identity-token \
  --output-file=/tmp/orq-google-credentials.json

export GOOGLE_APPLICATION_CREDENTIALS=/tmp/orq-google-credentials.json
gcloud auth login --cred-file=/tmp/orq-google-credentials.json
gcloud auth print-access-token >/dev/null
```

## HashiCorp Vault

Configure the JWT auth method with the federation discovery URL, then bind a role to the session subject and **Factory** claim.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
vault auth enable jwt

vault write auth/jwt/config \
  oidc_discovery_url="$ORQ_FEDERATION_ISSUER" \
  bound_issuer="$ORQ_FEDERATION_ISSUER"

vault write auth/jwt/role/orq-agent-sessions \
  role_type=jwt \
  bound_audiences=orq-agent-sessions \
  bound_claims_type=glob \
  bound_claims='{"sub":"orq:ws_<workspace_id>:session:*","factory_id":"fac_<factory_id>"}' \
  user_claim=sub \
  token_policies=agent-sessions \
  token_ttl=30m

vault write auth/jwt/login \
  role=orq-agent-sessions \
  jwt=@/run/orq/identity-token
```

## Generic JWKS verification

Read the issuer discovery document from `$ORQ_FEDERATION_ISSUER/.well-known/openid-configuration`. Use its `jwks_uri`, select the public key matching the JWT `kid`, require `RS256`, and validate `iss`, `aud`, `exp`, `nbf`, and `sub`. Apply authorization checks to the custom claims after signature validation.

```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { readFile } from "node:fs/promises";
import { createRemoteJWKSet, jwtVerify } from "jose";

const issuer = process.env.ORQ_FEDERATION_ISSUER;
const tokenPath = process.env.ORQ_IDENTITY_TOKEN_FILE;

if (!issuer || !tokenPath) {
  throw new Error("ORQ_FEDERATION_ISSUER and ORQ_IDENTITY_TOKEN_FILE are required");
}

const token = (await readFile(tokenPath, "utf8")).trim();
const jwks = createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`));
const { payload, protectedHeader } = await jwtVerify(token, jwks, {
  issuer,
  audience: "orq-agent-sessions",
  algorithms: ["RS256"],
});

if (
  !protectedHeader.kid ||
  !payload.sub?.startsWith("orq:ws_<workspace_id>:session:") ||
  payload.factory_id !== "fac_<factory_id>"
) {
  throw new Error("session identity is outside the accepted trust boundary");
}
```

## Token delivery and rotation

The runtime writes the JWT to `/run/orq/identity-token` with mode `0600` and exports `ORQ_IDENTITY_TOKEN_FILE` to every supported agent process. The token is minted when the computer becomes ready, at run start, and after resume. Ready and running sessions receive a replacement at half of the one-hour lifetime.

Pausing removes the computer and its ephemeral `/run/orq` volume. Resuming creates a new computer and pushes a fresh token. Sessions in a **Factory** with identity disabled do not receive the file. Turning identity off clears an existing file at the next credential push or scheduled refresh; an already issued JWT remains valid until expiry unless the relying party removes its trust. **Orq.ai** does not write the JWT to MongoDB, the pod specification, logs, or session events. Only expiry and refresh scheduling metadata are stored; `session.identity_issued` records the key id, expiry, and audiences.

<Note>
  The provider recipes describe the required configuration. Live AWS and Google Cloud token exchanges are not part of the local Agent Sessions verification.
</Note>
