Skip to content

feat(auth): support cloak config in OAuth credential JSON files#2709

Open
nyakojiru wants to merge 1 commit intorouter-for-me:devfrom
nyakojiru:feat/oauth-cloak-config
Open

feat(auth): support cloak config in OAuth credential JSON files#2709
nyakojiru wants to merge 1 commit intorouter-for-me:devfrom
nyakojiru:feat/oauth-cloak-config

Conversation

@nyakojiru
Copy link
Copy Markdown

Summary

  • Adds support for reading cloak configuration from OAuth credential JSON files (created via --claude-login)
  • Previously, cloak.mode was only configurable for claude-api-key entries in config.yaml — OAuth credentials always used the default "auto" mode, injecting the Claude Code system prompt into every request
  • This change extracts the cloak object from auth file metadata and populates auth attributes (cloak_mode, cloak_strict_mode, cloak_sensitive_words, cloak_cache_user_id) so that getCloakConfigFromAuth() can read them at runtime

Usage

Add a "cloak" field to the OAuth credential JSON file in the auth directory:

{
  "type": "claude",
  "email": "user@example.com",
  "cloak": {
    "mode": "never"
  }
}

Supported fields mirror the claude-api-key cloak config:

  • mode: "auto" (default), "always", or "never"
  • strict-mode: boolean
  • sensitive-words: array of strings
  • cache-user-id: boolean

Problem

When using OAuth credentials (--claude-login), downstream applications sending their own system prompts had the Claude Code system prompt (~1,378 tokens) prepended to every request. This caused:

  • Token waste on every request
  • Conflicting identity framing (model identified as "Claude Code" instead of following the app's system prompt)
  • No way to disable this behavior for OAuth credentials

Changes

  • internal/watcher/synthesizer/file.go: Extract cloak config from metadata during auth synthesis (runtime code path)
  • sdk/auth/filestore.go: Extract cloak config from metadata during file reading (SDK code path)

Test plan

  • Verified cloak_mode attribute is populated from auth JSON file on startup
  • Tested with cloak.mode: "never" — prompt tokens dropped from ~1,477 to 23 (clean passthrough)
  • Confirmed all cloak field types parse correctly (string mode, bool strict-mode, array sensitive-words, bool cache-user-id)
  • Build passes with no errors

🤖 Generated with Claude Code

The cloak configuration (mode, strict-mode, sensitive-words, cache-user-id)
was only supported for claude-api-key entries in config.yaml. OAuth
credentials from --claude-login had no way to control cloaking behavior,
forcing all OAuth requests to use the default "auto" mode which injects
the Claude Code system prompt.

This change extracts cloak configuration from the "cloak" object in OAuth
auth JSON files and populates the corresponding auth attributes so that
getCloakConfigFromAuth can read them at runtime.

Usage: Add a "cloak" field to the OAuth credential JSON file:
  {"cloak": {"mode": "never"}}

Supported fields mirror the claude-api-key cloak config:
  - mode: "auto" (default), "always", or "never"
  - strict-mode: boolean
  - sensitive-words: array of strings
  - cache-user-id: boolean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

This pull request targeted main.

The base branch has been automatically changed to dev.

@github-actions github-actions bot changed the base branch from main to dev April 11, 2026 12:02
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the extraction of cloak configuration from auth file metadata into attributes in both the synthesizer and the SDK's file token store. The reviewer recommends refactoring this duplicated logic into a shared utility function to improve maintainability and suggests using string trimming for more robust configuration parsing.

Comment on lines +160 to +183
// Extract cloak configuration from auth file metadata into attributes
// so that getCloakConfigFromAuth can read them for OAuth credentials.
if cloakObj, ok := metadata["cloak"].(map[string]any); ok {
if mode, ok := cloakObj["mode"].(string); ok && mode != "" {
a.Attributes["cloak_mode"] = mode
}
if strict, ok := cloakObj["strict-mode"].(bool); ok && strict {
a.Attributes["cloak_strict_mode"] = "true"
}
if words, ok := cloakObj["sensitive-words"].([]any); ok && len(words) > 0 {
var parts []string
for _, w := range words {
if s, ok := w.(string); ok {
parts = append(parts, s)
}
}
if len(parts) > 0 {
a.Attributes["cloak_sensitive_words"] = strings.Join(parts, ",")
}
}
if cache, ok := cloakObj["cache-user-id"].(bool); ok && cache {
a.Attributes["cloak_cache_user_id"] = "true"
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for extracting cloak configuration from metadata is duplicated here and in sdk/auth/filestore.go. To improve maintainability and ensure consistency, consider refactoring this into a shared utility function within the sdk/cliproxy/auth package (e.g., ApplyCloakConfigFromMetadata), similar to how ApplyCustomHeadersFromMetadata is implemented. This would ensure that any future changes to the cloak configuration schema are applied consistently across both the runtime and SDK paths.

Additionally, consider using strings.TrimSpace when parsing the mode and sensitive-words to make the configuration more robust, consistent with how other fields like prefix or note are handled in this file.

Comment thread sdk/auth/filestore.go
Comment on lines +257 to +280
// Extract cloak configuration from auth file metadata into attributes
// so that getCloakConfigFromAuth can read them for OAuth credentials.
if cloakObj, ok := metadata["cloak"].(map[string]any); ok {
if mode, ok := cloakObj["mode"].(string); ok && mode != "" {
auth.Attributes["cloak_mode"] = mode
}
if strict, ok := cloakObj["strict-mode"].(bool); ok && strict {
auth.Attributes["cloak_strict_mode"] = "true"
}
if words, ok := cloakObj["sensitive-words"].([]any); ok && len(words) > 0 {
var parts []string
for _, w := range words {
if s, ok := w.(string); ok {
parts = append(parts, s)
}
}
if len(parts) > 0 {
auth.Attributes["cloak_sensitive_words"] = strings.Join(parts, ",")
}
}
if cache, ok := cloakObj["cache-user-id"].(bool); ok && cache {
auth.Attributes["cloak_cache_user_id"] = "true"
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block is identical to the one added in internal/watcher/synthesizer/file.go. As suggested there, refactoring this into a shared helper function in the sdk/cliproxy/auth package would avoid code duplication and reduce the risk of logic divergence between the runtime and SDK code paths.

Copy link
Copy Markdown
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary:
This change moves OAuth cloak metadata into auth attributes so getCloakConfigFromAuth() can consume it, which is the right integration point.

Blocking findings:

  • The same auth-file parsing logic still exists in the Git/Object/Postgres stores, and those paths do not populate cloak_mode, cloak_strict_mode, cloak_sensitive_words, or cloak_cache_user_id. As written, the feature only works for the file watcher / SDK filestore paths and stays broken for the supported alternative auth backends.
  • buildAuthFromFileData() in the management auth-file handler still drops the new cloak metadata before upserting the auth into the manager, so files uploaded or updated through the management API will not honor the new settings immediately.

Non-blocking:

  • Please add focused regression tests for the watcher and SDK auth-file loaders so this mapping is covered explicitly.

Recommendation:
Please move the cloak extraction into a shared helper and reuse it across every auth JSON ingestion path before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants