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';
|
'http';
|
||||||
const host =
|
const host =
|
||||||
request.get('host') ?? request.headers.host ?? request.hostname;
|
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 ?? '';
|
const path = request.originalUrl ?? request.url ?? '';
|
||||||
|
|
||||||
return `${protocolHeader}://${host}${path}`;
|
return `${protocolHeader}://${host}${prefix}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapErrorToHttpException(error: NostrAuthError) {
|
private mapErrorToHttpException(error: NostrAuthError) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
CACHEBUST: "7"
|
CACHEBUST: "8"
|
||||||
VITE_USE_MOCK_DATA: "false"
|
VITE_USE_MOCK_DATA: "false"
|
||||||
VITE_CONTENT_ORIGIN: ${FRONTEND_URL}
|
VITE_CONTENT_ORIGIN: ${FRONTEND_URL}
|
||||||
VITE_INDEEHUB_API_URL: /api
|
VITE_INDEEHUB_API_URL: /api
|
||||||
@@ -47,7 +47,7 @@ services:
|
|||||||
context: ./backend
|
context: ./backend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
CACHEBUST: "7"
|
CACHEBUST: "8"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
# ── Core ─────────────────────────────────────────────
|
# ── Core ─────────────────────────────────────────────
|
||||||
@@ -179,7 +179,7 @@ services:
|
|||||||
context: ./backend
|
context: ./backend
|
||||||
dockerfile: Dockerfile.ffmpeg
|
dockerfile: Dockerfile.ffmpeg
|
||||||
args:
|
args:
|
||||||
CACHEBUST: "7"
|
CACHEBUST: "8"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
ENVIRONMENT: production
|
ENVIRONMENT: production
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ server {
|
|||||||
# Trust the outer reverse proxy's X-Forwarded-Proto when present,
|
# Trust the outer reverse proxy's X-Forwarded-Proto when present,
|
||||||
# otherwise fall back to the connection scheme.
|
# otherwise fall back to the connection scheme.
|
||||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
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_read_timeout 300s;
|
||||||
proxy_send_timeout 300s;
|
proxy_send_timeout 300s;
|
||||||
|
|
||||||
|
|||||||
@@ -119,18 +119,22 @@ class AuthService {
|
|||||||
* Signs a kind-27235 event and sends it as the Authorization header.
|
* Signs a kind-27235 event and sends it as the Authorization header.
|
||||||
*/
|
*/
|
||||||
async createNostrSession(_request: NostrSessionRequest): Promise<NostrSessionResponse> {
|
async createNostrSession(_request: NostrSessionRequest): Promise<NostrSessionResponse> {
|
||||||
const url = `${apiConfig.baseURL}/auth/nostr/session`
|
const relativeUrl = `${apiConfig.baseURL}/auth/nostr/session`
|
||||||
const method = 'POST'
|
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
|
// 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.
|
// Send the request without a body.
|
||||||
// We use axios({ method }) instead of axios.post(url, data) to
|
// We use axios({ method }) instead of axios.post(url, data) to
|
||||||
// guarantee no Content-Type or body is serialized.
|
// guarantee no Content-Type or body is serialized.
|
||||||
const response = await axios<NostrSessionResponse>({
|
const response = await axios<NostrSessionResponse>({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url,
|
url: absoluteUrl,
|
||||||
headers: { Authorization: authHeader },
|
headers: { Authorization: authHeader },
|
||||||
timeout: apiConfig.timeout,
|
timeout: apiConfig.timeout,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -67,15 +67,19 @@ class Nip98Service {
|
|||||||
*/
|
*/
|
||||||
async createSession(signer: any, pubkey: string): Promise<boolean> {
|
async createSession(signer: any, pubkey: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const url = `${indeehubApiConfig.baseURL}/auth/nostr/session`
|
const relativeUrl = `${indeehubApiConfig.baseURL}/auth/nostr/session`
|
||||||
const now = Math.floor(Date.now() / 1000)
|
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
|
// Build the NIP-98 event
|
||||||
const event = {
|
const event = {
|
||||||
kind: 27235,
|
kind: 27235,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
tags: [
|
tags: [
|
||||||
['u', url],
|
['u', absoluteUrl],
|
||||||
['method', 'POST'],
|
['method', 'POST'],
|
||||||
],
|
],
|
||||||
content: '',
|
content: '',
|
||||||
@@ -98,7 +102,7 @@ class Nip98Service {
|
|||||||
// Send to backend — no body to avoid NIP-98 payload mismatch
|
// Send to backend — no body to avoid NIP-98 payload mismatch
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url,
|
url: absoluteUrl,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Nostr ${encodedEvent}`,
|
Authorization: `Nostr ${encodedEvent}`,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user