quarkusio/quarkus · error · WebSocketServerException
New SecurityIdentity principal name '%s' is different than p
Error message
New SecurityIdentity principal name '%s' is different than previous principal name '%s'. SecurityIdentity update is aborted
What it means
When updating the SecurityIdentity of an existing WebSocket connection, WebSockets Next forbids a principal-name change: the new identity must belong to the same user. A mismatch aborts the update with WebSocketServerException to prevent one user's connection from being silently re-bound to a different identity.
Source
Thrown at extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/SecuritySupport.java:116
private synchronized void updateSecurityIdentity(SecurityIdentity updatedIdentity, WebSocketConnectionImpl connection) {
if (connection.isClosed()) {
return;
}
if (updatedIdentity.isAnonymous()) {
throw new AuthenticationFailedException("Updated SecurityIdentity is anonymous");
}
if (LOG.isDebugEnabled()) {
Long expireAt = updatedIdentity.getAttribute(QUARKUS_IDENTITY_EXPIRE_TIME);
String path = routingContext.normalizedPath();
String principalName = updatedIdentity.getPrincipal().getName();
LOG.debugf(
"Updated 'SecurityIdentity' with principal name '%s' used by WebSocket connection '%s' and path '%s', the new SecurityIdentity expires at '%d'",
principalName, connection.id(), path, expireAt);
}
String previousPrincipalName = this.identity.getPrincipal().getName();
String currentPrincipalName = updatedIdentity.getPrincipal().getName();
if (!previousPrincipalName.equals(currentPrincipalName)) {
throw new WebSocketServerException(
"New SecurityIdentity principal name '%s' is different than previous principal name '%s'. SecurityIdentity update is aborted"
.formatted(currentPrincipalName, previousPrincipalName));
}
onClose(); // cancel previous timer that closes connection when identity expired
this.identity = updatedIdentity;
// this shouldn't be necessary (and probably isn't) but updating ctx it just to stay on the safe side
QuarkusHttpUser.setUser(this.routingContext, new QuarkusHttpUser(updatedIdentity));
this.onClose = closeConnectionWhenIdentityExpired(routingContext, connection, updatedIdentity);
if (connection.isClosed()) {
// it could be that while we were updating identity, connection has been closed
// in that case, cancel timer we created few lines above (done this way to avoid race)
onClose();
}
}
private static Runnable closeConnectionWhenIdentityExpired(RoutingContext routingContext,
WebSocketConnectionImpl connection, SecurityIdentity identity) {
if (identity != null && identity.getAttribute(QUARKUS_IDENTITY_EXPIRE_TIME) instanceof Long expireAt) {View on GitHub (pinned to e1c734241f)
Solutions
- Ensure re-authentication uses credentials for the same user that opened the connection
- Align principal naming (quarkus.oidc token principal claim, custom IdentityProvider mapping) so the name is stable across refreshes
- If a different user must take over, close the old connection and open a new one
- Check for extension version changes that altered the default principal claim
Example fix
// before // refresh path used preferred_username at login but 'sub' at refresh // after // configure a consistent principal claim, e.g. quarkus.oidc.token.principal-claim=upn SecurityIdentity refreshed = auth.refresh(token); support.updateSecurityIdentity(refreshed, connection); // same principal name required
Defensive patterns
Strategy: try-catch
Validate before calling
// before updating identity on a live connection
if (!currentIdentity.getPrincipal().getName()
.equals(newIdentity.getPrincipal().getName())) {
// abort update; close and reconnect as the new user instead
connection.close();
} Try / catch
try {
support.updateSecurityIdentity(refreshed, connection);
} catch (WebSocketServerException e) {
if (e.getMessage().contains("different than previous principal name")) {
connection.close(); // reopen a fresh connection for the new principal
}
} Prevention
- Keep the principal claim (e.g. upn) stable between login and token refresh
- Never reuse a connection across different users; open a new connection instead
When it happens
Trigger: Re-authentication/refresh path produces an identity whose principal name differs from the current one — e.g. swapping tokens for a different user, a custom IdentityProvider changing the principal naming scheme between refreshes, or an OIDC token whose `sub`/`upn` changed.
Common situations: Testing with tokens of different users on one connection; principal-name mapping (upn vs preferred_username) changed after a config or extension-version update; connection reused across login switches.
Related errors
- Only SecurityIdentity attached to a WebSocket server connect
- Principal is null but anonymous status is false
- Updated SecurityIdentity is anonymous
- The '%s' policies required by the '%s' annotation instances
- The '%s' CDI bean injection point was detected, but there is
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b2ef4dba86ca0929.
Report an issue: GitHub.