Initial
This commit is contained in:
81
server/controllers/authController.js
Normal file
81
server/controllers/authController.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { authenticateLocal, publicUser, signUser } from '../auth.js';
|
||||
import { loginSchema, passwordChangeSchema, profileSchema, themeSchema } from '../forms/schemas.js';
|
||||
import { changePassword, updateProfile, updateTheme } from '../models/profileModel.js';
|
||||
import { findOrProvisionEntraUser } from '../models/userModel.js';
|
||||
import {
|
||||
buildAuthorizeUrl,
|
||||
exchangeCodeForToken,
|
||||
fetchEntraProfile,
|
||||
isEntraLoginConfigured,
|
||||
signState,
|
||||
verifyState
|
||||
} from '../services/entraService.js';
|
||||
import { getSetting } from '../models/settingsModel.js';
|
||||
import { config } from '../config.js';
|
||||
import { logger } from '../services/logger.js';
|
||||
|
||||
export function login(req, res) {
|
||||
const parsed = loginSchema.safeParse(req.body);
|
||||
if (!parsed.success) return res.status(400).json({ error: 'Valid email and password are required' });
|
||||
const user = authenticateLocal(parsed.data.email, parsed.data.password);
|
||||
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
|
||||
res.json({ token: signUser(user), user: publicUser(user) });
|
||||
}
|
||||
|
||||
export function me(req, res) {
|
||||
res.json({ user: req.user });
|
||||
}
|
||||
|
||||
// Step 1: redirect the browser to the Microsoft sign-in page. 404 when Entra
|
||||
// login is not fully configured so the client cleanly falls back to local auth.
|
||||
export function entraLogin(req, res) {
|
||||
if (!isEntraLoginConfigured()) return res.status(404).json({ error: 'Entra login is not enabled' });
|
||||
const state = signState({ nonce: Date.now() });
|
||||
return res.redirect(buildAuthorizeUrl(state));
|
||||
}
|
||||
|
||||
// Step 2: Microsoft redirects back here with an authorization code. We validate
|
||||
// the signed state, exchange the code, provision the user, and hand the SPA a
|
||||
// POSHManager JWT via the app URL.
|
||||
export async function entraCallback(req, res) {
|
||||
const appBase = (getSetting('server_fqdn') || config.serverFqdn || '').replace(/\/+$/, '');
|
||||
if (!isEntraLoginConfigured()) return res.status(404).json({ error: 'Entra login is not enabled' });
|
||||
try {
|
||||
if (req.query.error) throw new Error(String(req.query.error_description || req.query.error));
|
||||
verifyState(String(req.query.state || ''));
|
||||
const tokens = await exchangeCodeForToken(String(req.query.code || ''));
|
||||
const profile = await fetchEntraProfile(tokens.access_token);
|
||||
const user = findOrProvisionEntraUser(profile);
|
||||
const token = signUser(user);
|
||||
return res.redirect(`${appBase}/?entraToken=${encodeURIComponent(token)}`);
|
||||
} catch (error) {
|
||||
logger.error('entra callback failed', { error: error.message });
|
||||
return res.redirect(`${appBase}/?entraError=${encodeURIComponent(error.message)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function profile(req, res) {
|
||||
const parsed = profileSchema.safeParse(req.body);
|
||||
if (!parsed.success) return res.status(400).json({ error: 'Valid profile information is required' });
|
||||
try {
|
||||
res.json({ user: updateProfile(req.user.id, parsed.data) });
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export function theme(req, res) {
|
||||
const parsed = themeSchema.safeParse(req.body);
|
||||
if (!parsed.success) return res.status(400).json({ error: 'Valid theme id is required' });
|
||||
res.json({ user: updateTheme(req.user.id, parsed.data.theme) });
|
||||
}
|
||||
|
||||
export function password(req, res) {
|
||||
const parsed = passwordChangeSchema.safeParse(req.body);
|
||||
if (!parsed.success) return res.status(400).json({ error: 'New password must be at least 8 characters' });
|
||||
try {
|
||||
res.json(changePassword(req.user.id, parsed.data));
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user