This document explains how authentication works in J-Code.
J-Code can detect existing local credentials and can also run built-in OAuth and API-key login flows.
For auth files managed by other tools/CLIs, jcode asks before reading them. If you approve a source, jcode remembers that approval for that external auth file path for future sessions and still leaves the original file untouched (no move, rewrite, or permission mutation). Symlinked external auth files are rejected.
Credentials are stored locally:
- J-Code Claude OAuth (if logged in via
jcode login --provider claude):~/.jcode/auth.json - Claude Code CLI:
~/.claude/.credentials.json - OpenCode (optional provider/OAuth import source):
~/.local/share/opencode/auth.json - pi (optional provider/OAuth import source):
~/.pi/agent/auth.json - J-Code OpenAI/Codex OAuth:
~/.jcode/openai-auth.json - Codex CLI auth source (read in place only after confirmation):
~/.codex/auth.json - Gemini native OAuth:
~/.jcode/gemini_oauth.json - Gemini CLI import fallback:
~/.gemini/oauth_creds.json - Copilot CLI plaintext fallback:
~/.copilot/config.json - Legacy Copilot JSON sources:
~/.config/github-copilot/hosts.json,~/.config/github-copilot/apps.json
Relevant code:
- Claude provider:
src/provider/claude.rs - OpenAI login + refresh:
src/auth/oauth.rs - OpenAI credentials parsing:
src/auth/codex.rs - OpenAI requests:
src/provider/openai.rs - Azure OpenAI auth/config:
src/auth/azure.rs - Azure OpenAI transport:
src/provider/openrouter.rs - Gemini login + refresh:
src/auth/gemini.rs - Gemini Code Assist provider:
src/provider/gemini.rs - OpenAI-compatible provider metadata/login descriptors:
crates/jcode-provider-metadata/src/lib.rs
- Run
jcode login --provider claude(recommended), orjcode loginand choose Claude.- For headless / SSH use:
jcode login --provider claude --no-browser - For scriptable remote flows:
jcode login --provider claude --print-auth-url, then later complete with--callback-urlor--auth-code
- For headless / SSH use:
- Alternative: run
claude(orclaude setup-token). jcode can detect~/.claude/.credentials.json, ask before reading it, and remember that approval for future sessions. - Verify with
jcode --provider claude run "Say hello from jcode".
Credential discovery order is:
~/.jcode/auth.json~/.claude/.credentials.json~/.local/share/opencode/auth.json~/.pi/agent/auth.json
--provider claude uses the direct Anthropic Messages API by default.
jcode owns the full runtime path itself: auth, refresh, request shaping, tool
compatibility, and transport.
Claude Code OAuth tokens can be used directly against the Messages API, but only if the request matches the Claude Code "OAuth contract". jcode applies this automatically for the default Claude runtime path.
Required behaviors (applied by the Anthropic provider):
- Use the Messages endpoint with
?beta=true. - Send
User-Agent: claude-cli/1.0.0. - Send
anthropic-beta: oauth-2025-04-20,claude-code-20250219. - Prepend the system blocks with the Claude Code identity line as the first
block:
You are Claude Code, Anthropic's official CLI for Claude.
Tool name allow-list: Claude OAuth requests reject certain tool names. jcode remaps tool names on the wire and maps them back on responses so native tools continue to work. The mapping is:
bash→shell_execread→file_readwrite→file_writeedit→file_editglob→file_globgrep→file_greptask→task_runnertodoread→todo_readtodowrite→todo_write
Notes:
- If the OAuth token expires, refresh via the Claude OAuth refresh endpoint.
- Without the identity line and allow-listed tool names, the API will reject OAuth requests even if the token is otherwise valid.
The old Claude CLI shell-out path is deprecated and should only be used for legacy compatibility.
You can still force it temporarily with:
JCODE_USE_CLAUDE_CLI=1- or
--provider claude-subprocess(deprecated hidden compatibility value)
These environment variables control the deprecated Claude Code CLI transport:
JCODE_CLAUDE_CLI_PATH(default:claude)JCODE_CLAUDE_CLI_MODEL(default:claude-opus-4-5-20251101)JCODE_CLAUDE_CLI_PERMISSION_MODE(default:bypassPermissions)JCODE_CLAUDE_CLI_PARTIAL(set to0to disable partial streaming)
- Run
jcode login --provider openai.- For headless / SSH use:
jcode login --provider openai --no-browser - For scriptable remote flows:
jcode login --provider openai --print-auth-url, then later complete with--callback-url
- For headless / SSH use:
- Your browser opens to the OpenAI OAuth page unless you use
--no-browser. The local callback listens onhttp://localhost:1455/auth/callbackby default. If port1455is unavailable, jcode falls back to a manual paste flow where you can paste the full callback URL or query string. - After login, tokens are saved to
~/.jcode/openai-auth.json.
Credential discovery order is:
~/.jcode/openai-auth.json~/.codex/auth.json- trusted OpenCode/pi OAuth in
~/.local/share/opencode/auth.json/~/.pi/agent/auth.json OPENAI_API_KEY
If jcode finds existing credentials in ~/.codex/auth.json, it asks before
reading them. When approved, it remembers that trust decision for future jcode
sessions and still does not move, delete, or rewrite the Codex file.
J-Code uses the Responses API. If you have a ChatGPT subscription (refresh token or id_token present), requests go to:
https://chatgpt.com/backend-api/codex/responseswith headers:originator: codex_cli_rschatgpt-account-id: <from token>
Otherwise it uses:
https://api.openai.com/v1/responses
- Claude 401/auth errors: run
jcode login --provider claude. - 401/403: re-run
jcode login --provider openai. - Callback issues: make sure port 1455 is free and the browser can reach
http://localhost:1455/auth/callback.
This was added after comparing J-Code to OpenCode/Crush. The meaningful auth gap was not another browser OAuth flow, but support for Azure OpenAI using either:
- Microsoft Entra ID credentials (via Azure's
DefaultAzureCredentialchain), or - Azure OpenAI API keys.
- Run
jcode login --provider azure. - Enter your Azure OpenAI endpoint, for example:
https://your-resource.openai.azure.com
- Enter your Azure deployment/model name.
- Choose one auth mode:
- Entra ID (recommended)
- API key
- jcode saves settings to
~/.config/jcode/azure-openai.env.
The Azure env file may contain:
AZURE_OPENAI_ENDPOINTAZURE_OPENAI_MODELAZURE_OPENAI_USE_ENTRAAZURE_OPENAI_API_KEY(only when using key auth)
- jcode normalizes the endpoint to the newer Azure OpenAI
/openai/v1base. - In Entra ID mode, jcode obtains bearer tokens using
azure_identity::DefaultAzureCredentialwith scope:https://cognitiveservices.azure.com/.default
- In API key mode, jcode sends the credential in the Azure-style
api-keyheader. - The Azure provider currently reuses J-Code's OpenAI-compatible transport layer under the hood.
- Model catalog fetching is disabled for Azure by default, so you should configure a deployment/model explicitly.
DefaultAzureCredential can resolve credentials from sources like:
az login- managed identity
- Azure environment credentials
- If Entra ID auth fails locally, try
az loginfirst. - Make sure your identity has access to the Azure OpenAI resource.
- If requests fail with deployment/model errors, verify
AZURE_OPENAI_MODELmatches your deployed model name. - If you prefer static credentials, re-run
jcode login --provider azureand choose API key mode.
- Run
jcode login --provider geminior/login geminiinside the TUI.- For headless / SSH use:
jcode login --provider gemini --no-browser - For scriptable remote flows:
jcode login --provider gemini --print-auth-url, then later complete with--auth-code
- For headless / SSH use:
- jcode opens a browser to the Google OAuth flow used for Gemini Code Assist unless you use
--no-browser. - If local callback binding is unavailable, jcode falls back to a manual paste flow using
https://codeassist.google.com/authcode. - Tokens are saved to
~/.jcode/gemini_oauth.json.
- Native jcode Gemini tokens:
~/.jcode/gemini_oauth.json - Gemini CLI OAuth source (read only after approval):
~/.gemini/oauth_creds.json - trusted OpenCode/pi OAuth in
~/.local/share/opencode/auth.json/~/.pi/agent/auth.json
- jcode uses native Google OAuth and talks to the Google Code Assist backend directly.
- Expired tokens are refreshed automatically using the Google refresh token.
- Some school / Workspace accounts may require
GOOGLE_CLOUD_PROJECTorGOOGLE_CLOUD_PROJECT_IDfor Code Assist entitlement checks.
- If browser launch fails, use
--no-browserand the pasted callback/code flow. - If entitlement or onboarding fails for a Workspace account, set
GOOGLE_CLOUD_PROJECTand retry. - If login succeeds but requests fail later, re-run
jcode login --provider geminito refresh the stored session.
Use the built-in auth verifier to test the full local auth/runtime path after login:
# Run Gemini login now, then verify token refresh + provider smoke
jcode --provider gemini auth-test --login
# Verify existing Gemini auth without re-running login
jcode --provider gemini auth-test
# Check every currently configured supported auth provider
jcode auth-test --all-configuredFor model providers, auth-test attempts:
- credential discovery
- refresh/auth probe
- a real provider smoke prompt expecting
AUTH_TEST_OK - a tool-enabled smoke prompt using the same tool-attached request path as normal chat
Use --no-tool-smoke if you only want the auth/simple-runtime checks.
For Gmail/Google it verifies credential discovery and token refresh, but skips model smoke because it is not a model provider.
J-Code also ships first-class provider presets for many OpenAI-compatible APIs.
These providers use the same built-in login flow pattern: jcode login --provider <name>.
For arbitrary OpenAI-compatible APIs, especially when an agent is doing setup, prefer the named profile command instead of hand-editing config:
printf '%s' "$MY_API_KEY" | jcode provider add my-api \
--base-url https://llm.example.com/v1 \
--model my-model-id \
--api-key-stdin \
--set-default \
--json
jcode --provider-profile my-api auth-test --no-tool-smokeThis writes [providers.my-api] in ~/.jcode/config.toml and stores the key in jcode's private app config dir, for example ~/.config/jcode/provider-my-api.env. For localhost servers, use --no-api-key.
Two notable presets are:
- Login:
jcode login --provider fireworks - Stored env file:
~/.config/jcode/fireworks.env - API key env var:
FIREWORKS_API_KEY - Base URL:
https://api.fireworks.ai/inference/v1 - Default model hint:
accounts/fireworks/routers/kimi-k2p5-turbo - Docs: https://docs.fireworks.ai/tools-sdks/openai-compatibility
- Login:
jcode login --provider minimax - Stored env file:
~/.config/jcode/minimax.env - API key env var:
MINIMAX_API_KEY - Base URL:
https://api.minimax.io/v1 - Default model hint:
MiniMax-M2.7 - Docs: https://platform.minimax.io/docs/api-reference/text-openai-api
These are first-class jcode provider presets, not just manual custom endpoint examples.
You can still use openai-compatible for arbitrary custom providers when there is not a built-in preset.
If jcode finds matching API keys in trusted OpenCode/pi auth files, it can reuse them for the corresponding provider preset without asking you to paste the key again.
J-Code also supports experimental CLI-backed providers, plus Antigravity with native OAuth login:
--provider cursor--provider copilot--provider antigravity
Cursor and Copilot use each provider's local CLI session/auth and shell out in print mode. Antigravity still uses a CLI-backed runtime transport today, but login/auth storage is handled natively by jcode.
- Login:
jcode login --provider cursor- fast path: runs
cursor-agent login - fallback: saves
CURSOR_API_KEYto~/.config/jcode/cursor.env
- fast path: runs
- Runtime:
- jcode shells out to
cursor-agent - if a Cursor API key is configured, jcode injects it via
CURSOR_API_KEY cursor-agent statusis used to probe whether a local CLI session is authenticated
- jcode shells out to
- Env vars:
JCODE_CURSOR_CLI_PATH(default:cursor-agent)JCODE_CURSOR_MODEL(default:gpt-5)CURSOR_API_KEY(optional; overrides saved key)
- Login:
jcode login --provider copilot- Headless / SSH:
jcode login --provider copilot --no-browser - Scriptable remote flow:
jcode login --provider copilot --print-auth-url, then laterjcode login --provider copilot --complete - jcode uses GitHub device code flow and can print the verification URL/QR without opening a local browser.
- Headless / SSH:
- Credential discovery order:
COPILOT_GITHUB_TOKENGH_TOKENGITHUB_TOKEN- trusted
~/.copilot/config.json - trusted legacy
~/.config/github-copilot/hosts.json - trusted legacy
~/.config/github-copilot/apps.json - trusted OpenCode/pi OAuth entries
gh auth token
- Env vars:
JCODE_COPILOT_CLI_PATH(optional override for CLI path)JCODE_COPILOT_MODEL(default:claude-sonnet-4)
- Login:
jcode login --provider antigravity(native Google OAuth flow; does not require Antigravity to be installed)- Headless / SSH:
jcode login --provider antigravity --no-browser - Scriptable remote flow:
jcode login --provider antigravity --print-auth-url, then later complete with--callback-url
- Headless / SSH:
- Tokens:
~/.jcode/antigravity_oauth.json - Credential discovery order:
- native jcode tokens at
~/.jcode/antigravity_oauth.json - trusted OpenCode/pi OAuth entries when present
- native jcode tokens at
- Runtime:
- jcode authenticates directly and stores/refreshes Antigravity OAuth tokens itself
- the provider transport still shells out to the Antigravity CLI for completions if you choose
--provider antigravity
- Env vars:
JCODE_ANTIGRAVITY_CLIENT_ID(optional override for OAuth client id)JCODE_ANTIGRAVITY_CLIENT_SECRET(optional override for OAuth client secret)JCODE_ANTIGRAVITY_VERSION(optional override for Antigravity request fingerprint/version)JCODE_ANTIGRAVITY_CLI_PATH(default:antigravity, runtime only)JCODE_ANTIGRAVITY_MODEL(default:default)JCODE_ANTIGRAVITY_PROMPT_FLAG(default:-p)JCODE_ANTIGRAVITY_MODEL_FLAG(default:--model)
- Run
jcode login --provider google.- For headless / SSH use:
jcode login --provider google --no-browser - For scriptable remote flows after credentials are already configured:
jcode login --provider google --print-auth-url
- For headless / SSH use:
- If Google credentials are not configured yet, jcode first walks you through saving your client ID/client secret or importing the JSON credentials file.
- For scriptable Google flows, choose the Gmail scope with
--google-access-tier full|readonlyif you do not want the default full access tier. - Complete the printed flow later with
jcode login --provider google --callback-url '<full callback url or query>'.
- Google/Gmail scriptable auth requires saved OAuth client credentials first.
- The callback URL can come from a remote browser session that fails on the loopback redirect. Copy the final URL from the address bar and paste or pass it back to jcode.
- jcode stores temporary scriptable login state in
~/.jcode/pending-login/*.json - pending state expires automatically
- stale pending entries are cleaned up when scriptable login flows start or resume
- Copilot
--print-auth-urlstores the GitHub device code session and--completeresumes polling later