feat(auth): generic OAuth/OIDC logins (Google, GitHub, Facebook, Apple)#171
Merged
Conversation
Add external OAuth/OIDC login alongside the existing NIP-98 Nostr auth.
- New account_type on users (nostr|oauth); OAuth accounts use a synthetic
sha256("{provider}:{subject}") identity in place of a Nostr pubkey.
- Nip98Auth extractor now also accepts Authorization: Bearer <session-jwt>,
issued after login; stateless HS256 tokens with a CSRF state helper.
- /api/v1/oauth/{provider}/login + GET/POST callback with per-provider
handling: GitHub User-Agent + numeric id + /user/emails, Facebook Graph,
Apple id_token subject, ES256 client-secret JWT and form_post callback.
- Provider email synced into the account on first login (verified where the
provider asserts it, contact_email enabled), non-destructive + best-effort.
- Nostr-only features (NIP-17 DMs, npub display, LIR agreement signing) are
gated to native Nostr accounts.
Fixes #167
- GET /api/v1/account now returns account_type ('nostr'|'oauth') so the
frontend can hide Nostr-only UI (npub, NIP-17 DM settings) for OAuth users.
- PATCH /api/v1/account rejects enabling contact_nip17 for OAuth accounts,
consistent with the other contact-channel validations.
- Remove the 'NPUB:' line from VM-created user and admin notifications. - Replace the invoice 'Nostr Pubkey' line with the account email (shown only when set) - a universal identifier for both Nostr and OAuth accounts.
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.
Fixes #167
Adds generic external OAuth/OIDC login alongside the existing NIP-98 Nostr auth. The pubkey is treated as an opaque account identity, so almost all existing code is unchanged.
Approach
account_typeonusers(nostr|oauth). OAuth accounts store a syntheticsha256("{provider}:{subject}")in the existing 32-bytepubkeycolumn (no schema-type change; unchanged for existing rows, allnostr).Nip98Authextractor now also acceptsAuthorization: Bearer <session-jwt>in addition toAuthorization: Nostr <event>. Handlers useauth.pubkey(), so the ~40 existing call sites were a mechanical rename.hmac/sha2, already in the lockfile) issued after login, plus a signed CSRFstatehelper. Secret comes from config.Providers
GET /api/v1/oauth/{provider}/login→ provider redirect;GET/POST /api/v1/oauth/{provider}/callback→ code exchange → user upsert → session JWT (redirect with#token=or JSON). Configured under a newoauthsection, each withtype:google|github|facebook|apple|oidc.Per-provider quirks handled:
User-Agent, numericidsubject, primary verified email from/user/emailsmeendpointid_token), dynamically-signed ES256 client-secret JWT (p256), andform_post(POST) callbackEmail sync
On first login the provider email is synced into the account (marked verified when the provider asserts it) and
contact_emailenabled by default (OAuth users have no NIP-17 channel). Non-destructive (never overwrites later user edits) and best-effort (a sync failure never blocks login).Nostr-only features gated to native accounts
NIP-17 DMs, npub display (VM-created notification, invoice), and LIR agreement signing now check
account_type == Nostr.Tests
oauth_pubkeydeterminism +upsert_oauth_user(type + idempotency)id_tokenclaim extraction, string/boolemail_verifiednormalizationtypesMigration
20260718003000_user_account_type.sql—account_type smallint unsigned not null default 0.Notes
id_tokensubject/email are read without re-verifying the signature, since the token arrives over TLS directly from Apple's token endpoint (back-channel trust). JWKS verification is an easy follow-up if desired.p256(featuresecdsa,pkcs8,pem) onlnvps_api— all supporting crates already inCargo.lock.