Bump CACHEBUST to v8 for backend and frontend rebuilds; update Nginx and NostrAuthGuard to handle X-Forwarded-Prefix for NIP-98 compliance
This commit is contained in:
@@ -88,9 +88,15 @@ export class NostrAuthGuard extends AuthGuard('nostr') {
|
||||
'http';
|
||||
const host =
|
||||
request.get('host') ?? request.headers.host ?? request.hostname;
|
||||
|
||||
// When behind a reverse proxy that strips a path prefix (e.g. /api),
|
||||
// the proxy should forward X-Forwarded-Prefix so we can reconstruct
|
||||
// the original URL that the client signed in its NIP-98 event.
|
||||
const prefix =
|
||||
(request.headers['x-forwarded-prefix'] as string | undefined) ?? '';
|
||||
const path = request.originalUrl ?? request.url ?? '';
|
||||
|
||||
return `${protocolHeader}://${host}${path}`;
|
||||
return `${protocolHeader}://${host}${prefix}${path}`;
|
||||
}
|
||||
|
||||
private mapErrorToHttpException(error: NostrAuthError) {
|
||||
|
||||
@@ -20,7 +20,7 @@ services:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
CACHEBUST: "7"
|
||||
CACHEBUST: "8"
|
||||
VITE_USE_MOCK_DATA: "false"
|
||||
VITE_CONTENT_ORIGIN: ${FRONTEND_URL}
|
||||
VITE_INDEEHUB_API_URL: /api
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
CACHEBUST: "7"
|
||||
CACHEBUST: "8"
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# ── Core ─────────────────────────────────────────────
|
||||
@@ -179,7 +179,7 @@ services:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile.ffmpeg
|
||||
args:
|
||||
CACHEBUST: "7"
|
||||
CACHEBUST: "8"
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ENVIRONMENT: production
|
||||
|
||||
@@ -41,6 +41,9 @@ server {
|
||||
# Trust the outer reverse proxy's X-Forwarded-Proto when present,
|
||||
# otherwise fall back to the connection scheme.
|
||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
||||
# Preserve the original /api prefix so NIP-98 URL verification
|
||||
# can reconstruct the URL the client actually signed.
|
||||
proxy_set_header X-Forwarded-Prefix /api;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
|
||||
|
||||
@@ -119,18 +119,22 @@ class AuthService {
|
||||
* Signs a kind-27235 event and sends it as the Authorization header.
|
||||
*/
|
||||
async createNostrSession(_request: NostrSessionRequest): Promise<NostrSessionResponse> {
|
||||
const url = `${apiConfig.baseURL}/auth/nostr/session`
|
||||
const relativeUrl = `${apiConfig.baseURL}/auth/nostr/session`
|
||||
const method = 'POST'
|
||||
|
||||
// NIP-98 requires an absolute URL in the event tag so it matches
|
||||
// what the backend reconstructs from Host / X-Forwarded-Proto.
|
||||
const absoluteUrl = new URL(relativeUrl, window.location.origin).toString()
|
||||
|
||||
// Create NIP-98 auth header — no body is sent
|
||||
const authHeader = await createNip98AuthHeader(url, method)
|
||||
const authHeader = await createNip98AuthHeader(absoluteUrl, method)
|
||||
|
||||
// Send the request without a body.
|
||||
// We use axios({ method }) instead of axios.post(url, data) to
|
||||
// guarantee no Content-Type or body is serialized.
|
||||
const response = await axios<NostrSessionResponse>({
|
||||
method: 'POST',
|
||||
url,
|
||||
url: absoluteUrl,
|
||||
headers: { Authorization: authHeader },
|
||||
timeout: apiConfig.timeout,
|
||||
})
|
||||
|
||||
@@ -67,15 +67,19 @@ class Nip98Service {
|
||||
*/
|
||||
async createSession(signer: any, pubkey: string): Promise<boolean> {
|
||||
try {
|
||||
const url = `${indeehubApiConfig.baseURL}/auth/nostr/session`
|
||||
const relativeUrl = `${indeehubApiConfig.baseURL}/auth/nostr/session`
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
|
||||
// NIP-98 requires an absolute URL in the event tag so it matches
|
||||
// what the backend reconstructs from Host / X-Forwarded-Proto.
|
||||
const absoluteUrl = new URL(relativeUrl, window.location.origin).toString()
|
||||
|
||||
// Build the NIP-98 event
|
||||
const event = {
|
||||
kind: 27235,
|
||||
created_at: now,
|
||||
tags: [
|
||||
['u', url],
|
||||
['u', absoluteUrl],
|
||||
['method', 'POST'],
|
||||
],
|
||||
content: '',
|
||||
@@ -98,7 +102,7 @@ class Nip98Service {
|
||||
// Send to backend — no body to avoid NIP-98 payload mismatch
|
||||
const response = await axios({
|
||||
method: 'POST',
|
||||
url,
|
||||
url: absoluteUrl,
|
||||
headers: {
|
||||
Authorization: `Nostr ${encodedEvent}`,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user