Skip to content

Fix SLO sending stale transient NameID when SP re-authenticates within same IdP session#984

Open
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-stale-nameid-on-slo
Open

Fix SLO sending stale transient NameID when SP re-authenticates within same IdP session#984
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-stale-nameid-on-slo

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

When an SP completes ACS more than once within the same IdP session (e.g. SP local session timeout while IdP session remains active), the nameIDandSPpairs cache in IDPSession retained the original NameID. SLO then sent a LogoutRequest with that stale NameID, causing a mismatch on the SP side.

Change

IDPSSOUtil.getAssertion() — replace the existing NameIDandSPpair for a given SP instead of silently skipping the update:

// Before: skips update if SP already present
boolean found = false;
for (NameIDandSPpair nameIDandSPpair : list) {
    if (nameIDandSPpair.getSPEntityID().equals(id)) {
        found = true;
        break;
    }
}
if (!found) {
    list.add(pair);
}

// After: replaces existing entry with latest NameID
boolean found = false;
for (int i = 0; i < list.size(); i++) {
    if (list.get(i).getSPEntityID().equals(id)) {
        found = true;
        list.set(i, pair);  // update to latest NameID
        break;
    }
}
if (!found) {
    list.add(pair);
}

This ensures SLO always uses the NameID that was last issued to the SP, keeping the IdP cache in sync with the SP's active session.

Original prompt

Bug Description

When the same SP completes ACS (Assertion Consumer Service) multiple times within the same IdP session (e.g., because the SP's local session timed out but the IdP session is still alive), the IdP sends a LogoutRequest during SLO with a stale/earlier transient NameID instead of the most recent one. This causes SLO to fail on the SP side because the NameID in the LogoutRequest doesn't match the SP's current session.

Root Cause

The bug is in IDPSSOUtil.java in the getAssertion() method, around lines 1025–1043. When registering the (NameID, SPEntityID) pair in the IDP session cache for later SLO use, the code checks if a pair for the SP already exists. If found == true, the existing pair is not updated with the new NameID — it is simply skipped:

NameIDandSPpair pair = new NameIDandSPpair(subject.getNameID(), spEntityID);

synchronized (IDPCache.idpSessionsByIndices) {
    List<NameIDandSPpair> list = idpSession.getNameIDandSPpairs();
    String id;
    if (authnReq != null) {
        id = authnReq.getIssuer().getValue();
    } else {
        id = spEntityID;
    }
    boolean found = false;
    for (NameIDandSPpair nameIDandSPpair : list) {
        if (nameIDandSPpair.getSPEntityID().equals(id)) {
            found = true;
            break;
        }
    }
    if (!found) {
        list.add(pair);
    }
}

When a second ACS happens for the same SP (with a new transient NameID), the old NameID remains in the cache. Later, during SLO, the IdP retrieves this stale NameID from the nameIDandSPpairs list and sends it in the LogoutRequest, causing a mismatch on the SP side.

Required Fix

In openam-federation/openam-federation-library/src/main/java/com/sun/identity/saml2/profile/IDPSSOUtil.java, change the logic so that when an existing NameIDandSPpair is found for the same SP entity ID, the pair is updated/replaced with the new NameID rather than being skipped.

The fix should change the block around lines 1025-1043 from:

if (!found) {
    list.add(pair);
}

to something like:

if (!found) {
    list.add(pair);
} else {
    // Update the NameID for the existing SP to ensure SLO uses the latest NameID
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).getSPEntityID().equals(id)) {
            list.set(i, pair);
            break;
        }
    }
}

This ensures that when a repeated ACS occurs for the same SP within the same IdP session, the NameID stored in the IDP session cache is always updated to match the latest assertion sent to the SP. This way, SLO will use the correct (most recent) NameID.

References

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: проанализируй

Assistant:

I'll analyze this issue by investigating how OpenAM handles SAML SLO NameID storage when the same SP completes ACS multiple times within the same IdP session.

Assistant: Now let me look at the critical code — how NameID+SP pairs are added to the IDP session, particularly in the sendResponseToACS flow:

Assistant: Now I have a thorough understanding of the code. Let me also look at the DefaultIDPAccountMapper.getNameID() method more closely to understand the transient NameID lookup:

Assistant: Now let me look at the key method sendResponseToACS in IDPSSOUtil.java to see how NameID is stored in the nameIDandSPpairs list:

Assistant: Now I can see the critical piece. Let me get the full context of this code to see exactly what happens when the SP already exists in the list.

Assistant: ## Анализ проблемы: SLO использует устаревший transient NameID при повторном ACS к тому же SP

После анализа исходного кода OpenAM, я могу подтвердить, что описанное поведение является багом в текущей реализации. Вот детальный разбор корневой причины.


Корневая причина

Проблема находится в файле IDPSSOUtil.java, в логике, которая регистрирует пару (NameID, SPEntityID) в кэше IDP-сессии при каждом SSO-ответе:

NameIDandSPpair pair = new NameIDandSPpair(subject.getNameID(), spEntityID);

synchronized (IDPCache.idpSessionsByIndices) {
    List<NameIDandSPpair> list = idpSession.getNameIDandSPpairs();
    String id;
    if (authnReq != null) {
        id = authnReq.getIssuer().getValue();
    } else {
        id = spEntityID;
    }
    boolean found = false;
    for (NameIDandSPpair nameIDandSPpair : list) {
        if (nameIDandSPpair.getSPEntityID().equals(id)) {
            found = true;
            break;
        }
    }
 ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

…tes in same IdP session

Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenAM/sessions/9cf5d8fe-55a0-436f-99d6-674c58e05b0e

Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix stale NameID issue during SLO Fix SLO sending stale transient NameID when SP re-authenticates within same IdP session Apr 9, 2026
Copilot AI requested a review from vharseko April 9, 2026 12:16
@vharseko vharseko requested a review from maximthomas April 9, 2026 12:22
@vharseko vharseko marked this pull request as ready for review April 9, 2026 12:22
@vharseko vharseko removed their request for review April 10, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants