feat(access): add scoped API key viewer dashboard - #298
Conversation
- 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.
There was a problem hiding this comment.
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.
| func (s *usageIdentityService) listScopedAuthFileUsageIdentities(ctx context.Context) ([]entities.UsageIdentity, error) { | ||
| scope, ok := accessscope.ViewerScopeFromContext(ctx) |
There was a problem hiding this comment.
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.
| 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
- Ensure pointer receivers in Go are checked for nil before accessing their fields or locking their mutexes to prevent nil pointer dereference panics.
- 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).
| return | ||
| } | ||
| h.clearFailedAttempts(clientKey) | ||
| if h.apiKeyAuthFileScopeProvider != nil { |
There was a problem hiding this comment.
Defensively check if h is nil before accessing h.apiKeyAuthFileScopeProvider to prevent a potential nil pointer dereference panic, consistent with other methods in authHandler.
| if h.apiKeyAuthFileScopeProvider != nil { | |
| if h != nil && h.apiKeyAuthFileScopeProvider != nil { |
References
- Ensure pointer receivers in Go are checked for nil before accessing their fields or locking their mutexes to prevent nil pointer dereference panics.
| void Promise.all(apiKeySettings.map(async (item) => [ | ||
| item.id, | ||
| await fetchCpaApiKeyAuthFileScopes(item.id, controller.signal), | ||
| ] as const)) |
There was a problem hiding this comment.
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.
Summary
This PR adds a scoped, read-only dashboard for CPA API Key users.
What changed
mainchanges 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=1npm run typechecknpm run lintnpm test -- --run