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

  1. Ensure re-authentication uses credentials for the same user that opened the connection
  2. Align principal naming (quarkus.oidc token principal claim, custom IdentityProvider mapping) so the name is stable across refreshes
  3. If a different user must take over, close the old connection and open a new one
  4. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/b2ef4dba86ca0929. Report an issue: GitHub.