Fix passkey login 429 by forwarding client IPs correctly.
Forward X-Forwarded-For through frontend nginx, use TRUST_PROXY=1 for the Docker hop, and limit auth rate limiting to login flows only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+2
-2
@@ -13,8 +13,8 @@ RP_ID=localhost
|
|||||||
# Must match the frontend URL exactly (Vite dev: http://localhost:5173; Docker: http://localhost)
|
# Must match the frontend URL exactly (Vite dev: http://localhost:5173; Docker: http://localhost)
|
||||||
ORIGIN=http://localhost:5173
|
ORIGIN=http://localhost:5173
|
||||||
|
|
||||||
# Behind Nginx Proxy Manager — see docs/deployment/npm-security.md
|
# Behind reverse proxy — see docs/deployment/npm-security.md
|
||||||
# TRUST_PROXY=172.16.10.10
|
# Docker Compose (NPM → frontend nginx → backend): TRUST_PROXY=1
|
||||||
# TRUST_PROXY=1
|
# TRUST_PROXY=1
|
||||||
|
|
||||||
# Docker Compose database (required for production deploy)
|
# Docker Compose database (required for production deploy)
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ server {
|
|||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection 'upgrade';
|
proxy_set_header Connection 'upgrade';
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_cache_bypass $http_upgrade;
|
proxy_cache_bypass $http_upgrade;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ proxy_set_header X-Real-IP $remote_addr;
|
|||||||
ORIGIN=https://kapteins-daagbok.eu
|
ORIGIN=https://kapteins-daagbok.eu
|
||||||
RP_ID=kapteins-daagbok.eu
|
RP_ID=kapteins-daagbok.eu
|
||||||
SESSION_SECRET=<min. 32 Zeichen, openssl rand -base64 48>
|
SESSION_SECRET=<min. 32 Zeichen, openssl rand -base64 48>
|
||||||
TRUST_PROXY=172.16.10.10
|
# Docker Compose: Frontend-Nginx ist der direkte Proxy zum Backend → 1 Hop
|
||||||
# oder TRUST_PROXY=1 für genau einen Proxy-Hop
|
TRUST_PROXY=1
|
||||||
|
# Nur bei direktem Backend-Zugriff ohne Frontend-Nginx: NPM-IP, z. B. TRUST_PROXY=172.16.10.10
|
||||||
```
|
```
|
||||||
|
|
||||||
`ORIGIN` muss **exakt** der Browser-URL entsprechen (ohne trailing slash).
|
`ORIGIN` muss **exakt** der Browser-URL entsprechen (ohne trailing slash).
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ if ! grep -q "^POSTGRES_PASSWORD=" "$ENV_FILE" || grep -q "^POSTGRES_PASSWORD=$"
|
|||||||
else
|
else
|
||||||
echo " keep POSTGRES_PASSWORD (already set)"
|
echo " keep POSTGRES_PASSWORD (already set)"
|
||||||
fi
|
fi
|
||||||
# NPM on 172.16.10.10 → app on this host
|
# Frontend-Nginx → Backend (one hop); NPM is in front of Nginx, not Backend directly
|
||||||
ensure_var TRUST_PROXY "172.16.10.10"
|
ensure_var TRUST_PROXY "1"
|
||||||
|
|
||||||
echo "Done. Verify with: docker exec daagbox-prod-db psql -U postgres -d daagbox -c 'SELECT 1'"
|
echo "Done. Verify with: docker exec daagbox-prod-db psql -U postgres -d daagbox -c 'SELECT 1'"
|
||||||
|
|||||||
+18
-1
@@ -45,6 +45,18 @@ export function createApp(): express.Express {
|
|||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
app.use(express.json({ limit: '50mb' }))
|
app.use(express.json({ limit: '50mb' }))
|
||||||
|
|
||||||
|
/** Passkey login/register only — not person-pool, profile, etc. */
|
||||||
|
const authFlowPaths = new Set([
|
||||||
|
'/register-options',
|
||||||
|
'/register-verify',
|
||||||
|
'/login-options',
|
||||||
|
'/login-verify',
|
||||||
|
'/reauth-options',
|
||||||
|
'/reauth-verify',
|
||||||
|
'/logout',
|
||||||
|
'/session'
|
||||||
|
])
|
||||||
|
|
||||||
const authLimiter = rateLimit({
|
const authLimiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 60,
|
max: 60,
|
||||||
@@ -66,7 +78,12 @@ export function createApp(): express.Express {
|
|||||||
legacyHeaders: false
|
legacyHeaders: false
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use('/api/auth', authLimiter)
|
app.use('/api/auth', (req, res, next) => {
|
||||||
|
if (authFlowPaths.has(req.path)) {
|
||||||
|
return authLimiter(req, res, next)
|
||||||
|
}
|
||||||
|
return next()
|
||||||
|
})
|
||||||
app.use('/api/collaboration/invite-details', publicCollaborationLimiter)
|
app.use('/api/collaboration/invite-details', publicCollaborationLimiter)
|
||||||
app.use('/api/collaboration/share-pull', publicCollaborationLimiter)
|
app.use('/api/collaboration/share-pull', publicCollaborationLimiter)
|
||||||
app.use('/api', apiLimiter)
|
app.use('/api', apiLimiter)
|
||||||
|
|||||||
Reference in New Issue
Block a user