Skip to content

feat(dashboard): add managed API keys page#195

Merged
SantiagoDePolonia merged 5 commits intomainfrom
feature/dashboard-multikey
Mar 31, 2026
Merged

feat(dashboard): add managed API keys page#195
SantiagoDePolonia merged 5 commits intomainfrom
feature/dashboard-multikey

Conversation

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor

@SantiagoDePolonia SantiagoDePolonia commented Mar 31, 2026

Summary

  • add a managed API Keys page to the admin dashboard for listing, issuing, and deactivating keys
  • treat date-only key expirations as valid through the selected UTC day and render expiration dates without timezone drift in the table
  • surface auth method data in audit traces and execution-plan charts, while keeping the merged audit-log schema compatible with both auth_method and user_path

Testing

  • go test ./internal/auditlog ./internal/server ./internal/admin
  • node --test internal/admin/dashboard/static/js/modules/auth-keys.test.js internal/admin/dashboard/static/js/modules/timestamp-format.test.js internal/admin/dashboard/static/js/modules/dashboard-layout.test.js internal/admin/dashboard/static/js/modules/execution-plans.test.js internal/admin/dashboard/static/js/modules/execution-plans-layout.test.js

Summary by CodeRabbit

  • New Features

    • Added an API Keys page to create, view, copy, and deactivate keys with expirations, issued-key banner, copy feedback, badges, and empty-state messaging.
    • Execution pipeline chart now shows an "Auth" step with per-request auth success/error visuals.
    • Sidebar navigation link for API Keys.
  • Tests

    • New and expanded tests covering API key workflows, copy fallbacks, auth-state rendering, and UTC date formatting for expirations.

…tikey

# Conflicts:
#	internal/auditlog/reader_postgresql.go
#	internal/auditlog/reader_sqlite.go
#	internal/auditlog/store_postgresql.go
#	internal/auditlog/store_postgresql_test.go
#	internal/auditlog/store_sqlite.go
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

This PR adds an API Keys admin UI and client module, persists and surfaces auth-method metadata through audit-log schema/reads/writes and middleware enrichment, and shows authentication status in execution-plan charts (templates, JS model, CSS). Tests for client behavior, timestamp formatting, chart outputs, and DB store adjustments were added/updated.

Changes

Cohort / File(s) Summary
Dashboard Styling
internal/admin/dashboard/static/css/dashboard.css
Added API Keys page styles (help notice, issued-key banner, token display, copy button/copy state, badges) and execution-pipeline auth node success/error variants.
Dashboard Routing & Core
internal/admin/dashboard/static/js/dashboard.js
Added auth-keys route handling, call sites for fetchAuthKeys() on init/popstate/navigate, new formatDateUTC(ts), and optional dashboardAuthKeysModule resolution.
Auth Keys Module
internal/admin/dashboard/static/js/modules/auth-keys.js
New browser module dashboardAuthKeysModule implementing fetch/create/deactivate flows, expiry normalization, copy-to-clipboard (native + fallback), state flags, errors/notices, and helper utilities.
Auth Keys Tests & Timestamp
internal/admin/dashboard/static/js/modules/auth-keys.test.js, internal/admin/dashboard/static/js/modules/timestamp-format.test.js, internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
New deterministic tests for submit/copy/fallback behavior, UTC date formatting, and updated template assertions for auth-keys rendering.
Execution Plan Chart
internal/admin/dashboard/static/js/modules/execution-plans.js, internal/admin/dashboard/templates/execution-plan-chart.html, internal/admin/dashboard/static/js/modules/execution-plans.test.js
Added auth step to template, emitted authNodeClass/authNodeSublabel from chart model, derived authMethod/authError from entries, and updated tests/CSS expectations.
Dashboard Templates & Layout
internal/admin/dashboard/templates/index.html, internal/admin/dashboard/templates/layout.html
Added API Keys page markup (create/list/issued banner/copy/deactivate flows), sidebar nav link, and included auth-keys.js module script.
Audit Log Core & Middleware
internal/auditlog/auditlog.go, internal/auditlog/middleware.go
Added AuthMethod* constants and LogEntry.AuthMethod field; implemented exported EnrichEntryWithAuthMethod to validate/assign auth method into in-flight log entries.
Audit Log Readers
internal/auditlog/reader_mongodb.go, internal/auditlog/reader_postgresql.go, internal/auditlog/reader_sqlite.go
Mapped auth_method from DB rows into LogEntry.AuthMethod across MongoDB/Postgres/SQLite readers and scans.
Audit Log Stores / Migrations
internal/auditlog/store_postgresql.go, internal/auditlog/store_sqlite.go
Added auth_method column to create/migrations, increased INSERT column/placeholders to include auth_method, appended AuthMethod to insert args, and adjusted batch sizing/placeholders.
Audit Log Tests
internal/auditlog/store_postgresql_test.go, internal/auditlog/middleware_auth_method_test.go
Updated SQL/args expectations for added column and added unit tests for EnrichEntryWithAuthMethod behavior.
Server Auth
internal/server/auth.go, internal/server/auth_test.go
Middleware now calls EnrichEntryWithAuthMethod to record no-key/master-key/API-key prior to continuing auth flow; tests updated/added to assert audit entry enrichment.

Sequence Diagrams

sequenceDiagram
    actor Admin
    participant Dashboard as Admin Dashboard
    participant Server as API Server
    participant DB as Database

    rect rgba(100, 150, 255, 0.5)
    Note over Admin,DB: Create API Key Flow
    Admin->>Dashboard: Open "Create API Key" and submit form
    Dashboard->>Dashboard: Normalize expires_at to UTC end-of-day
    Dashboard->>Server: POST /admin/api/v1/auth-keys
    Server->>DB: Insert new auth key row
    Server->>Server: Enrich audit log with auth_method
    Server->>DB: Insert audit entry
    Server-->>Dashboard: Return issued key value
    Dashboard->>Admin: Show issued key + copy button
    end

    rect rgba(100, 200, 100, 0.5)
    Note over Admin,DB: Deactivate API Key Flow
    Admin->>Dashboard: Click "Deactivate"
    Dashboard->>Admin: Confirm
    Admin->>Dashboard: Confirm
    Dashboard->>Server: POST /admin/api/v1/auth-keys/{id}/deactivate
    Server->>DB: Mark key inactive
    Server->>Server: Enrich audit log with auth_method
    Server->>DB: Insert audit entry
    Server-->>Dashboard: Success
    Dashboard->>Dashboard: Refresh list
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Admin,DB: Fetch & Display Keys
    Admin->>Dashboard: Navigate to API Keys page
    Dashboard->>Server: GET /admin/api/v1/auth-keys
    Server->>DB: Query auth keys
    Server-->>Dashboard: Return key list
    Dashboard->>Admin: Render table with statuses and expirations
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped to the dashboard, keys in my paw,
I painted the nodes and taught logs to draw,
Server, DB, and CSS in a nifty tryst,
Keys issued, copied — a celebratory twist! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a managed API keys page to the admin dashboard.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dashboard-multikey

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/server/auth.go (1)

74-85: ⚠️ Potential issue | 🟠 Major

Record AuthMethodAPIKey before authentication so failed attempts are attributed.

Right now auth_method is only set after successful Authenticate, so rejected managed-key attempts lose auth-method attribution in audit traces.

🔧 Proposed fix
 if authenticator != nil && authenticator.Enabled() {
+    auditlog.EnrichEntryWithAuthMethod(c, auditlog.AuthMethodAPIKey)
     authKeyID, err := authenticator.Authenticate(c.Request().Context(), token)
     if err == nil {
         ctx := core.WithAuthKeyID(c.Request().Context(), authKeyID)
         c.SetRequest(c.Request().WithContext(ctx))
         auditlog.EnrichEntryWithAuthKeyID(c, authKeyID)
-        auditlog.EnrichEntryWithAuthMethod(c, auditlog.AuthMethodAPIKey)
         return next(c)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/server/auth.go` around lines 74 - 85, Before calling
authenticator.Authenticate, set the audit method to AuthMethodAPIKey so failed
managed-key attempts are attributed; specifically, move or add the call to
auditlog.EnrichEntryWithAuthMethod(c, auditlog.AuthMethodAPIKey) to occur before
invoking authenticator.Authenticate (in the block guarded by authenticator !=
nil && authenticator.Enabled()), so that authenticationErrorWithAudit and
authFailureMessage will include the method even on failure while leaving
existing ctx/authKeyID handling unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/modules/auth-keys.js`:
- Around line 66-71: The copyAuthKeyValue method currently calls
navigator.clipboard.writeText(this.authKeyIssuedValue) without handling
rejection or absence of the Clipboard API; update copyAuthKeyValue to first
feature-detect navigator.clipboard and, if available, call
writeText(...).then(...) and add a .catch(...) to handle failures (set a new
error flag like this.authKeyCopyError = true, reset authKeyCopied appropriately,
and optionally console.error). If navigator.clipboard is not available,
implement a synchronous fallback that creates a temporary textarea, selects its
content and uses document.execCommand('copy') with try/catch to set
authKeyCopied or authKeyCopyError accordingly; ensure you clear the temporary
element and reset flags after 2s as existing logic does.

In `@internal/auditlog/middleware.go`:
- Around line 391-402: EnrichEntryWithAuthMethod currently assigns any string to
LogEntry.AuthMethod; update the EnrichEntryWithAuthMethod function to first trim
whitespace from the incoming method string, return early if the trimmed value is
empty, and validate it against a small allowlist of acceptable auth method
identifiers (e.g., "password", "oauth", "sso", "api_key", "magic_link",
"unknown"); only set entry.AuthMethod when the trimmed value is one of those
allowed values and keep existing nil-checks for c.Get(string(LogEntryKey)) and
type-assertion to *LogEntry as currently done.

In `@internal/auditlog/store_postgresql.go`:
- Around line 88-91: The migration slice in
internal/auditlog/store_postgresql.go contains a duplicated SQL entry "ALTER
TABLE audit_logs ADD COLUMN IF NOT EXISTS auth_method TEXT"; locate the
migrations array (the slice of ALTER TABLE statements) and remove the duplicate
entry so each column migration appears only once (ensure "user_path" and a
single "auth_method" remain), updating the migrations slice where those ALTER
TABLE strings are defined.

In `@internal/auditlog/store_sqlite.go`:
- Around line 73-76: Remove the duplicate "ALTER TABLE audit_logs ADD COLUMN
auth_method TEXT" entry from the migrations list so the migrations slice only
contains one addition of auth_method; edit the migrations variable (the
slice/array that includes "ALTER TABLE audit_logs ADD COLUMN auth_method TEXT",
"ALTER TABLE audit_logs ADD COLUMN user_path TEXT", etc.) and delete the
repeated auth_method string to keep the migration entries unique.

---

Outside diff comments:
In `@internal/server/auth.go`:
- Around line 74-85: Before calling authenticator.Authenticate, set the audit
method to AuthMethodAPIKey so failed managed-key attempts are attributed;
specifically, move or add the call to auditlog.EnrichEntryWithAuthMethod(c,
auditlog.AuthMethodAPIKey) to occur before invoking authenticator.Authenticate
(in the block guarded by authenticator != nil && authenticator.Enabled()), so
that authenticationErrorWithAudit and authFailureMessage will include the method
even on failure while leaving existing ctx/authKeyID handling unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9e8fd29a-f5f0-4186-8414-46370ffb2f35

📥 Commits

Reviewing files that changed from the base of the PR and between bd2c192 and adc6cfd.

📒 Files selected for processing (21)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/dashboard.js
  • internal/admin/dashboard/static/js/modules/auth-keys.js
  • internal/admin/dashboard/static/js/modules/auth-keys.test.js
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
  • internal/admin/dashboard/static/js/modules/execution-plans-layout.test.js
  • internal/admin/dashboard/static/js/modules/execution-plans.js
  • internal/admin/dashboard/static/js/modules/execution-plans.test.js
  • internal/admin/dashboard/static/js/modules/timestamp-format.test.js
  • internal/admin/dashboard/templates/execution-plan-chart.html
  • internal/admin/dashboard/templates/index.html
  • internal/admin/dashboard/templates/layout.html
  • internal/auditlog/auditlog.go
  • internal/auditlog/middleware.go
  • internal/auditlog/reader_mongodb.go
  • internal/auditlog/reader_postgresql.go
  • internal/auditlog/reader_sqlite.go
  • internal/auditlog/store_postgresql.go
  • internal/auditlog/store_postgresql_test.go
  • internal/auditlog/store_sqlite.go
  • internal/server/auth.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/server/auth.go (1)

43-52: 🧹 Nitpick | 🔵 Trivial

Skipped paths leave AuthMethod empty.

When a request path matches skipPaths, the middleware returns without setting AuthMethod. This means audit entries for skipped paths will have an empty auth method even when auth is configured for other routes. If this is intentional (skipped paths don't undergo auth evaluation), no change needed. Otherwise, consider setting AuthMethodNoKey or a dedicated "skipped" value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/server/auth.go` around lines 43 - 52, When a request matches the
skipPaths branch the middleware returns early without setting AuthMethod,
leaving audit records with an empty auth method; update the skipPaths handling
(the loop that checks skipPaths and requestPath and currently does "return
next(c)") to set the audit/auth indicator (e.g. set AuthMethod = AuthMethodNoKey
or the string "skipped" in the same context/storage you use elsewhere)
immediately before returning, so skipped routes produce a consistent auth method
value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/modules/auth-keys.js`:
- Around line 37-40: When handleFetchResponse(res, 'auth keys') returns false,
we should surface the HTTP failure instead of silently clearing the list: in the
failure branch in auth-keys.js (the block that currently sets this.authKeys = []
and returns) set the component/state variable authKeyError (or assign an error
message from the response) to indicate a load failure and do not clobber
authKeys; use handleFetchResponse and the response object to distinguish a 401
vs other errors so only 401 handling remains unchanged and all other non-2xx
responses set authKeyError (and early-return) rather than clearing the table.

In `@internal/admin/dashboard/templates/index.html`:
- Around line 238-240: The template buttons currently call openAuthKeyForm() and
can trigger clearing of the one-time authKeyIssuedValue while the key is
visible; update the logic in
internal/admin/dashboard/static/js/modules/auth-keys.js so openAuthKeyForm() and
closeAuthKeyForm() do not unconditionally clear authKeyIssuedValue — add checks
for a submission-in-flight flag (e.g., isIssuing) and whether the editor is
already open (e.g., isAuthEditorOpen) before clearing, set/clear that isIssuing
flag around submitAuthKeyForm() so submitAuthKeyForm() will set
authKeyIssuedValue and ensure it reopens the editor (set isAuthEditorOpen) if
the request completes after a manual close; also guard the template buttons (the
Create API Key and the other control mentioned) to not call openAuthKeyForm()
when isIssuing is true to avoid losing the only visible key.
- Around line 362-364: The empty-state paragraph currently shows "No API keys
yet" even when an authKeyError exists; update its x-show condition to also
require that authKeyError is falsy so it only renders when there is no load
error (i.e., change the condition on the <p class="empty-state"> that references
authKeys, authKeysLoading, authError, and authKeysAvailable to also include
!authKeyError) to prevent the empty message appearing alongside real error
messages.

In `@internal/auditlog/middleware_auth_method_test.go`:
- Around line 11-47: Add assertions for the remaining allowed auth-method values
in the existing tests: call EnrichEntryWithAuthMethod(c, "no_key") and assert
entry.AuthMethod == AuthMethodNoKey, and call EnrichEntryWithAuthMethod(c,
"unknown") and assert entry.AuthMethod == "unknown" (or the corresponding
constant if defined), keeping the same context setup (echo.New,
httptest.NewRequest, c.Set(string(LogEntryKey), entry)) and trimming/validation
expectations used with EnrichEntryWithAuthMethod so the test suite covers all
permitted values.

---

Outside diff comments:
In `@internal/server/auth.go`:
- Around line 43-52: When a request matches the skipPaths branch the middleware
returns early without setting AuthMethod, leaving audit records with an empty
auth method; update the skipPaths handling (the loop that checks skipPaths and
requestPath and currently does "return next(c)") to set the audit/auth indicator
(e.g. set AuthMethod = AuthMethodNoKey or the string "skipped" in the same
context/storage you use elsewhere) immediately before returning, so skipped
routes produce a consistent auth method value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 99468d40-a6e1-426b-b7d0-e0e818a36f0a

📥 Commits

Reviewing files that changed from the base of the PR and between adc6cfd and b486213.

📒 Files selected for processing (9)
  • internal/admin/dashboard/static/js/modules/auth-keys.js
  • internal/admin/dashboard/static/js/modules/auth-keys.test.js
  • internal/admin/dashboard/templates/index.html
  • internal/auditlog/middleware.go
  • internal/auditlog/middleware_auth_method_test.go
  • internal/auditlog/store_postgresql.go
  • internal/auditlog/store_sqlite.go
  • internal/server/auth.go
  • internal/server/auth_test.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/modules/auth-keys.js`:
- Around line 233-273: The deactivateAuthKey method directly calls
window.confirm which breaks the module's testability; change that call to use
the injected global.confirm (or the module's injected confirm reference) instead
of window.confirm so tests can mock it; update the conditional at the start of
deactivateAuthKey to call global.confirm('Deactivate key "' + key.name + '"?
This cannot be undone.') and leave all other logic (authKeyDeactivatingID,
authKeyError, fetchAuthKeys, etc.) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 47d20864-44f8-4c29-89af-e71137068ec6

📥 Commits

Reviewing files that changed from the base of the PR and between b486213 and 10eb8df.

📒 Files selected for processing (7)
  • internal/admin/dashboard/static/js/modules/auth-keys.js
  • internal/admin/dashboard/static/js/modules/auth-keys.test.js
  • internal/admin/dashboard/static/js/modules/dashboard-layout.test.js
  • internal/admin/dashboard/templates/index.html
  • internal/auditlog/middleware_auth_method_test.go
  • internal/server/auth.go
  • internal/server/auth_test.go

Comment on lines +233 to +273
async deactivateAuthKey(key) {
if (!key || !key.active) {
return;
}
if (!window.confirm('Deactivate key "' + key.name + '"? This cannot be undone.')) {
return;
}

this.authKeyDeactivatingID = key.id;
this.authKeyError = '';
this.authKeyNotice = '';

try {
const res = await fetch('/admin/api/v1/auth-keys/' + encodeURIComponent(key.id) + '/deactivate', {
method: 'POST',
headers: this.headers()
});
if (res.status === 503) {
this.authKeysAvailable = false;
this.authKeyError = 'Auth keys feature is unavailable.';
return;
}
if (res.status === 401) {
this.authError = true;
this.needsAuth = true;
this.authKeyError = 'Authentication required.';
return;
}
if (res.status !== 204) {
this.authKeyError = await this._authKeyResponseMessage(res, 'Failed to deactivate key.');
return;
}
await this.fetchAuthKeys();
this.authKeyNotice = 'Key "' + key.name + '" deactivated.';
} catch (e) {
console.error('Failed to deactivate auth key:', e);
this.authKeyError = 'Failed to deactivate key.';
} finally {
this.authKeyDeactivatingID = '';
}
}
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.

🧹 Nitpick | 🔵 Trivial

Use the injected global reference for confirm to maintain testability.

The module uses global.navigator, global.document etc. for testability, but line 237 directly references window.confirm. While window equals global at runtime, using global.confirm (or a parameter) would be more consistent and allow mocking in tests if needed.

♻️ Suggested fix
-                if (!window.confirm('Deactivate key "' + key.name + '"? This cannot be undone.')) {
+                if (!global.confirm('Deactivate key "' + key.name + '"? This cannot be undone.')) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async deactivateAuthKey(key) {
if (!key || !key.active) {
return;
}
if (!window.confirm('Deactivate key "' + key.name + '"? This cannot be undone.')) {
return;
}
this.authKeyDeactivatingID = key.id;
this.authKeyError = '';
this.authKeyNotice = '';
try {
const res = await fetch('/admin/api/v1/auth-keys/' + encodeURIComponent(key.id) + '/deactivate', {
method: 'POST',
headers: this.headers()
});
if (res.status === 503) {
this.authKeysAvailable = false;
this.authKeyError = 'Auth keys feature is unavailable.';
return;
}
if (res.status === 401) {
this.authError = true;
this.needsAuth = true;
this.authKeyError = 'Authentication required.';
return;
}
if (res.status !== 204) {
this.authKeyError = await this._authKeyResponseMessage(res, 'Failed to deactivate key.');
return;
}
await this.fetchAuthKeys();
this.authKeyNotice = 'Key "' + key.name + '" deactivated.';
} catch (e) {
console.error('Failed to deactivate auth key:', e);
this.authKeyError = 'Failed to deactivate key.';
} finally {
this.authKeyDeactivatingID = '';
}
}
async deactivateAuthKey(key) {
if (!key || !key.active) {
return;
}
if (!global.confirm('Deactivate key "' + key.name + '"? This cannot be undone.')) {
return;
}
this.authKeyDeactivatingID = key.id;
this.authKeyError = '';
this.authKeyNotice = '';
try {
const res = await fetch('/admin/api/v1/auth-keys/' + encodeURIComponent(key.id) + '/deactivate', {
method: 'POST',
headers: this.headers()
});
if (res.status === 503) {
this.authKeysAvailable = false;
this.authKeyError = 'Auth keys feature is unavailable.';
return;
}
if (res.status === 401) {
this.authError = true;
this.needsAuth = true;
this.authKeyError = 'Authentication required.';
return;
}
if (res.status !== 204) {
this.authKeyError = await this._authKeyResponseMessage(res, 'Failed to deactivate key.');
return;
}
await this.fetchAuthKeys();
this.authKeyNotice = 'Key "' + key.name + '" deactivated.';
} catch (e) {
console.error('Failed to deactivate auth key:', e);
this.authKeyError = 'Failed to deactivate key.';
} finally {
this.authKeyDeactivatingID = '';
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/js/modules/auth-keys.js` around lines 233 -
273, The deactivateAuthKey method directly calls window.confirm which breaks the
module's testability; change that call to use the injected global.confirm (or
the module's injected confirm reference) instead of window.confirm so tests can
mock it; update the conditional at the start of deactivateAuthKey to call
global.confirm('Deactivate key "' + key.name + '"? This cannot be undone.') and
leave all other logic (authKeyDeactivatingID, authKeyError, fetchAuthKeys, etc.)
unchanged.

@SantiagoDePolonia SantiagoDePolonia merged commit ce6b390 into main Mar 31, 2026
16 checks passed
@SantiagoDePolonia SantiagoDePolonia deleted the feature/dashboard-multikey branch April 1, 2026 21:29
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