feat(admin): accept NIP-98 auth on admin API routes - #730
Conversation
🦋 Changeset detectedLatest commit: e643772 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 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Anshumancanrock
commented
Aug 13, 2026
I think CodeQL doesn’t recognize our Redis adminRateLimitMiddleware (it only models express-rate-limit etc). These routes still run that limiter before auth, same as the rest of this file. |
Ferryx349
commented
Aug 13, 2026
@Anshumancanrock totally agree, its a codeql false positive, it was happening same with me too, had tried a fix in #692. |
There was a problem hiding this comment.
Pull request overview
Adds optional NIP-98 authentication to protected admin APIs while preserving session authentication.
Changes:
- Adds NIP-98 verification, allowlisting, payload binding, and Redis replay protection.
- Introduces pre-body authentication gates and raw-body capture.
- Adds configuration, documentation, and tests.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/handlers/request-handlers/admin-auth-middleware.ts | Implements NIP-98 admin authentication. |
src/handlers/request-handlers/admin-json-body-middleware.ts | Captures raw JSON bodies. |
src/utils/nip98-replay.ts | Adds Redis-backed replay claims. |
src/routes/admin/index.ts | Wires authentication and body middleware. |
src/@types/settings.ts | Defines NIP-98 settings types. |
resources/default-settings.yaml | Adds disabled-by-default settings. |
CONFIGURATION.md | Documents configuration. |
test/unit/handlers/request-handlers/admin-auth-middleware.spec.ts | Tests authentication behavior. |
test/unit/utils/nip98-replay.spec.ts | Tests replay claims. |
.knip.json | Recognizes the now-used verifier. |
.changeset/nip98-admin-middleware.md | Records the feature release. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
5ca12e7 to
558ee46Compare558ee46 to
115a5eaCompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Description
Protected admin API routes now accept
Authorization: Nostron top of the existing session cookie.Off by default. Set
admin.nip98.enabledand put hex pubkeys inadmin.nip98.allowedPubkeys. Empty allowlist means nobody gets in that way.For PATCH/POST we check the header (sig + allowlist) before parsing JSON, so a junk
Nostrtoken never hits the 1mb body parser. After the body is read we check the payload hash and store the event id in Redis withSET NX(TTL =maxSkewSeconds). Same event twice, or Redis down → 401.GETs don't parse a body. Same session-or-NIP-98 check, including the one-time event id.
Clients still sign
uagainstinfo.relay_url(not the requestHost). Password login / dashboard / cookie session are unchanged;/loginstays 100kb.Related Issue
Follows #722 (verifier), #725 (absolute URL), #726 (Redis NX).
Motivation and Context
#722 only verifies a header. This is the part that actually lets you call the admin API with a signed event instead of sharing the password.