apereo/cas · error · FailedLoginException

Invalid credentials:

Error message

Invalid credentials: 

What it means

Catch-all in AzureActiveDirectoryAuthenticationHandler.authenticateUsernamePasswordInternal: any exception during the Azure AD username/password flow (token acquisition via getAccessTokenFromUserCredentials, Graph user-info fetch, or principal attribute mapping) is logged and rethrown as FailedLoginException("Invalid credentials: " + e.getMessage()). The original exception's message is preserved but its type is flattened to a failed login.

Solutions

  1. Read the nested cause logged by LoggingUtils.error (the message appended after 'Invalid credentials:') to identify the real failure — e.g. invalid_grant vs. connection timeout.
  2. If invalid_grant/MFA required: the ROPC credential flow cannot proceed for that user — have the user authenticate interactively or exempt them from conditional access, since this handler only supports direct username/password.
  3. Validate cas.authn.azuread[0] client-id, client-secret, tenant and that the secret has not expired in the Azure portal; rotate and retry.
  4. Verify the supplied username/password is correct and the account is not locked/expired in Azure AD.
  5. Check network/DNS/proxy reachability to login.microsoftonline.com and graph.microsoft.com from the CAS server.

Example fix

// before: users with conditional-access MFA always fail
// Invalid credentials: invalid_grant - MFA required

// after: exclude these users from conditional access or use an
// interactive OAuth flow instead of the ROPC-based handler
Defensive patterns

Strategy: try-catch

Validate before calling

// validate config before authenticating
if (StringUtils.isAnyBlank(properties.getClientId(), properties.getClientSecret(), properties.getTenant())) {
    throw new IllegalArgumentException("azuread client-id/secret/tenant must be set");
}

Try / catch

try {
    return handler.authenticateUsernamePasswordInternal(credential);
} catch (FailedLoginException e) {
    // 'Invalid credentials: <cause message>' — inspect the appended cause
    LOGGER.error("Azure AD auth failed, root cause: {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: Any Exception thrown inside authenticateUsernamePasswordInternal — MSAL token acquisition failures (invalid_grant, wrong client secret, network errors), the Graph HTTP failures from getUserInfoFromGraph, JSON/attribute parsing errors, or NPEs from unexpected responses.

Common situations: Users whose accounts require MFA/conditional access (ROPC unsupported → invalid_grant); expired Azure AD client secret; wrong tenant/client-id; password wrong or expired in Azure AD; transient network failure to login.microsoftonline.com.

Understand the failure class

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/9dfe071a5a56e738. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-azuread-authentication/src/main/java/org/apereo/cas/azure/ad/authentication/AzureActiveDirectoryAuthenticationHandler.java:126

            LOGGER.trace("Fetching token for [{}]", username);
            val result = getAccessTokenFromUserCredentials(username, credential.toPassword());
            LOGGER.debug("Retrieved token [{}] for [{}]", result.accessToken(), username);
            val userInfo = getUserInfoFromGraph(result, username);
            LOGGER.trace("Retrieved user info [{}]", userInfo);
            val userInfoMap = (Map<String, ?>) MAPPER.readValue(JsonValue.readHjson(userInfo).toString(), Map.class);
            val attributeMap = new HashMap<String, List<Object>>(userInfoMap.size());
            userInfoMap.forEach((key, value) -> {
                val values = CollectionUtils.toCollection(value, ArrayList.class);
                if (!values.isEmpty()) {
                    attributeMap.put(key, values);
                }
            });
            val principal = principalFactory.createPrincipal(username, attributeMap);
            LOGGER.debug("Created principal for id [{}] and [{}] attributes", username, attributeMap);
            return createHandlerResult(credential, principal, new ArrayList<>());
        } catch (final Exception e) {
            LoggingUtils.error(LOGGER, e);
            throw new FailedLoginException("Invalid credentials: " + e.getMessage());
        }
    }
}

View on GitHub (pinned to e7288fc434)