Skip to content

NullPointerException in ApiServlet.skip2FAcheckForUser for SAML SSO sessions when 2FA is not enabled #13815

Description

@mogamal1

problem

ApiServlet.skip2FAcheckForUser(HttpSession) unboxes the IS_2FA_VERIFIED session attribute without a null check (ApiServlet.java:512):

boolean is2FAverified = (boolean) session.getAttribute(ApiConstants.IS_2FA_VERIFIED);

For sessions established via SAML SSO, this attribute is never set on the HttpSession. getAttribute returns null, unboxing to boolean throws a NullPointerException, and the API response write aborts with an empty 200 body. The UI then fails at permission.js (GenerateRoutes) and renders a blank page. This happens even though 2FA is not enabled anywhere in the environment.

Server-side error:

ERROR [c.c.a.ApiServlet] unknown exception writing api response
java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()"
because the return value of "javax.servlet.http.HttpSession.getAttribute(String)" is null
at com.cloud.api.ApiServlet.skip2FAcheckForUser(ApiServlet.java:512)
at com.cloud.api.ApiServlet.processRequestInContext(ApiServlet.java:362)
at com.cloud.api.ApiServlet$1.run(ApiServlet.java:194)

versions

CloudStack: 4.22.1.0
Config: saml2.enabled=true; two-factor authentication NOT enabled (no enable.2fa configuration present)
IdP: Microsoft Entra ID (SAML 2.0)
OS: EL8.10
DB: MariaDB 10.5
Management server behind Apache httpd reverse proxy (443 -> 8443)

The steps to reproduce the bug

  1. Configure SAML SSO (saml2.enabled=true), with 2FA NOT enabled.
  2. Log in as a SAML user via SSO.
  3. Continue using the session until an API call reaches skip2FAcheckForUser (e.g. listUsers during UI bootstrap on a session that has aged).
  4. The API returns an empty 200 body; the UI renders a blank page.

Expected: SAML sessions without 2FA proceed normally and the API returns a valid response.
Actual: NullPointerException at ApiServlet.java:512, empty response, blank UI.

What to do about it?

Suggested fix:

  • Null-safe read at ApiServlet.java:512, e.g.:
    boolean is2FAverified = Boolean.TRUE.equals(session.getAttribute(ApiConstants.IS_2FA_VERIFIED));
  • Additionally, set IS_2FA_VERIFIED on the session in the SAML login path (SAML2LoginAPIAuthenticatorCmd) as the standard username/password login path does, so SAML sessions carry the attribute.

Workaround for operators:

  • Delete the JSESSIONID cookie and re-authenticate via SSO to establish a fresh session (confirmed working).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions