This commit is contained in:
Matthias Hochmeister
2026-03-16 14:41:08 +01:00
parent 5f329bb5c1
commit 215528a521
46 changed files with 462 additions and 251 deletions

View File

@@ -93,7 +93,7 @@ class AuthController {
try {
await authentikService.verifyIdToken(tokens.id_token);
} catch (error) {
logger.warn('ID token verification failed — continuing with userinfo', { error });
logger.error('ID token verification failed — continuing with userinfo (security event)', { error });
}
}
@@ -136,7 +136,31 @@ class AuthController {
metadata: { new_account: true },
});
} else {
// User exists, update last login
// User exists — check active status BEFORE any mutations
if (!user.is_active) {
logger.warn('Inactive user attempted login', { userId: user.id });
auditService.logAudit({
user_id: user.id,
user_email: user.email,
action: AuditAction.PERMISSION_DENIED,
resource_type: AuditResourceType.USER,
resource_id: user.id,
old_value: null,
new_value: null,
ip_address: ip,
user_agent: userAgent,
metadata: { reason: 'account_inactive' },
});
res.status(403).json({
success: false,
message: 'User account is inactive',
});
return;
}
// User is active, proceed with login updates
logger.info('Existing user logging in', {
userId: user.id,
email: user.email,
@@ -148,12 +172,13 @@ class AuthController {
const { given_name: updatedGivenName, family_name: updatedFamilyName } = extractNames(userInfo);
// Refresh profile fields from Authentik on every login
// Refresh profile fields from Authentik on every login (including profile picture)
await userService.updateUser(user.id, {
name: userInfo.name,
given_name: updatedGivenName,
family_name: updatedFamilyName,
preferred_username: userInfo.preferred_username,
name: userInfo.name,
given_name: updatedGivenName,
family_name: updatedFamilyName,
preferred_username: userInfo.preferred_username,
profile_picture_url: userInfo.picture || null,
});
// Audit: returning user login
@@ -174,31 +199,6 @@ class AuthController {
// Extract normalised names once for use in the response
const { given_name: resolvedGivenName, family_name: resolvedFamilyName } = extractNames(userInfo);
// Check if user is active
if (!user.is_active) {
logger.warn('Inactive user attempted login', { userId: user.id });
// Audit the denied login attempt
auditService.logAudit({
user_id: user.id,
user_email: user.email,
action: AuditAction.PERMISSION_DENIED,
resource_type: AuditResourceType.USER,
resource_id: user.id,
old_value: null,
new_value: null,
ip_address: ip,
user_agent: userAgent,
metadata: { reason: 'account_inactive' },
});
res.status(403).json({
success: false,
message: 'User account is inactive',
});
return;
}
// Step 5: Generate internal JWT token
const role = await getUserRole(user.id);
const accessToken = tokenService.generateToken({