Conversation
Contributor
Author
|
solves #197 |
Member
|
@awongh Okay, I made a bunch of changes, I think it's pretty solid now, but we're out of time, so I'll do another review and then see if I can deploy it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
nr-bridge
Bridge between the legacy Trustroots.org MongoDB
user database and the new Nostr-based Nostroots iOS app. The service verifies
that a user owns the email address associated with their Trustroots username and,
once verified, writes their Nostr public key (
npub) into the MongoDBuserscollection.
Overview
nr-bridge is a lightweight Deno server that exposes two HTTP endpoints. It uses
Deno KV for short-lived verification state and communicates with the Trustroots
MongoDB instance to read and update user records.
Tech Stack
User Flow
Goal
Verify that a Nostroots app user owns the email address tied to their Trustroots
username, then persist their Nostr
npubin MongoDB so the two identities arelinked.
Step-by-Step
POST /verify_tokenwith the username.UUID token, stores both in Deno KV (15 min TTL), and emails the user.
nostroots://verify?token=<uuid>) that opens the appdirectly, bypassing the code entry screen.
POST /authenticatewith the username, the user'snpub, andeither the code or the token.
npubtoMongoDB, and deletes the KV entry.
User Flow Diagram
flowchart TD A["User opens nr-app"] --> B["Enters Trustroots username"] B --> C["Taps 'Verify identity with email'"] C --> D["nr-app POST /verify_token"] D --> E{"Username exists\nin MongoDB?"} E -- No --> F["404 Not Found"] E -- Yes --> G{"Unexpired token\nalready pending?"} G -- Yes --> H["409 Conflict"] G -- No --> I["Generate code + token"] I --> J["Store in Deno KV\n(15 min TTL)"] J --> K["Email code + deep link"] K --> L["200 Success"] L --> M{"User chooses\nverification method"} M -- "Types 6-digit code" --> N["nr-app POST /authenticate\nwith code"] M -- "Taps deep link" --> O["nr-app POST /authenticate\nwith token"] N --> P{"Code or token\nvalid?"} O --> P P -- No --> Q["401 Unauthorized"] P -- Yes --> R["Set npub in MongoDB"] R --> S["Delete KV entry"] S --> T["200 Success"]Sequence Diagram
sequenceDiagram participant App as nr-app (iOS) participant Bridge as nr-bridge participant KV as Deno KV participant Mongo as MongoDB participant Email as SMTP App->>Bridge: POST /verify_token {username} Bridge->>Mongo: Find user by username Mongo-->>Bridge: {email, username} Bridge->>KV: Check for existing token KV-->>Bridge: null (no existing) Bridge->>Bridge: Generate code + token Bridge->>KV: Store token request (15min expiry) Bridge->>Email: Send email with code + deep link Bridge-->>App: 200 {success: true} alt User enters 6-digit code App->>Bridge: POST /authenticate {username, npub, code} else User clicks deep link App->>Bridge: POST /authenticate {username, npub, token} end Bridge->>KV: Get token request by username KV-->>Bridge: {code, token, ...} Bridge->>Bridge: Verify code or token matches Bridge->>Mongo: Update nostrNpub for user Bridge->>KV: Delete token request Bridge-->>App: 200 {success: true}API Reference
POST /verify_tokenInitiate email verification for a Trustroots username.
Request
{ "username": "alice" }usernameResponses
200{ "success": true }400{ "error": "Invalid request", "details": {...} }404{ "error": "User not found" }409{ "error": "Verification already pending" }Side effects
["tokenRequests", username],TTL: 15 minutes).
nostroots://verify?token=<uuid>.POST /authenticateComplete verification by providing the six-digit code or the deep-link token,
along with the user's Nostr public key.
Request
{ "username": "alice", "npub": "npub1abc123...", "code": "482937" }or
{ "username": "alice", "npub": "npub1abc123...", "token": "550e8400-e29b-41d4-a716-446655440000" }usernamenpub"npub")codetoken*At least one of
codeortokenmust be provided.Responses
200{ "success": true }400{ "error": "Invalid request", "details": {...} }401{ "error": "No pending verification or code expired" }401{ "error": "Invalid code or token" }500{ "error": "Failed to update user" }Side effects
nostrNpubfield and updates theupdatedtimestamp on thematching user document in MongoDB.
Data Model
Token Request (Deno KV)
Stored under the key
["tokenRequests", <username>]with a 15-minute TTL viaDeno KV's
expireInoption. An additional application-level check onexpiresAtguards against clock drift.idusernameemailcode100000-999999)tokencreatedAtexpiresAtMongoDB User (relevant fields)
The
userscollection in the Trustroots database. nr-bridge only readsusernameandemail, and writesnostrNpubandupdated.usernameemailnostrNpubupdatedArchitecture
Component Diagram
graph LR subgraph nrBridge["nr-bridge (Deno)"] main["main.ts"] server["server.ts (Hono)"] vt["verifyToken.ts"] auth["authenticate.ts"] mongoMod["mongodb.ts"] kvMod["kv.ts"] emailMod["email/send.ts"] tmpl["email/templates.ts"] utils["utils.ts"] main --> server server --> vt server --> auth vt --> mongoMod vt --> kvMod vt --> emailMod vt --> tmpl vt --> utils auth --> mongoMod auth --> kvMod end mongoMod --> MongoDB["MongoDB (Trustroots)"] kvMod --> DenoKV["Deno KV"] emailMod --> SMTP["SMTP Server"]Directory Structure
Development
Prerequisites
Dev Container
The recommended way to develop is with the VS Code devcontainer at
.devcontainer/nr-bridge/. It provides:mongoshtrustroots_docs/trustroots-dev.archiveMONGODB_URIpre-configured tomongodb://mongodb:27017/trustroots-devOpen the repo in VS Code, select Reopen in Container, and choose
Nostroots Bridge.
Running Locally
Running Tests
The test suite includes 27 tests:
utils.test.tsschemas.test.tstokenRequest.test.tsroutes.test.tsverifyToken.test.tsauthenticate.test.tsUnit tests use
:memory:Deno KV instances so they require no externalservices. E2E tests connect to the MongoDB instance from the devcontainer.
Environment Variables
MONGODB_URImongodb://mongodb:27017/trustroots-devPORT8000SMTP_HOSTSMTP_PORT587SMTP_USERSMTP_PASSSMTP_FROMnoreply@nostroots.comDEEP_LINK_BASEnostroots://verifyDENO_KV_PATHError Handling
All error responses follow a consistent JSON shape:
{ "error": "Human-readable message" }Validation errors (status
400) include an additionaldetailsfield withZod's flattened error output:
{ "error": "Invalid request", "details": { "fieldErrors": { "username": ["String must contain at least 1 character(s)"] }, "formErrors": [] } }Email Template
The verification email is a responsive HTML email with:
with letter-spacing for readability.
nostroots://verify?token=<uuid>.safe-to-ignore disclaimer.
Security Considerations
crypto.getRandomValues(), notMath.random().crypto.randomUUID().(
expireIn) and the application level (expiresAttimestamp check).npubfield is validated to start with"npub"toprevent accidental submission of secret keys (
nsec).returned in an HTTP response; they are only delivered via email.