fix: Configure HTTPS domain and OAuth callback route

- Update configuration for production HTTPS domain (devden.hiddenden.cafe)
- Add nginx reverse proxy for /api and /auth routes to backend
- Create auth-callback.html to handle Microsoft Entra ID OAuth redirect
- Fix API_URL in script.js to use same origin (remove :8000 port)
- Add cache-busting query parameter (?v=2) to script.js
- Update .env.example with HTTPS requirements documentation

This resolves Azure Entra ID redirect URI mismatch and enables proper
OAuth authentication flow through the nginx frontend proxy.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-16 10:43:16 +00:00
parent b61aa68bcd
commit 44ca64e120
7 changed files with 90 additions and 5 deletions

View File

@@ -13,11 +13,15 @@ OPENAI_MODEL=gpt-4o-mini
# API Configuration # API Configuration
MAX_TOKENS=4000 MAX_TOKENS=4000
TEMPERATURE=0.7 TEMPERATURE=0.7
# For local development use: http://localhost:3000
# For production use your domain with HTTPS: https://your-domain.com
FRONTEND_URL=http://localhost:3000 FRONTEND_URL=http://localhost:3000
# Microsoft Entra ID (Azure AD) # Microsoft Entra ID (Azure AD)
# Create an app registration at: https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps # Create an app registration at: https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps
# Add redirect URI: http://localhost:3000/auth/callback # IMPORTANT: Azure requires HTTPS for non-localhost redirect URIs
# For localhost: http://localhost:3000/auth/callback
# For production: https://your-domain.com/auth/callback
ENTRA_TENANT_ID=your-tenant-id ENTRA_TENANT_ID=your-tenant-id
ENTRA_CLIENT_ID=your-client-id ENTRA_CLIENT_ID=your-client-id
ENTRA_CLIENT_SECRET=your-client-secret ENTRA_CLIENT_SECRET=your-client-secret

View File

@@ -6,6 +6,7 @@ COPY default.conf /etc/nginx/conf.d/default.conf
# Copy static files to nginx html directory # Copy static files to nginx html directory
COPY index.html /usr/share/nginx/html/ COPY index.html /usr/share/nginx/html/
COPY auth-callback.html /usr/share/nginx/html/
COPY style.css /usr/share/nginx/html/ COPY style.css /usr/share/nginx/html/
COPY script.js /usr/share/nginx/html/ COPY script.js /usr/share/nginx/html/

47
auth-callback.html Normal file
View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DevDen - Authentication</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #1e1e2e;
color: #cdd6f4;
font-family: monospace;
}
.loader {
text-align: center;
}
.spinner {
border: 3px solid #313244;
border-top: 3px solid #cba6f7;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="loader">
<div class="spinner"></div>
<p>Completing authentication...</p>
</div>
<script>
// Redirect to root with query parameters preserved
window.location.href = '/' + window.location.search;
</script>
</body>
</html>

View File

@@ -4,6 +4,38 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
# Proxy API requests to backend
location /api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
# Support for SSE (Server-Sent Events)
proxy_buffering off;
proxy_cache off;
}
# OAuth callback endpoint - redirect to root with query params
location = /auth/callback {
try_files /auth-callback.html =404;
}
# Proxy other auth requests to backend
location /auth/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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;
}
# Enable SPA routing - try to serve the file, then directory, then fallback to index.html # Enable SPA routing - try to serve the file, then directory, then fallback to index.html
location / { location / {
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;

View File

@@ -25,11 +25,11 @@ services:
- DEFAULT_PROVIDER=${DEFAULT_PROVIDER:-claude} - DEFAULT_PROVIDER=${DEFAULT_PROVIDER:-claude}
- CLAUDE_MODEL=${CLAUDE_MODEL:-claude-3-5-sonnet-20241022} - CLAUDE_MODEL=${CLAUDE_MODEL:-claude-3-5-sonnet-20241022}
- OPENAI_MODEL=${OPENAI_MODEL:-gpt-4-turbo-preview} - OPENAI_MODEL=${OPENAI_MODEL:-gpt-4-turbo-preview}
- FRONTEND_URL=http://localhost:3000 - FRONTEND_URL=https://devden.hiddenden.cafe
- ENTRA_TENANT_ID=${ENTRA_TENANT_ID} - ENTRA_TENANT_ID=${ENTRA_TENANT_ID}
- ENTRA_CLIENT_ID=${ENTRA_CLIENT_ID} - ENTRA_CLIENT_ID=${ENTRA_CLIENT_ID}
- ENTRA_CLIENT_SECRET=${ENTRA_CLIENT_SECRET} - ENTRA_CLIENT_SECRET=${ENTRA_CLIENT_SECRET}
- ENTRA_REDIRECT_URI=${ENTRA_REDIRECT_URI:-http://localhost:3000/auth/callback} - ENTRA_REDIRECT_URI=https://devden.hiddenden.cafe/auth/callback
- JWT_SECRET=${JWT_SECRET:-change-this-in-production} - JWT_SECRET=${JWT_SECRET:-change-this-in-production}
- JWT_EXPIRY_HOURS=${JWT_EXPIRY_HOURS:-24} - JWT_EXPIRY_HOURS=${JWT_EXPIRY_HOURS:-24}
env_file: env_file:

View File

@@ -63,6 +63,6 @@
<div id="debugOutput" style="margin-top: 10px; max-height: 200px; overflow-y: auto;"></div> <div id="debugOutput" style="margin-top: 10px; max-height: 200px; overflow-y: auto;"></div>
</div> </div>
<script src="script.js"></script> <script src="script.js?v=2"></script>
</body> </body>
</html> </html>

View File

@@ -72,7 +72,8 @@ const welcomeInput = document.getElementById("welcomeInput");
const chatInput = document.getElementById("chatInput"); const chatInput = document.getElementById("chatInput");
const loginBtn = document.getElementById("loginBtn"); const loginBtn = document.getElementById("loginBtn");
const API_URL = "http://localhost:8000"; // API URL is same as frontend (nginx proxies /api and /auth to backend)
const API_URL = window.location.origin;
let isInChat = false; let isInChat = false;