mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 18:15:23 +00:00
feat(dashboard-auth): clarify remote dashboard login flow
- add explicit access modes so remote users see setup guidance instead of an ambiguous login - redesign the login page with remote setup messaging, password visibility, and light/dark controls - cover the access-state contract and login UI with focused tests
This commit is contained in:
1 parent
9b3e85c5d9
commit
d130972916
7 files changed
+600
-89
No files matched your search
@@ -7,6 +7,7 @@ import { Router, type Request, type Response } from 'express';
|
||||
import bcrypt from 'bcrypt';
|
||||
import crypto from 'crypto';
|
||||
import { getDashboardAuthConfig } from '../../config/unified-config-loader';
|
||||
import type { DashboardAuthConfig } from '../../config/unified-config-types';
|
||||
import { isLoopbackRemoteAddress, loginRateLimiter } from '../middleware/auth-middleware';
|
||||
|
||||
/**
|
||||
@@ -24,6 +25,52 @@ function timingSafeEqual(a: string, b: string): boolean {
|
||||
|
||||
const router = Router();
|
||||
|
||||
export type DashboardAccessMode = 'open' | 'login' | 'setup';
|
||||
|
||||
export interface DashboardAccessState {
|
||||
authRequired: boolean;
|
||||
authEnabled: boolean;
|
||||
authConfigured: boolean;
|
||||
isLocalAccess: boolean;
|
||||
accessMode: DashboardAccessMode;
|
||||
}
|
||||
|
||||
export function resolveDashboardAccessState(
|
||||
authConfig: DashboardAuthConfig,
|
||||
remoteAddress: string | undefined
|
||||
): DashboardAccessState {
|
||||
const isLocalAccess = isLoopbackRemoteAddress(remoteAddress);
|
||||
const authConfigured = Boolean(authConfig.username && authConfig.password_hash);
|
||||
|
||||
if (authConfig.enabled && authConfigured) {
|
||||
return {
|
||||
authRequired: true,
|
||||
authEnabled: true,
|
||||
authConfigured: true,
|
||||
isLocalAccess,
|
||||
accessMode: 'login',
|
||||
};
|
||||
}
|
||||
|
||||
if (!authConfig.enabled && isLocalAccess) {
|
||||
return {
|
||||
authRequired: false,
|
||||
authEnabled: false,
|
||||
authConfigured,
|
||||
isLocalAccess: true,
|
||||
accessMode: 'open',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
authRequired: true,
|
||||
authEnabled: authConfig.enabled,
|
||||
authConfigured,
|
||||
isLocalAccess,
|
||||
accessMode: 'setup',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/auth/login
|
||||
* Authenticate user with username/password.
|
||||
@@ -94,15 +141,10 @@ router.post('/logout', (req: Request, res: Response) => {
|
||||
*/
|
||||
router.get('/check', (req: Request, res: Response) => {
|
||||
const authConfig = getDashboardAuthConfig();
|
||||
const isLocal = isLoopbackRemoteAddress(req.socket.remoteAddress);
|
||||
|
||||
// When auth is not configured and access is remote, the dashboard API
|
||||
// endpoints return 403. Signal auth-required so the UI can show the
|
||||
// login/setup page instead of a silently broken dashboard.
|
||||
const effectiveAuthRequired = authConfig.enabled || !isLocal;
|
||||
const accessState = resolveDashboardAccessState(authConfig, req.socket.remoteAddress);
|
||||
|
||||
res.json({
|
||||
authRequired: effectiveAuthRequired,
|
||||
...accessState,
|
||||
authenticated: req.session?.authenticated ?? false,
|
||||
username: req.session?.username ?? null,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user