private Authentication buildAuthentication(final PrincipalElectionStrategy principalElectionStrategy) throws Throwable {
        if (isEmpty()) {
            LOGGER.warn("No authentication event has been recorded; CAS cannot finalize the authentication result");
            return null;
        }
        val authenticationAttributes = new HashMap<String, List<Object>>();
        val principalAttributes = new HashMap<String, List<Object>>();
        val authenticationBuilder = DefaultAuthenticationBuilder.newInstance();

        buildAuthenticationHistory(this.authentications, authenticationAttributes,
            principalAttributes, authenticationBuilder, principalElectionStrategy);

        synchronized (this.authentications) {
            val primaryPrincipal = getPrimaryPrincipal(principalElectionStrategy, this.authentications, principalAttributes);
            authenticationBuilder.setPrincipal(primaryPrincipal);
        }
        LOGGER.debug("Determined primary authentication principal to be [{}]", authenticationBuilder.getPrincipal());

        authenticationBuilder.setAttributes(authenticationAttributes);
        LOGGER.trace("Collected authentication attributes for this result are [{}]", authenticationAttributes);

        authenticationBuilder.setAuthenticationDate(ZonedDateTime.now(ZoneOffset.UTC));

        val auth = authenticationBuilder.build();
        LOGGER.trace("Authentication result commenced at [{}]", auth.getAuthenticationDate());
        return auth;
    }
Principal id is and must be enforced to be the same for all authentications.
Based on that restriction, it's safe to grab the first principal id in the chain
when composing the authentication chain for the caller.
core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/DefaultAuthenticationResultBuilder.java