Skip to content

feat(access): add scoped API key viewer dashboard - #298

Open
iqiuzixuan wants to merge 4 commits into
Willxup:mainfrom
iqiuzixuan:feat/api-key-auth-file-viewer
Open

feat(access): add scoped API key viewer dashboard#298
iqiuzixuan wants to merge 4 commits into
Willxup:mainfrom
iqiuzixuan:feat/api-key-auth-file-viewer

Conversation

@iqiuzixuan

Copy link
Copy Markdown

Summary

This PR adds a scoped, read-only dashboard for CPA API Key users.

What changed

  • Added per-API-key auth file visibility scopes configurable from the Settings page.
  • Replaced manual auth file name input with a multi-select dropdown of active auth files.
  • Enabled API Key viewers to access Overview, Analysis, Request Events, and Auth Files.
  • Enforced API key and OAuth auth file scope filtering on the server side for all viewer queries.
  • Limited viewers to cached quota data and blocked quota refreshes, resets, alias changes, raw request log access, and credential downloads.
  • Added a database migration to persist auth file scopes by file name and resolve current auth indexes dynamically.
  • Merged the latest main changes and updated viewer analysis to use canonical cache read and cache creation token fields.

Why

API Key users need visibility into their own usage without gaining access to unrelated auth files, credentials, mutable settings, or sensitive request logs.

Validation

  • GIN_MODE=release go test ./... -count=1
  • npm run typecheck
  • npm run lint
  • npm test -- --run

- Add per-key auth file scope configuration through the settings UI.
- Enforce API key and OAuth scope across dashboard data and cached quotas.
- Keep viewers read-only and deny sensitive request log access.
Preserve scoped viewer access controls with upstream cache token changes.

- Register both database migrations.
- Use canonical cache read and creation tokens in viewer analysis.

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

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.

Code Review

This pull request implements a read-only API Key dashboard that restricts API Key viewers to specific, permitted auth files (OAuth events) scoped by API Key. It introduces database schemas, backend services, and APIs to manage and resolve these scopes (APIKeyAuthFileScope), and updates the frontend UI to support read-only tabs and visible auth file settings. The review feedback highlights a few improvement opportunities: adding defensive nil checks on pointer receivers in usage_identities_service.go and auth.go to prevent potential nil pointer dereference panics, and handling individual errors gracefully when fetching API key scopes in UsagePage.tsx to prevent a single failure from rejecting the entire batch.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +140 to +141
func (s *usageIdentityService) listScopedAuthFileUsageIdentities(ctx context.Context) ([]entities.UsageIdentity, error) {
scope, ok := accessscope.ViewerScopeFromContext(ctx)

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.

high

The pointer receiver s and its field s.db are accessed without a nil check, which could lead to a nil pointer dereference panic if the service is nil. Please add a defensive nil check at the beginning of the function.

Suggested change
func (s *usageIdentityService) listScopedAuthFileUsageIdentities(ctx context.Context) ([]entities.UsageIdentity, error) {
scope, ok := accessscope.ViewerScopeFromContext(ctx)
func (s *usageIdentityService) listScopedAuthFileUsageIdentities(ctx context.Context) ([]entities.UsageIdentity, error) {
if s == nil || s.db == nil {
return nil, errors.New("database is nil")
}
scope, ok := accessscope.ViewerScopeFromContext(ctx)
References
  1. Ensure pointer receivers in Go are checked for nil before accessing their fields or locking their mutexes to prevent nil pointer dereference panics.
  2. In Go, check pointer receivers for nil at the very beginning of a method before performing any state or in-flight checks, to prevent returning semantically misleading errors (such as reporting a reset-in-progress state for an uninitialized service).

Comment thread internal/api/auth.go
return
}
h.clearFailedAttempts(clientKey)
if h.apiKeyAuthFileScopeProvider != nil {

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

Defensively check if h is nil before accessing h.apiKeyAuthFileScopeProvider to prevent a potential nil pointer dereference panic, consistent with other methods in authHandler.

Suggested change
if h.apiKeyAuthFileScopeProvider != nil {
if h != nil && h.apiKeyAuthFileScopeProvider != nil {
References
  1. Ensure pointer receivers in Go are checked for nil before accessing their fields or locking their mutexes to prevent nil pointer dereference panics.

Comment on lines +1493 to +1496
void Promise.all(apiKeySettings.map(async (item) => [
item.id,
await fetchCpaApiKeyAuthFileScopes(item.id, controller.signal),
] as const))

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

Using Promise.all directly means that if fetching the scope for any single API key fails (e.g., due to a temporary network issue or a deleted key), the entire batch will reject, and no scopes will be loaded for any of the keys. Consider handling individual errors gracefully so that successfully fetched scopes can still be displayed.

    void Promise.all(apiKeySettings.map(async (item) => {
      try {
        const response = await fetchCpaApiKeyAuthFileScopes(item.id, controller.signal);
        return [item.id, response.authFileNames ?? []] as const;
      } catch (error) {
        if (error instanceof ApiError && error.status === 401) {
          throw error;
        }
        return [item.id, []] as const;
      }
    }))

Preserve scoped viewer access controls with upstream quota updates.

- Keep viewer quota access cache-only and block reset-credit lookups.
- Register xAI user identity migration alongside auth file scopes.
Preserve scoped API key viewer access controls with current upstream usage and credential updates.
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.

1 participant