fix(api): resolve consent/dsgvo 'User not found' — principal is userId not email
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled

ConsentController.resolveUserId() and DsgvoController.resolveUserId() read
auth.getName() as an email and did findByEmailAndTenantId(...), but JwtAuthFilter
sets the Authentication principal to the userId (UUID) — the JWT subject is the
userId, not the email. So auth.getName() returns a UUID string, the email lookup
never matched, and every consent/dsgvo call threw 'User not found' (404/500).

This made the DSGVO consent banner unusable: /consent/check 404'd (banner always
shown) and clicking Accept POSTed /consent which 500'd with no UI feedback — the
button appeared to 'not react'.

Fix: parse auth.getName() as the userId UUID directly and verify existsById.
This commit is contained in:
Patrick Plate
2026-06-13 10:52:43 +02:00
parent 26a77b5e16
commit 52251cf711
2 changed files with 27 additions and 8 deletions
@@ -78,10 +78,21 @@ public class ConsentController {
}
private UUID resolveUserId(Authentication auth) {
String email = auth.getName();
return userRepository.findByEmailAndTenantId(email, TenantContext.getCurrentTenant())
.map(User::getId)
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "User not found"));
// JwtAuthFilter sets the Authentication principal to the userId (the JWT subject),
// so auth.getName() is the userId UUID — NOT an email. Parse it directly and verify
// the user exists in the current tenant. (Previously this did findByEmailAndTenantId
// on auth.getName(), which searched the email column for a UUID → always "User not
// found" → 404/500 on every consent call.)
UUID userId;
try {
userId = UUID.fromString(auth.getName());
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(NOT_FOUND, "User not found");
}
if (!userRepository.existsById(userId)) {
throw new ResponseStatusException(NOT_FOUND, "User not found");
}
return userId;
}
private ConsentResponse toResponse(Consent consent) {
@@ -54,9 +54,17 @@ public class DsgvoController {
}
private UUID resolveUserId(Authentication auth) {
String email = auth.getName();
return userRepository.findByEmailAndTenantId(email, TenantContext.getCurrentTenant())
.map(User::getId)
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "User not found"));
// JwtAuthFilter sets the Authentication principal to the userId (the JWT subject),
// so auth.getName() is the userId UUID — NOT an email. Parse it directly.
UUID userId;
try {
userId = UUID.fromString(auth.getName());
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(NOT_FOUND, "User not found");
}
if (!userRepository.existsById(userId)) {
throw new ResponseStatusException(NOT_FOUND, "User not found");
}
return userId;
}
}