- The agent must be allowed to run
/login,auth login, or a device-code flow. - The real OAuth token response must not become readable by the sandboxed agent.
- Later API calls still need to authenticate successfully.
When to use this
Use sandboxed OAuth logins when all of these are true:- The tool has an interactive OAuth login flow.
- The tool needs to persist a session for future runs.
- You do not want to grant the sandbox access to raw OAuth tokens, API keys, or host credential stores just so login works.
What the sandbox sees
The sandbox sees values shaped like credentials, but they are not credentials:A JWT-shaped phantom is still a phantom. It exists for clients that locally
parse
id_token as a JWT. The payload is synthetic and must not contain
provider-specific secrets. Use jwt only for fields the client parses
locally and does not later send as a bearer token.The simplest profile
Start with a profile that allows the tool to run normally and enables network access. Save this as~/.config/nono/profiles/my-agent-oauth.json.
This example shows only the OAuth capture pieces:
How the pieces fit
credential_providers
A provider describes where login tokens come from and which API origins may use
the resulting phantoms.
token_endpoints
Each token endpoint is an HTTPS origin plus an absolute path. Path matching is
exact after the query string is removed, so configure every token-bearing path
the client may use.
access_token, refresh_token, or id_token, nono fails closed instead of
streaming real token material back to the sandbox. This is a backstop, not
configuration discovery; use debug logs to add the correct token_endpoints and
response_fields.
response_fields
response_fields declares which JSON fields contain real token material.
opaque for normal bearer tokens and refresh tokens. Use jwt only when
the client locally parses that field as a JWT, usually id_token. Do not mark
an access token as jwt if the client will later resend the whole JWT as
Authorization: Bearer ...; the synthetic value is not a provider-valid JWT.
request_body
When the client refreshes or exchanges tokens, it may send the phantom back to
the token endpoint. nono must resolve that phantom before forwarding upstream.
request_body controls how nono parses the outgoing token request:
Use
auto first unless you know the client requires a specific format.
request_nonce_fields
These are request fields where nono should look for phantoms before forwarding
a token refresh or exchange request.
api_hosts and credential_routes
api_hosts declares where phantoms may be resolved:
credential_routes binds a provider to one or more proxy routes:
nono_<64hex> and use it directly against the provider.
Add endpoint policy
Once the basic flow works, restrict what the logged-in agent can call. For example, allow only message creation:Add client-specific CA variables
OAuth capture uses TLS interception for the configured token hosts. nono writes a temporary trust bundle and sets the common CA environment variables. Some clients need their own CA variable. Add it in the profile:Add credential-store detection
Some tools persist auth state in a known JSON file or credential-store entry. You can describe that store so nono can create needed parent directories and detect whether expected fields contain phantoms. File JSON example:Add helper commands
Helpers document how a user should check login state or start login/logout. They are command arrays, not shell strings:Codex-style device auth
Codex device auth uses OpenAI auth endpoints and may parseid_token locally as
a JWT. A profile for that flow needs:
- both token endpoints
- JWT-shaped phantom output for
id_token formrequest body parsing- Codex’s CA bundle variable
~/.config/nono/profiles/codex-oauth.json:
Claude Code-style OAuth
Claude Code’s OAuth flow exchanges tokens through Claude hosts and then calls Anthropic API hosts. A profile for that flow usually needs:- token endpoints for
claude.ai,claude.com, andplatform.claude.com - API host access for
https://api.anthropic.com - browser opening for the login command
- client state paths such as
~/.claude
Verify what was stored
Do not print token values. Check only shape. For a JSON auth file:Troubleshooting
Token endpoint is not matched
Enable debug logging:token_endpoints.
The client fails TLS verification
Add the client’s CA environment variable undernetwork.tls_intercept.ca_env_vars. Keep the standard CA variables in place;
this setting adds client-specific names.
The client says an ID token is invalid
If the failing field isid_token, change its response field kind from
opaque to jwt:
Refresh fails after login
Setrequest_body to the format the client actually uses:
The client tries to read Keychain or another credential store
Do not grant broad credential-store access as the first fix. First verify that the OAuth token response was captured and that the client stored phantoms. If the client truly requires a native credential store for session metadata, grant only the minimum client-specific paths or system services and confirm stored token fields remain phantom-shaped.Security model
Sandboxed OAuth login is designed around three boundaries:- Real OAuth token responses are rewritten before the sandbox sees them.
- Phantoms resolve only through admitted proxy consumers.
- Token capture endpoints fail closed when nono cannot safely rewrite the response.
response_fields; do not rely on the backstop as
the primary control.
That means a successful login should not give the agent a transferable bearer
token. The agent can keep working through the nono-mediated route, but it cannot
take nono_<64hex> and authenticate directly with the provider.