feat(admin): replace password login with NIP-98 HTTP auth#671
Conversation
🦋 Changeset detectedLatest commit: 9ce1dc9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| router.post('/login', adminLoginRateLimitMiddleware, raw({ type: () => true, limit: '64kb' }), withAdminController(createPostAdminLoginController)) | ||
| // Everything below requires NIP-98 authentication; unauthenticated browser | ||
| // navigations are redirected to the login page, other requests receive 401. | ||
| router.use(adminRateLimitMiddleware, adminAuthMiddleware) |
| return failure('missing authorization header') | ||
| } | ||
|
|
||
| const match = NOSTR_SCHEME_REGEX.exec(header.trim()) |
|
Hi @Justxd22, Thank you for the PR and for your contribution! |
There was a problem hiding this comment.
Pull request overview
This PR replaces the admin console’s temporary password-based authentication with NIP-98 (kind 27235) HTTP auth, introducing a pubkey allowlist and replay protection while adding minimal browser-facing login/dashboard/logout flows for the /admin/* area.
Changes:
- Implement NIP-98 verification utilities and a new
Nip98AdminAuthProviderbacked by a Redis replay guard andadmin.pubkeysallowlist. - Update admin routing to gate all
/admin/*paths, adding HTML login + placeholder dashboard pages and a logout endpoint. - Remove the password login implementation and add/refresh unit tests covering NIP-98 and admin flows.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/utils/nip98.spec.ts | New unit tests for NIP-98 parsing and verification logic. |
| test/unit/routes/admin.spec.ts | Updated admin router tests for NIP-98 login/auth, replay protection, redirects, logout. |
| src/utils/nip98.ts | New NIP-98 parsing/verification helpers. |
| src/utils/http.ts | Add getPublicRequestUrl for reconstructing request URLs for signing/verification. |
| src/utils/admin-password.ts | Remove password hashing/verification utilities. |
| src/schemas/admin-login-schema.ts | Remove password login request body schema. |
| src/routes/admin/index.ts | Rework admin routes for NIP-98 login page, raw-body login POST, global auth gate, dashboard, logout. |
| src/handlers/request-handlers/post-admin-logout-handler.ts | New logout handler that clears the admin session cookie. |
| src/handlers/request-handlers/get-admin-login-page-handler.ts | New handler that serves the admin login HTML template. |
| src/handlers/request-handlers/get-admin-dashboard-page-handler.ts | New handler that serves a minimal authenticated admin landing page. |
| src/handlers/request-handlers/admin-auth-middleware.ts | Switch auth middleware to async provider + browser redirect behavior. |
| src/factories/admin-auth-provider-factory.ts | Swap password auth provider for NIP-98 provider + Redis replay guard. |
| src/constants/base.ts | Add NIP-98 event kind and tag constants. |
| src/admin/redis-nip98-replay-guard.ts | New Redis-backed replay guard for NIP-98 event ids. |
| src/admin/password-admin-auth-provider.ts | Remove password-based admin auth provider. |
| src/admin/nip98-admin-auth-provider.ts | New NIP-98 admin auth provider (allowlist + per-request header support + session cookie minting). |
| src/@types/settings.ts | Replace admin password hash setting with admin.pubkeys and add timestamp tolerance setting. |
| src/@types/admin.ts | Make isRequestAuthenticated async and add replay-guard interface type. |
| resources/default-settings.yaml | Add empty default admin.pubkeys allowlist. |
| resources/admin-login.html | Add browser login page that signs a NIP-98 event via NIP-07. |
| resources/admin-dashboard.html | Add minimal authenticated admin landing page + logout action. |
| README.md | Document NIP-98 as supported for admin console login. |
| package.json | Add NIP-98 to supported NIPs list. |
| .changeset/cool-ways-brake.md | Changeset for the new admin auth behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public async registerEventId(eventId: string, ttlSeconds: number): Promise<boolean> { | ||
| const client = this.getClient() | ||
| if (!client.isOpen) { | ||
| await client.connect() | ||
| } | ||
|
|
||
| const result = await client.set(`nip98:seen:${eventId}`, '1', { NX: true, EX: ttlSeconds }) | ||
|
|
||
| return result === 'OK' | ||
| } |
| const proto = isSecureRequest(request, settings) ? 'https' : 'http' | ||
| const prefix = getPublicPathPrefix(request, settings) | ||
| const path = request.originalUrl ?? request.url ?? '/' | ||
|
|
||
| return `${proto}://${host}${joinPathPrefix(prefix, path)}` |
|
@Anshumancanrock Only added admin dashboard support, Relay NIP98 still not impl |
Implements NIP-98 HTTP auth for the admin console, fully replacing the temporary password login shipped with the Phase 1 admin backend foundation
Nip98AdminAuthProvider: pubkey must be on the newadmin.pubkeysallowlist (hex or npub; empty = deny all); Redis replay guard rejects reused event ids (fail-closed). Login mints the existing HMAC session cookie; protected routes also accept a fresh per-requestNostrheader.GET /admin/loginpage asks any connected NIP-07 signer/wallet to sign the auth event; auth middleware now gates all/admin/*traffic, Any unauthenticated browser redirects 302 to the login page, API clients get 401 JSON./admin(placeholder until the dashboard UI )POST /admin/logout.PasswordAdminAuthProvider,admin.passwordHash,ADMIN_PASSWORD, login body schema.supported_nips.Operators must set
admin.pubkeysinsettings.yamlto use the admin console;admin.enabledstill defaults tofalse.Related Issue
closes #670
How Has This Been Tested?
test/unit/utils/nip98.spec.ts(36 cases: parsing, crypto, freshness,u/method/payloadmatching).test/unit/routes/admin.spec.ts(20 cases: login, per-request auth, replay rejection, payload hash, npub allowlist, redirects, dashboard, logout, disabled/401/500 behaviors preserved).Screenshots:
recording_2026-07-08_06.52.03.mp4
Types of changes
Checklist: