Phase 14: Full WebAuthn support for passwordless passkey login and hardware security key 2FA. - go-webauthn/webauthn v0.16.1 dependency - WebAuthnConfig with RPID/RPOrigin/DisplayName validation - Migration 000009: webauthn_credentials table - DB CRUD with ownership checks and admin operations - internal/webauthn adapter: encrypt/decrypt at rest with AES-256-GCM - REST: register begin/finish, login begin/finish, list, delete - Web UI: profile enrollment, login passkey button, admin management - gRPC: ListWebAuthnCredentials, RemoveWebAuthnCredential RPCs - mciasdb: webauthn list/delete/reset subcommands - OpenAPI: 6 new endpoints, WebAuthnCredentialInfo schema - Policy: self-service enrollment rule, admin remove via wildcard - Tests: DB CRUD, adapter round-trip, interface compliance - Docs: ARCHITECTURE.md §22, PROJECT_PLAN.md Phase 14 Security: Credential IDs and public keys encrypted at rest with AES-256-GCM via vault master key. Challenge ceremonies use 128-bit nonces with 120s TTL in sync.Map. Sign counter validated on each assertion to detect cloned authenticators. Password re-auth required for registration (SEC-01 pattern). No credential material in API responses or logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
31 lines
1.0 KiB
HTML
31 lines
1.0 KiB
HTML
{{define "webauthn_credentials"}}
|
|
<div id="webauthn-credentials-section">
|
|
{{if .WebAuthnCreds}}
|
|
<table class="table" style="font-size:.85rem;margin-bottom:1rem">
|
|
<thead>
|
|
<tr><th>Name</th><th>Created</th><th>Last Used</th><th>Sign Count</th><th></th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .WebAuthnCreds}}
|
|
<tr>
|
|
<td>{{.Name}}{{if .Discoverable}} <span class="badge" title="Passkey (discoverable)">passkey</span>{{end}}</td>
|
|
<td class="text-small">{{formatTime .CreatedAt}}</td>
|
|
<td class="text-small">{{if .LastUsedAt}}{{formatTime (derefTime .LastUsedAt)}}{{else}}Never{{end}}</td>
|
|
<td>{{.SignCount}}</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-danger"
|
|
hx-delete="{{$.DeletePrefix}}/{{.ID}}"
|
|
hx-target="#webauthn-credentials-section"
|
|
hx-swap="outerHTML"
|
|
hx-confirm="Remove this passkey?">Remove</button>
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<p class="text-muted text-small">No passkeys registered.</p>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|