Nxing/Caddy forward_auth support. Various fixes to bugs created in earlier edits. Vite server needs different base in dev mode, fixed.

This commit is contained in:
Leo Vasanko 2025-07-13 16:54:53 -06:00
parent 99b5187a33
commit 1c79132e22
6 changed files with 42 additions and 26 deletions

View File

@ -31,7 +31,11 @@ const handleLogin = async () => {
authStore.showMessage('Starting authentication...', 'info')
await authStore.authenticate()
authStore.showMessage('Authentication successful!', 'success', 2000)
authStore.currentView = 'profile'
if (location.pathname.startsWith('/auth/')) {
authStore.currentView = 'profile'
} else {
location.reload()
}
} catch (error) {
authStore.showMessage(`Authentication failed: ${error.message}`, 'error')
}

View File

@ -5,7 +5,7 @@
<form @submit.prevent="handleRegister">
<input
type="text"
v-model="username"
v-model="user_name"
placeholder="Enter username"
required
:disabled="authStore.isLoading"
@ -13,7 +13,7 @@
<button
type="submit"
class="btn-primary"
:disabled="authStore.isLoading || !username.trim()"
:disabled="authStore.isLoading || !user_name.trim()"
>
{{ authStore.isLoading ? 'Registering...' : 'Register Passkey' }}
</button>

View File

@ -15,7 +15,7 @@ export async function register(url, options) {
}
export async function registerUser(user_name) {
return register('/auth/ws/new_user_registration', { user_name })
return register('/auth/ws/register_new', { user_name })
}
export async function registerCredential() {

View File

@ -4,7 +4,7 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/
export default defineConfig({
export default defineConfig(({ command, mode }) => ({
plugins: [
vue(),
],
@ -13,7 +13,7 @@ export default defineConfig({
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
base: '/auth/',
base: command == 'build' ? '/auth/' : '/',
server: {
port: 3000,
proxy: {
@ -29,4 +29,4 @@ export default defineConfig({
emptyOutDir: true,
assetsDir: 'assets'
}
})
}))

View File

@ -15,11 +15,17 @@ from datetime import datetime
from pathlib import Path
from uuid import UUID, uuid4
from fastapi import FastAPI, Request, Response, WebSocket, WebSocketDisconnect
from fastapi import (
FastAPI,
Request,
Response,
WebSocket,
WebSocketDisconnect,
)
from fastapi import (
Path as FastAPIPath,
)
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from webauthn.helpers.exceptions import InvalidAuthenticationResponse
@ -204,7 +210,7 @@ async def register_chat(
)
await ws.send_json(options)
response = await ws.receive_json()
return passkey.reg_verify(response, challenge, user_id)
return passkey.reg_verify(response, challenge, user_id, origin=origin)
@app.websocket("/auth/ws/authenticate")
@ -269,6 +275,27 @@ async def api_validate_token(request: Request):
return await validate_token(request)
@app.get("/auth/forward-auth")
async def forward_authentication(request: Request):
"""A verification endpoint to use with Caddy forward_auth or Nginx auth_request."""
result = await validate_token(request)
if result.get("status") != "success":
# Serve the index.html of the authentication app if not authenticated
return FileResponse(
STATIC_DIR / "index.html",
status_code=401,
headers={"www-authenticate": "PrivateToken"},
)
# If authenticated, return a success response
return JSONResponse(
result,
headers={
"x-auth-user-id": result["user_id"],
},
)
@app.post("/auth/logout")
async def api_logout(response: Response):
"""Log out the current user by clearing the session cookie."""
@ -315,20 +342,6 @@ async def reset_authentication(
return response
@app.get("/auth/user-info-by-passphrase")
async def api_get_user_info_by_passphrase(token: str):
"""Get user information using the passphrase."""
reset_token = await db.get_reset_token(token)
if not reset_token:
return Response(content="Invalid or expired passphrase", status_code=403)
user = await db.get_user_by_id(reset_token.user_id)
if not user:
return Response(content="User not found", status_code=404)
return {"user_name": user.user_name}
# Serve static files
app.mount(
"/auth/assets", StaticFiles(directory=STATIC_DIR / "assets"), name="static assets"

View File

@ -141,11 +141,10 @@ class Passkey:
Registration verification result
"""
credential = parse_registration_credential_json(response_json)
expected_origin = origin or self.origin
registration = verify_registration_response(
credential=credential,
expected_challenge=expected_challenge,
expected_origin=expected_origin,
expected_origin=origin or self.origin,
expected_rp_id=self.rp_id,
)
return StoredCredential(