From 9f39e2393b26245504ca1b5b9420d4a9381e3e33 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Wed, 18 Feb 2026 14:32:57 -0700 Subject: [PATCH] fix: change WebAuthn RP ID from jeffemmett.com to rspace.online The RP ID jeffemmett.com caused "relying party ID is not a registrable domain suffix" errors on *.rspace.online subdomains. Related Origins also exceeded the 5 eTLD+1 browser limit with 18+ domains listed. Now rspace.online is the RP ID, so all *.rspace.online subdomains (including cca.rspace.online) are valid automatically. The Related Origins file only lists non-rspace.online r* ecosystem domains. Also points rspace-header auth URL to auth.rspace.online. Co-Authored-By: Claude Opus 4.6 --- docker-compose.encryptid.yml | 4 ++-- lib/rspace-header.ts | 2 +- public/.well-known/webauthn | 13 +------------ server/index.ts | 20 ++++++++++++++++++++ src/encryptid/server.ts | 28 ++++++++++++++++------------ src/encryptid/webauthn.ts | 2 +- 6 files changed, 41 insertions(+), 28 deletions(-) diff --git a/docker-compose.encryptid.yml b/docker-compose.encryptid.yml index 10223b5..aa94164 100644 --- a/docker-compose.encryptid.yml +++ b/docker-compose.encryptid.yml @@ -29,8 +29,8 @@ services: - "traefik.http.routers.encryptid.entrypoints=web" - "traefik.http.routers.encryptid.priority=150" - "traefik.http.services.encryptid.loadbalancer.server.port=3000" - # Also serve from root domain for .well-known (WebAuthn Related Origins) - - "traefik.http.routers.encryptid-wellknown.rule=Host(`jeffemmett.com`) && PathPrefix(`/.well-known/webauthn`)" + # Also serve from RP ID domain for .well-known (WebAuthn Related Origins) + - "traefik.http.routers.encryptid-wellknown.rule=Host(`rspace.online`) && PathPrefix(`/.well-known/webauthn`)" - "traefik.http.routers.encryptid-wellknown.entrypoints=web" - "traefik.http.routers.encryptid-wellknown.priority=200" - "traefik.http.routers.encryptid-wellknown.service=encryptid" diff --git a/lib/rspace-header.ts b/lib/rspace-header.ts index 15de15b..dcd329c 100644 --- a/lib/rspace-header.ts +++ b/lib/rspace-header.ts @@ -6,7 +6,7 @@ */ const SESSION_KEY = 'encryptid_session'; -const ENCRYPTID_URL = 'https://encryptid.jeffemmett.com'; +const ENCRYPTID_URL = 'https://auth.rspace.online'; interface SessionState { accessToken: string; diff --git a/public/.well-known/webauthn b/public/.well-known/webauthn index 3ec9c0c..a4d6e1a 100644 --- a/public/.well-known/webauthn +++ b/public/.well-known/webauthn @@ -1,20 +1,9 @@ { "origins": [ - "https://rspace.online", - "https://app.rspace.online", - "https://dev.rspace.online", "https://rwallet.online", - "https://app.rwallet.online", "https://rvote.online", - "https://app.rvote.online", "https://rmaps.online", - "https://app.rmaps.online", "https://rfiles.online", - "https://app.rfiles.online", - "https://encryptid.jeffemmett.com", - "https://jeffemmett.com", - "https://canvas.jeffemmett.com", - "http://localhost:3000", - "http://localhost:5173" + "https://rnotes.online" ] } diff --git a/server/index.ts b/server/index.ts index b7825f2..547eaa1 100644 --- a/server/index.ts +++ b/server/index.ts @@ -194,6 +194,26 @@ const server = Bun.serve({ return new Response("WebSocket upgrade failed", { status: 400 }); } + // Serve .well-known/webauthn for WebAuthn Related Origins + // RP ID is rspace.online — *.rspace.online subdomains are automatic, + // this file lists non-rspace.online origins that should also be allowed. + if (url.pathname === "/.well-known/webauthn") { + return Response.json({ + origins: [ + "https://rwallet.online", + "https://rvote.online", + "https://rmaps.online", + "https://rfiles.online", + "https://rnotes.online", + ], + }, { + headers: { + "Access-Control-Allow-Origin": "*", + "Cache-Control": "public, max-age=3600", + }, + }); + } + // API routes if (url.pathname.startsWith("/api/")) { return handleAPI(req, url); diff --git a/src/encryptid/server.ts b/src/encryptid/server.ts index b32e747..532fb46 100644 --- a/src/encryptid/server.ts +++ b/src/encryptid/server.ts @@ -47,7 +47,7 @@ import { const CONFIG = { port: process.env.PORT || 3000, - rpId: 'jeffemmett.com', + rpId: 'rspace.online', rpName: 'EncryptID', jwtSecret: (() => { const secret = process.env.JWT_SECRET; @@ -66,10 +66,14 @@ const CONFIG = { }, recoveryUrl: process.env.RECOVERY_URL || 'https://auth.rspace.online/recover', allowedOrigins: [ - 'https://auth.rspace.online', - 'https://encryptid.jeffemmett.com', - 'https://jeffemmett.com', + // rspace.online — RP ID domain and all subdomains 'https://rspace.online', + 'https://auth.rspace.online', + 'https://cca.rspace.online', + 'https://demo.rspace.online', + 'https://app.rspace.online', + 'https://dev.rspace.online', + // r* ecosystem apps (each *.online is an eTLD+1 — Related Origins limit is 5) 'https://rwallet.online', 'https://rvote.online', 'https://rmaps.online', @@ -84,11 +88,7 @@ const CONFIG = { 'https://rstack.online', 'https://rpubs.online', 'https://rauctions.online', - 'https://shop.mycofi.earth', - 'https://canvas.jeffemmett.com', - 'https://press.jeffemmett.com', - 'https://cart.jeffemmett.com', - 'https://cart.mycofi.earth', + // Development 'http://localhost:3000', 'http://localhost:5173', ], @@ -205,10 +205,14 @@ app.use('*', cors({ // ============================================================================ // Serve .well-known/webauthn for Related Origins +// Only list non-rspace.online origins here — *.rspace.online subdomains are +// automatically valid because rspace.online is the RP ID. +// Keep to max 5 eTLD+1 labels to stay within browser limits. app.get('/.well-known/webauthn', (c) => { - return c.json({ - origins: CONFIG.allowedOrigins.filter(o => o.startsWith('https://')), - }); + const nonRspaceOrigins = CONFIG.allowedOrigins.filter( + o => o.startsWith('https://') && !o.endsWith('.rspace.online') && o !== 'https://rspace.online' + ); + return c.json({ origins: nonRspaceOrigins }); }); // Health check — includes database connectivity diff --git a/src/encryptid/webauthn.ts b/src/encryptid/webauthn.ts index 6b8d0bf..b92cfed 100644 --- a/src/encryptid/webauthn.ts +++ b/src/encryptid/webauthn.ts @@ -37,7 +37,7 @@ export interface EncryptIDConfig { // Default configuration for EncryptID const DEFAULT_CONFIG: EncryptIDConfig = { - rpId: 'jeffemmett.com', // Root domain for Related Origins to work across subdomains + rpId: 'rspace.online', // Root domain — all *.rspace.online subdomains are valid rpName: 'EncryptID', origin: typeof window !== 'undefined' ? window.location.origin : '', userVerification: 'required',