Skip to content

fix(auth): align file auth ids and callback auth directory handling#1921

Open
shenshuoyaoyouguang wants to merge 3 commits intorouter-for-me:mainfrom
shenshuoyaoyouguang:pr/auth-dir-consistency
Open

fix(auth): align file auth ids and callback auth directory handling#1921
shenshuoyaoyouguang wants to merge 3 commits intorouter-for-me:mainfrom
shenshuoyaoyouguang:pr/auth-dir-consistency

Conversation

@shenshuoyaoyouguang
Copy link
Contributor

Summary

  • normalize file-backed auth ids through a shared helper
  • align management callback files with the effective runtime auth directory
  • keep management auth file listing and watcher synthesizer behavior consistent

Business value

  • avoids duplicate or mismatched file auth records, especially on Windows and nested auth paths
  • reduces support issues where callback files land in the configured directory while runtime uses a mirrored directory
  • isolates auth-dir consistency work from broader watcher and executor changes

Tests

  • go test ./internal/api/handlers/management ./internal/api ./internal/watcher/synthesizer ./sdk/auth

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the handling of file-based authentication, primarily by standardizing how authentication file IDs are generated and how the effective authentication directory is determined. These changes aim to prevent issues like duplicate authentication records, especially in environments with case-insensitive file systems or mirrored directories. By ensuring consistency in path resolution and ID normalization, the system becomes more robust and predictable, reducing potential support burdens and isolating directory consistency work from broader system changes.

Highlights

  • Normalized File-Backed Auth IDs: Introduced a shared helper function, NormalizeFileAuthID, to ensure consistent identification of file-backed authentication records across different operating systems, particularly addressing case-insensitivity on Windows.
  • Aligned Callback Auth Directory Handling: Modified management API handlers and OAuth callback mechanisms to consistently use an 'effective' runtime authentication directory, which can be overridden by token stores with mirrored workspaces, preventing discrepancies between configured and actual runtime paths.
  • Consistent Management Auth File Listing and Watcher Synthesizer Behavior: Updated the logic for listing authentication files and synthesizing authentication entries from files to reflect the new effective auth directory and normalized IDs, ensuring that the management interface and file watcher operate on a unified view of authentication data.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/api/handlers/management/auth_files.go
    • Removed unused 'runtime' import.
    • Updated ListAuthFiles to use effectiveAuthDir() and a pre-built file info index for efficiency and consistency.
    • Modified listAuthFilesFromDisk to retrieve files from the effectiveAuthDir().
    • Adjusted buildAuthFileEntry to accept and utilize the effective auth directory and file info index for accurate file metadata retrieval.
    • Introduced buildAuthDirFileInfoIndex and resolveAuthFileInfo helper functions to efficiently gather and resolve file information within the effective auth directory.
    • Updated DownloadAuthFile, UploadAuthFile, and DeleteAuthFile to consistently use the effectiveAuthDir() for file operations.
    • Refactored authIDForPath to leverage the new sdkAuth.NormalizeFileAuthID for consistent ID generation.
    • Modified OAuth token request handlers (RequestAnthropicToken, RequestGeminiCLIToken, RequestCodexToken, RequestAntigravityToken, RequestIFlowToken, RequestIFlowCookieToken) to use effectiveAuthDir() for callback file paths.
  • internal/api/handlers/management/auth_files_list_test.go
    • Added new test cases for ListAuthFiles to verify correct handling of managed files, nested paths, and hiding of deleted files, ensuring the effective auth directory and snapshot logic work as expected.
  • internal/api/handlers/management/auth_files_test.go
    • Added a new test for authIDForPath to confirm its integration with the shared sdkAuth.NormalizeFileAuthID normalization logic.
  • internal/api/handlers/management/effective_auth_dir_test.go
    • Added new test cases to validate that PostOAuthCallback and authIDForPath correctly utilize the effective authentication directory, especially when a mirrored directory is in use.
  • internal/api/handlers/management/handler.go
    • Introduced a stateMu (RWMutex) to ensure thread-safe access and updates to handler state variables.
    • Defined an authDirProvider interface and implemented ResolveEffectiveAuthDir to determine the runtime authentication directory, allowing token stores to override the configured directory.
    • Added StateMiddleware to serialize management runtime state access for request handlers, hot-reload updates, and persistence.
    • Updated SetConfig, SetAuthManager, SetUsageStatistics, SetLocalPassword, SetLogDirectory, and SetPostAuthHook methods to use stateMu for synchronized state modifications.
    • Implemented effectiveAuthDir() method to provide the resolved runtime authentication directory.
  • internal/api/handlers/management/oauth_callback.go
    • Modified PostOAuthCallback to use h.effectiveAuthDir() when writing OAuth callback files, ensuring consistency with the runtime auth directory.
  • internal/api/server.go
    • Imported configaccess for access manager configuration.
    • Created a new helper function writePendingOAuthCallbackFile to encapsulate the logic for writing OAuth callback files using the effective auth directory.
    • Enhanced NewServer to initialize a default access manager and register providers if none is provided.
    • Updated all OAuth callback routes (/oauth/anthropic, /oauth/codex, /oauth/gemini, /oauth/iflow, /oauth/antigravity) to use the new writePendingOAuthCallbackFile helper.
    • Applied s.mgmt.StateMiddleware() to management routes to ensure serialized access to management handler state.
    • Modified AuthMiddleware to return an internal server error if the access manager is nil, preventing misconfiguration issues.
  • internal/api/server_oauth_callback_test.go
    • Added a new test case to verify that writePendingOAuthCallbackFile correctly uses the effective authentication directory, particularly in scenarios with mirrored directories.
  • internal/watcher/synthesizer/file.go
    • Removed unused 'runtime' import.
    • Imported sdkauth for file ID normalization.
    • Refactored Synthesize to delegate the creation of auth entries for individual files to a new synthesizeFileAuths function.
    • Introduced SynthesizeAuthFile and synthesizeFileAuths functions to centralize the logic for generating authentication entries from file payloads.
    • Updated the ID generation within synthesizeFileAuths to use sdkauth.NormalizeFileAuthID, ensuring consistent ID mapping.
  • internal/watcher/synthesizer/file_test.go
    • Imported sdkauth for testing normalization.
    • Added a new test case TestSynthesizeAuthFile_UsesNormalizedWindowsAuthID to confirm that synthesized auth IDs are correctly normalized for Windows paths.
  • sdk/auth/file_id.go
    • Added a new file containing NormalizeFileAuthID and normalizeFileAuthIDForOS functions.
    • Implemented NormalizeFileAuthID to provide a canonical ID for file-backed authentication entries, handling relative paths and case-insensitivity for Windows.
  • sdk/auth/file_id_test.go
    • Added a new test file for NormalizeFileAuthIDForOS to verify its behavior across different operating systems, specifically for Windows and Linux path normalization.
  • sdk/auth/filestore.go
    • Removed unused 'runtime' import.
    • Updated the idFor method to utilize the new NormalizeFileAuthID function, centralizing file ID normalization logic.
Activity
  • No specific activity (comments, reviews, or progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 effectively refactors the authentication file handling logic, centralizing the auth ID normalization into a shared sdkAuth.NormalizeFileAuthID helper and consistently using an "effective auth directory" throughout the codebase. It also introduces a mutex in the management handler to prevent race conditions during hot-reloads, and includes comprehensive new tests. However, a critical path traversal vulnerability was identified in the DownloadAuthFile handler, specifically affecting Windows deployments due to inconsistent path separator validation. It is recommended to apply similar sanitization, like using filepath.Base, as seen in UploadAuthFile and DeleteAuthFile handlers, to mitigate this risk. Additionally, there is a suggestion to improve error visibility for a potential performance issue. Overall, this is a high-quality contribution, but the identified security vulnerability in DownloadAuthFile needs immediate attention.

return
}
full := filepath.Join(h.cfg.AuthDir, name)
full := filepath.Join(h.effectiveAuthDir(), name)
Copy link
Contributor

Choose a reason for hiding this comment

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

security-high high

The DownloadAuthFile handler is vulnerable to path traversal on Windows systems. The check strings.Contains(name, string(os.PathSeparator)) only validates against the backslash (\) separator on Windows, but filepath.Join also recognizes the forward slash (/) as a separator. An authenticated attacker can use forward slashes in the name query parameter (e.g., ?name=../../sensitive.json) to bypass the check and read arbitrary .json files outside the intended authentication directory. It is recommended to use filepath.Base(name) to ensure only the filename is used, preventing any directory traversal attempts.

Suggested change
full := filepath.Join(h.effectiveAuthDir(), name)
full := filepath.Join(h.effectiveAuthDir(), filepath.Base(name))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a491a71. DownloadAuthFile now rejects names containing either separator style or drive prefixes through a stricter simple-filename guard, so Windows /-based traversal no longer slips through.

Comment on lines +504 to +506
if err != nil {
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The error from filepath.Walk is silently ignored here. If walking the authDir fails (e.g., due to an incorrect path or permissions), buildAuthDirFileInfoIndex will return nil. The caller then falls back to os.Stat for every auth file, which is a silent performance degradation and can hide configuration issues. I suggest logging this error to aid in debugging.

Suggested change
if err != nil {
return nil
}
if err != nil {
log.WithError(err).Warnf("failed to build auth dir file index for %s", cleanAuthDir)
return nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in a491a71. I kept the fallback behavior but added explicit warning logs for per-entry walk errors, so auth-dir traversal failures are now observable instead of silently degrading into repeated os.Stat calls.

@shenshuoyaoyouguang shenshuoyaoyouguang marked this pull request as ready for review March 7, 2026 09:39
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@shenshuoyaoyouguang
Copy link
Contributor Author

/gemini review

Copy link
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 is a significant and well-executed refactoring that centralizes authentication ID normalization and aligns auth directory handling across different parts of the application. The introduction of a shared NormalizeFileAuthID helper and the consistent use of an effectiveAuthDir greatly improve code clarity, maintainability, and correctness, especially for Windows and nested path scenarios. The added tests are comprehensive and cover the new logic well. I've included a few suggestions to further enhance robustness and performance.

Comment on lines +204 to +209
if accessManager == nil {
log.Warn("access manager was nil, creating a default manager")
accessManager = sdkaccess.NewManager()
configaccess.Register(&cfg.SDKConfig)
accessManager.SetProviders(sdkaccess.RegisteredProviders())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Creating a default accessManager here prevents a nil pointer dereference, but it might hide a configuration error where the caller intended to provide a specific manager but failed to do so. This could lead to the server running with incorrect or incomplete access control.
To make such configuration errors explicit and fail fast, consider changing NewServer to return an error if accessManager is nil, rather than creating a default one.

Comment on lines +125 to +131
func (h *Handler) StateMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
h.stateMu.Lock()
defer h.stateMu.Unlock()
c.Next()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using a write lock (Lock()) for all requests in this middleware ensures thread safety but serializes all management API calls, which can be a performance bottleneck for concurrent read-only requests. Since stateMu is an RWMutex, you can improve concurrency by using a read lock (RLock()) for read-only methods like GET and HEAD.

This would require ensuring that any functions called by these handlers are free of write side-effects. For example, the lazy initialization in tokenStoreWithBaseDir would need to be made thread-safe if it can be called under a read lock.

func (h *Handler) StateMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		switch c.Request.Method {
		case http.MethodGet, http.MethodHead, http.MethodOptions:
			h.stateMu.RLock()
			defer h.stateMu.RUnlock()
		default:
			h.stateMu.Lock()
			defer h.stateMu.Unlock()
		}
		c.Next()
	}
}

Comment on lines +21 to +23
if rel, errRel := filepath.Rel(baseDir, id); errRel == nil && rel != "" {
id = rel
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The error from filepath.Rel is currently ignored. If filepath.Rel fails, the function falls back to using the original path as the ID, which might be unexpected.
Additionally, if path and baseDir refer to the same file, filepath.Rel returns . which is likely not a desirable auth ID.
Consider handling these cases for more robust and predictable ID generation.

Suggested change
if rel, errRel := filepath.Rel(baseDir, id); errRel == nil && rel != "" {
id = rel
}
if rel, errRel := filepath.Rel(baseDir, id); errRel == nil && rel != "" && rel != "." {
id = rel
}

@shenshuoyaoyouguang
Copy link
Contributor Author

/gemini review

Copy link
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 refactors and standardizes file-based authentication paths and identifiers, introducing NormalizeFileAuthID and effectiveAuthDir for consistency, and includes performance improvements and security hardening against path traversal. However, it introduces a new middleware with an overly broad exclusive lock, posing a DoS risk to the management API. Additionally, fragile OAuth callback logic could lead to a process-crashing panic if malformed authentication files are uploaded. There is also a suggestion regarding the choice of mutex type for concurrency safety.


// Helper: wait for callback file
waitFile := filepath.Join(h.cfg.AuthDir, fmt.Sprintf(".oauth-anthropic-%s.oauth", state))
waitFile := filepath.Join(h.effectiveAuthDir(), fmt.Sprintf(".oauth-anthropic-%s.oauth", state))
Copy link
Contributor

Choose a reason for hiding this comment

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

security-high high

The OAuth callback handling logic is vulnerable to a Denial of Service (DoS) attack via a runtime panic. The waitForFile function (and similar logic in other OAuth flows) ignores the error from json.Unmarshal. If the callback file contains invalid JSON or is empty, the resultMap variable remains nil. Subsequent attempts to access keys in this map (e.g., resultMap["error"] on line 1138) will cause a runtime panic, crashing the entire server process. An attacker with management API access can exploit this by uploading a malformed file to the authentication directory using the UploadAuthFile endpoint with a name that matches the expected OAuth callback pattern (e.g., .oauth-anthropic-<state>.oauth).

Comment on lines +127 to +129
h.stateMu.Lock()
defer h.stateMu.Unlock()
c.Next()
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The StateMiddleware introduced in this PR uses an exclusive lock (h.stateMu.Lock()) that is held for the entire duration of every management API request, including the execution of the handler (c.Next()). This creates a significant Denial of Service (DoS) vector. An authenticated attacker can initiate a slow request (e.g., a large file upload or a request with a slow body stream) to any management endpoint, which will hold the lock and block all other management API requests until the first request completes. This effectively allows a single user to freeze the management interface for all other administrators.

cfg *config.Config
configFilePath string
mu sync.Mutex
stateMu sync.RWMutex
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The stateMu is declared as a sync.RWMutex, but it is only ever used with Lock(). There are no calls to RLock(). Using a sync.Mutex would be more idiomatic and slightly more efficient in this case, as it more clearly communicates the intent of exclusive access for all protected operations.

Suggested change
stateMu sync.RWMutex
 stateMu sync.Mutex

@shenshuoyaoyouguang
Copy link
Contributor Author

Review Navigation: This PR is suitable for priority review. Previous high-risk suggestions regarding DownloadAuthFile path traversal and file auth ID normalization have been addressed. Current CI has passed; remaining items are mainly concurrency read optimizations that do not affect current correctness.

Copy link
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:
Thanks for tightening the auth-dir and file-id handling. The overall direction looks good, but I found one blocking correctness issue in the new state-locking model.

Key findings:

  • Blocking: StateMiddleware() only serializes synchronous Gin request handling, but several management OAuth flows continue in background goroutines and still access handler state after the request returns. At the same time, hot reload updates that same state via SetConfig() and SetAuthManager() in two separate steps. This means the code can still observe mixed runtime state even though the new middleware/comment suggests otherwise.
  • Non-blocking: nested auth entries are now listed, but single-file management endpoints still reject separator-containing names, and the disk-fallback / delete-all paths are still top-level only. That leaves nested auth handling inconsistent across management paths.

Test plan:

  • go test ./internal/api/handlers/management ./internal/api ./internal/watcher/synthesizer ./sdk/auth

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