Skip to content

feat(admin): replace password login with NIP-98 HTTP auth - #671

Open
Justxd22 wants to merge 1 commit into
cameri:mainfrom
Justxd22:feat/nip98-admin-auth
Open

feat(admin): replace password login with NIP-98 HTTP auth#671
Justxd22 wants to merge 1 commit into
cameri:mainfrom
Justxd22:feat/nip98-admin-auth

Conversation

@Justxd22

Copy link
Copy Markdown
Collaborator

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 new admin.pubkeys allowlist (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-request Nostr header.
  • Browser flow: GET /admin/login page 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.
  • Added Minimal authenticated landing page at /admin (placeholder until the dashboard UI )
  • Added POST /admin/logout.
  • Removed: PasswordAdminAuthProvider, admin.passwordHash, ADMIN_PASSWORD, login body schema.
  • Added NIP 98 in supported_nips.

Operators must set admin.pubkeys in settings.yaml to use the admin console; admin.enabled still defaults to false.

Related Issue

closes#670

How Has This Been Tested?

  • New test/unit/utils/nip98.spec.ts (36 cases: parsing, crypto, freshness, u/method/payload matching).
  • Rewritten 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).
  • Verified live in a browser against a NIP-07 signer: login 200 + cookie, replay rejected.

Screenshots:

recording_2026-07-08_06.52.03.mp4

Types of changes

  • Non-functional change (docs, style, minor refactor)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my code changes.
  • I added a changeset, or this is docs-only and I added an empty changeset.
  • All new and existing tests passed.

@changeset-bot

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9ce1dc9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
nostreamMinor

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)
Comment threadsrc/utils/nip98.ts
return failure('missing authorization header')
}

const match = NOSTR_SCHEME_REGEX.exec(header.trim())
@Anshumancanrock

Copy link
Copy Markdown
Collaborator

Hi @Justxd22, Thank you for the PR and for your contribution!
However, NIP-98 is part of my project and I’m supposed to implement it during this phase (Phase 2). Thanks again for your effort though.

@coveralls

Copy link
Copy Markdown
Collaborator

Coverage Status

coverage: 68.333% (+0.6%) from 67.747% — Justxd22:feat/nip98-admin-auth into cameri:main

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 Nip98AdminAuthProvider backed by a Redis replay guard and admin.pubkeys allowlist.
  • 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
FileDescription
test/unit/utils/nip98.spec.tsNew unit tests for NIP-98 parsing and verification logic.
test/unit/routes/admin.spec.tsUpdated admin router tests for NIP-98 login/auth, replay protection, redirects, logout.
src/utils/nip98.tsNew NIP-98 parsing/verification helpers.
src/utils/http.tsAdd getPublicRequestUrl for reconstructing request URLs for signing/verification.
src/utils/admin-password.tsRemove password hashing/verification utilities.
src/schemas/admin-login-schema.tsRemove password login request body schema.
src/routes/admin/index.tsRework admin routes for NIP-98 login page, raw-body login POST, global auth gate, dashboard, logout.
src/handlers/request-handlers/post-admin-logout-handler.tsNew logout handler that clears the admin session cookie.
src/handlers/request-handlers/get-admin-login-page-handler.tsNew handler that serves the admin login HTML template.
src/handlers/request-handlers/get-admin-dashboard-page-handler.tsNew handler that serves a minimal authenticated admin landing page.
src/handlers/request-handlers/admin-auth-middleware.tsSwitch auth middleware to async provider + browser redirect behavior.
src/factories/admin-auth-provider-factory.tsSwap password auth provider for NIP-98 provider + Redis replay guard.
src/constants/base.tsAdd NIP-98 event kind and tag constants.
src/admin/redis-nip98-replay-guard.tsNew Redis-backed replay guard for NIP-98 event ids.
src/admin/password-admin-auth-provider.tsRemove password-based admin auth provider.
src/admin/nip98-admin-auth-provider.tsNew NIP-98 admin auth provider (allowlist + per-request header support + session cookie minting).
src/@types/settings.tsReplace admin password hash setting with admin.pubkeys and add timestamp tolerance setting.
src/@types/admin.tsMake isRequestAuthenticated async and add replay-guard interface type.
resources/default-settings.yamlAdd empty default admin.pubkeys allowlist.
resources/admin-login.htmlAdd browser login page that signs a NIP-98 event via NIP-07.
resources/admin-dashboard.htmlAdd minimal authenticated admin landing page + logout action.
README.mdDocument NIP-98 as supported for admin console login.
package.jsonAdd NIP-98 to supported NIPs list.
.changeset/cool-ways-brake.mdChangeset for the new admin auth behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +7 to +16
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'
}
Comment threadsrc/utils/http.ts
Comment on lines +165 to +169
const proto = isSecureRequest(request, settings) ? 'https' : 'http'
const prefix = getPublicPathPrefix(request, settings)
const path = request.originalUrl ?? request.url ?? '/'

return `${proto}://${host}${joinPathPrefix(prefix, path)}`
@Justxd22

Copy link
Copy Markdown
CollaboratorAuthor

@Anshumancanrock Only added admin dashboard support, Relay NIP98 still not impl

Sign up for freeto 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.

[Admin console] Implement NIP-98 HTTP auth for the admin console

5 participants

@Justxd22@Anshumancanrock@coveralls@github-advanced-security