Fix OIDC login immediate logout — rename JWT subject prefix ui: → user:
OIDC tokens had subject "oidc:<sub>" which didn't match the "ui:" prefix check in JwtAuthenticationFilter, causing every post-login API call to return 401 and trigger automatic logout. Renamed the prefix from "ui:" to "user:" across all auth code for clarity (it covers both browser and API clients, not just UI). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,7 +52,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
JwtValidationResult result = jwtService.validateAccessToken(token);
|
||||
String subject = result.subject();
|
||||
|
||||
if (subject.startsWith("ui:")) {
|
||||
if (subject.startsWith("user:")) {
|
||||
// UI user token — authenticate with roles from JWT
|
||||
List<GrantedAuthority> authorities = toAuthorities(result.roles());
|
||||
UsernamePasswordAuthenticationToken auth =
|
||||
|
||||
@@ -110,7 +110,7 @@ public class OidcAuthController {
|
||||
OidcTokenExchanger.OidcUserInfo oidcUser =
|
||||
tokenExchanger.exchange(request.code(), request.redirectUri());
|
||||
|
||||
String userId = "oidc:" + oidcUser.subject();
|
||||
String userId = "user:oidc:" + oidcUser.subject();
|
||||
String issuerHost = URI.create(config.get().issuerUri()).getHost();
|
||||
String provider = "oidc:" + issuerHost;
|
||||
|
||||
@@ -127,8 +127,8 @@ public class OidcAuthController {
|
||||
userRepository.upsert(new UserInfo(
|
||||
userId, provider, oidcUser.email(), oidcUser.name(), roles, Instant.now()));
|
||||
|
||||
String accessToken = jwtService.createAccessToken(userId, "ui", roles);
|
||||
String refreshToken = jwtService.createRefreshToken(userId, "ui", roles);
|
||||
String accessToken = jwtService.createAccessToken(userId, "user", roles);
|
||||
String refreshToken = jwtService.createRefreshToken(userId, "user", roles);
|
||||
|
||||
return ResponseEntity.ok(new AuthTokenResponse(accessToken, refreshToken));
|
||||
} catch (ResponseStatusException e) {
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.List;
|
||||
* Authentication endpoints for the UI (local credentials).
|
||||
* <p>
|
||||
* Validates credentials against environment-configured username/password,
|
||||
* then issues JWTs with {@code ui:} prefixed subjects and ADMIN roles.
|
||||
* then issues JWTs with {@code user:} prefixed subjects and ADMIN roles.
|
||||
* Upserts the user into the user store on login.
|
||||
*/
|
||||
@RestController
|
||||
@@ -70,7 +70,7 @@ public class UiAuthController {
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid credentials");
|
||||
}
|
||||
|
||||
String subject = "ui:" + request.username();
|
||||
String subject = "user:" + request.username();
|
||||
List<String> roles = List.of("ADMIN");
|
||||
|
||||
// Upsert local user into store
|
||||
@@ -81,8 +81,8 @@ public class UiAuthController {
|
||||
log.warn("Failed to upsert local user to store (login continues): {}", e.getMessage());
|
||||
}
|
||||
|
||||
String accessToken = jwtService.createAccessToken(subject, "ui", roles);
|
||||
String refreshToken = jwtService.createRefreshToken(subject, "ui", roles);
|
||||
String accessToken = jwtService.createAccessToken(subject, "user", roles);
|
||||
String refreshToken = jwtService.createRefreshToken(subject, "user", roles);
|
||||
|
||||
log.info("UI user logged in: {}", request.username());
|
||||
return ResponseEntity.ok(new AuthTokenResponse(accessToken, refreshToken));
|
||||
@@ -96,14 +96,14 @@ public class UiAuthController {
|
||||
public ResponseEntity<AuthTokenResponse> refresh(@RequestBody RefreshRequest request) {
|
||||
try {
|
||||
JwtValidationResult result = jwtService.validateRefreshToken(request.refreshToken());
|
||||
if (!result.subject().startsWith("ui:")) {
|
||||
if (!result.subject().startsWith("user:")) {
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Not a UI token");
|
||||
}
|
||||
|
||||
// Preserve roles from the refresh token
|
||||
List<String> roles = result.roles();
|
||||
String accessToken = jwtService.createAccessToken(result.subject(), "ui", roles);
|
||||
String refreshToken = jwtService.createRefreshToken(result.subject(), "ui", roles);
|
||||
String accessToken = jwtService.createAccessToken(result.subject(), "user", roles);
|
||||
String refreshToken = jwtService.createRefreshToken(result.subject(), "user", roles);
|
||||
|
||||
return ResponseEntity.ok(new AuthTokenResponse(accessToken, refreshToken));
|
||||
} catch (ResponseStatusException e) {
|
||||
|
||||
@@ -75,10 +75,10 @@ class JwtServiceTest {
|
||||
@Test
|
||||
void accessToken_rolesRoundTrip() {
|
||||
List<String> roles = List.of("ADMIN", "OPERATOR");
|
||||
String token = jwtService.createAccessToken("ui:admin", "ui", roles);
|
||||
String token = jwtService.createAccessToken("user:admin", "user", roles);
|
||||
JwtService.JwtValidationResult result = jwtService.validateAccessToken(token);
|
||||
assertEquals("ui:admin", result.subject());
|
||||
assertEquals("ui", result.group());
|
||||
assertEquals("user:admin", result.subject());
|
||||
assertEquals("user", result.group());
|
||||
assertEquals(roles, result.roles());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user