Verify session identity in self-hosted environments changedself-hosted-environments-identity
Nearest release: v2.1.250, published 5 hours before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Recorded here
Lines+11added
Lines−11removed
From line
6
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits9to this page, all time
The whole hunk
from line 6, old and new numbered
/
from line 6
66 Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page covers session identity verification; see the [quickstart](/docs/en/self-hosted-environments-quickstart) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy) for the fleet recipes.
77</Note>
88
9A [self-hosted environment](/docs/en/self-hosted-environments) lets [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions run on infrastructure you operate instead of on Anthropic's. Because the session runs inside your network, Claude can call your internal services directly. Those services need a way to confirm that a request really came from a Claude Code session in your environment, and to identify which user created that session.
9A [self-hosted environment](/docs/en/self-hosted-environments) lets [Claude Code on the web](/docs/en/claude-code-on-the-web) sessions run on infrastructure you operate instead of on Anthropic's. Because the session runs inside your network, Claude can call your internal services directly. Those services need a way to confirm that a request really came from a Claude Code session in your environment, and to identify the user or service identity that created that session.
1010
1111Every session in a self-hosted environment receives a signed JSON Web Token (JWT) in the `CLAUDE_CODE_SESSION_ACCESS_TOKEN` environment variable. A session presents the token like any bearer credential; for example, a script Claude runs can call your service with `curl -H "Authorization: Bearer $CLAUDE_CODE_SESSION_ACCESS_TOKEN"`. Anthropic signs the token and publishes the verification keys at a public JWKS endpoint. Your services fetch those keys, verify the signature, and read the claims to decide what access to grant.
1212
from line 18
1818
1919A valid token establishes some facts and deliberately not others:
2020
21* **Proves**: Anthropic issued the token for a specific session in a specific environment, and how the session was created: by a user in your organization, or with an organization service key
21* **Proves**: Anthropic issued the token for a specific session in a specific environment, and how the session was created: by a user in your organization, or by your organization's service identity, which is how [Claude Tag channel sessions](https://claude.com/docs/claude-tag/concepts/agent-identity) start
2222* **Doesn't prove**: which process on the runner host presents it. The token sits in an environment variable inside the session, so any code Claude runs, and any tool or MCP server the session starts, can read and present it.
2323
2424Two consequences for your services:
2525
2626* Verify the `aud` claim against your environment ID, the `ccpool_...` value shown with your environment on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), to reject tokens issued to any other organization's environment.
27* Scope credentials you derive from the token to what a single coding session should be able to do, not to everything the creating user can do. See [Scope derived credentials](#scope-derived-credentials).
27* Scope credentials you derive from the token to what a single coding session should be able to do, not to everything the session's creator can do. See [Scope derived credentials](#scope-derived-credentials).
2828
2929### Token format
3030
from line 80
8080 </Step>
8181
8282 <Step title="Read the identity">
83 The creating user's identity is in the `act` claim: `act.sub` is their Anthropic user ID in the prefixed form `user:<id>`, and `act.email`, when the creating surface recorded one, is their email address. Sessions created with an organization service key carry no user identity, so treat a session as user-created only when `act.sub` carries the `user:` prefix, rather than testing whether identity claims are absent. See the [claims reference](#claims-reference) for the full structure and the flat duplicate claims.
83 The creating user's identity is in the `act` claim: `act.sub` is their Anthropic user ID in the prefixed form `user:<id>`, and `act.email`, when the creating surface recorded one, is their email address. Sessions your organization's service identity creates, including Claude Tag channel sessions, carry an `agent:` subject instead, so treat a session as user-created only when `act.sub` carries the `user:` prefix, rather than testing whether identity claims are absent. See the [claims reference](#claims-reference) for the full structure and the flat duplicate claims.
8484 </Step>
8585</Steps>
8686
from line 171
171171
172172[Wrapper scripts](/docs/en/self-hosted-environments-configuration#wrapper-scripts) run inside the session, before Claude starts. Instead of calling a JWT library, they can run the runner binary's `self-hosted-runner decode-token` subcommand. The subcommand reads the token from a positional argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from piped stdin, in that order, then strips the prefix, verifies the signature against the JWKS endpoint, checks expiry, and prints the claims as JSON. The subcommand performs the signature and expiry checks only; it doesn't check `iss`, `aud`, or `ccr:role`. When your wrapper's auth decision depends on those claims, read them from the printed JSON and compare them explicitly.
173173
174This command extracts the creator identity, preferring the SSO provider's subject, then the email address, then the always-present Anthropic user ID:
174This command extracts the creator identity, preferring the SSO provider's subject, then the email address, then the creator's `act.sub` subject, `user:<id>` or `agent:<id>`:
175175
176176```bash theme={null}
177177"$CLAUDE_RUNNER_CLAUDE_BIN" self-hosted-runner decode-token | jq -re '.act.attested_by.sub // .act.email // .act.sub'
from line 183
183183
184184## Claims reference
185185
186The table below lists the session token claims relevant to verification. Read identity from the `ccr:*` namespace and the `act` chain; the flat `account_email`, `organization_uuid`, and `account_uuid` claims are backward-compatibility duplicates that may be removed. Sessions created with an organization service key omit `act.email`, `ccr:account_id`, `account_email`, and `account_uuid`. The two email claims are optional for user-created sessions too: Anthropic records them at session creation only when the creating request's credentials carry an email, and a session dispatched from the CLI can lack both, so key identity on `act.sub` or `ccr:account_id` rather than on email. Tokens can also carry additional claims beyond this table; ignore claims you don't recognize.
186The table below lists the session token claims relevant to verification. Read identity from the `ccr:*` namespace and the `act` chain; the flat `account_email`, `organization_uuid`, and `account_uuid` claims are backward-compatibility duplicates that may be removed. Sessions your organization's service identity creates, including Claude Tag channel sessions, carry an `agent:` subject in `act.sub` and omit `act.email`, `ccr:account_id`, `account_email`, and `account_uuid`. The two email claims are optional for user-created sessions too: Anthropic records them at session creation only when the creating request's credentials carry an email, and a session dispatched from the CLI can lack both, so key identity on `act.sub` or `ccr:account_id` rather than on email. Tokens can also carry additional claims beyond this table; ignore claims you don't recognize.
187187
188188| Claim | Type | Description |
189189| :------------------ | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
from line 205
205205
206206### The `act` chain
207207
208The `act` claim records the full delegation path from the user who created the session down to the [environment](/docs/en/self-hosted-environments#key-concepts) whose secret admitted the runner, and the identity that created that secret. The creating user is the outermost actor, so `act.sub` identifies them directly.
208The `act` claim records the full delegation path from the user or service identity that created the session down to the [environment](/docs/en/self-hosted-environments#key-concepts) whose secret admitted the runner, and the identity that created that secret. The creator is the outermost actor, so `act.sub` identifies them directly.
209209
210210| Path | Description |
211211| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
212| `act.sub` | The creating user's Anthropic user ID, in the form `user:<id>`. |
212| `act.sub` | The creating user's Anthropic user ID, in the form `user:<id>`, or `agent:<id>` when your organization's service identity created the session, as it does for Claude Tag channel sessions. |
213213| `act.email` | The creating user's email address, when one was recorded at session creation. Don't require it; key on `act.sub`. |
214214| `act.attested_by` | The upstream identity provider's attestation for the creating user, when available. `act.attested_by.sub` is the subject your SSO provider, such as Google or Okta, issued. Prefer this over `act.email` when mapping to identities in your own systems. |
215215| `act.act` | The runner that spawned the session. `act.act.sub` is `ccr:runner:<runner_id>`. |
from line 218
218218
219219## Scope derived credentials
220220
221The session token identifies the creating user, but don't treat it as equivalent to that user logging in directly. The token sits in an environment variable inside the session, so any code Claude runs, and any tool or MCP server the session starts, can read and present it.
221The session token identifies the user or service identity that created the session, but don't treat it as equivalent to that creator logging in directly. The token sits in an environment variable inside the session, so any code Claude runs, and any tool or MCP server the session starts, can read and present it.
222222
223223Verification is also offline: a token that verifies against the JWKS stays valid until its `exp`, whatever has happened to the session since, and Anthropic doesn't publish a revocation feed for session tokens. Bound anything you derive from the token accordingly.
224224
225225When your service exchanges the token for internal credentials, issue credentials scoped to what one coding session should reach:
226226
227* **Limit capabilities**: grant read and write access to the resources the session needs for coding tasks, not administrative capabilities the user holds elsewhere.
227* **Limit capabilities**: grant read and write access to the resources the session needs for coding tasks, not administrative capabilities the creator holds elsewhere.
228228* **Limit lifetime**: bound derived credentials to the token's `exp`, or shorter.
229* **Audit as the session**: record the `ccr:session_id` and `jti` alongside the user identity so you can trace actions back to a specific session.
229* **Audit as the session**: record the `ccr:session_id` and `jti` alongside the creator identity so you can trace actions back to a specific session.
230230
231231## Related environment variables
232232
No line in this hunk matches that.