{"record":{"id":"b63779b83be3b599","repo":"apereo/cas","slug":"unable-to-extract-credentials-for-multifactor-auth","errorCode":null,"errorMessage":"Unable to extract credentials for multifactor authentication","messagePattern":"Unable to extract credentials for multifactor authentication","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"core/cas-server-core-rest-api/src/main/java/org/apereo/cas/rest/authentication/DefaultRestAuthenticationService.java","lineNumber":76,"sourceCode":"        }\n        val service = serviceFactory.createService(request);\n        val registeredService = servicesManager.findServiceBy(service);\n        val authResult = Optional.ofNullable(\n            authenticationSystemSupport.handleInitialAuthenticationTransaction(service, credentials.toArray(Credential[]::new)));\n\n        return authResult\n            .map(result -> result.getInitialAuthentication()\n                .filter(Unchecked.predicate(authn -> restAuthenticationPolicy.isSatisfiedBy(authn, applicationContext).isSuccess()))\n                .filter(Unchecked.predicate(authn -> {\n                    val validationResult = requestedContextValidator.validateAuthenticationContext(request, response, registeredService, authn, service);\n                    return !validationResult.isSuccess();\n                }))\n                .map(Unchecked.function(authn -> multifactorTriggerSelectionStrategy.resolve(request, response, registeredService, authn, service)\n                    .map(Unchecked.function(provider -> {\n                        LOGGER.debug(\"Extracting credentials for multifactor authentication via [{}]\", provider);\n                        val authnCredentials = credentialFactory.fromAuthentication(request, requestBody, authn, provider);\n                        if (authnCredentials == null || authnCredentials.isEmpty()) {\n                            throw new AuthenticationException(\"Unable to extract credentials for multifactor authentication\");\n                        }\n                        return authenticationSystemSupport.finalizeAuthenticationTransaction(service, authnCredentials);\n                    }))\n                    .orElseGet(Unchecked.supplier(() -> authenticationSystemSupport.finalizeAllAuthenticationTransactions(result, service)))))\n                .orElseGet(Unchecked.supplier(() -> authenticationSystemSupport.finalizeAllAuthenticationTransactions(result, service))));\n    }\n}\n","sourceCodeStart":58,"sourceCodeEnd":84,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-rest-api/src/main/java/org/apereo/cas/rest/authentication/DefaultRestAuthenticationService.java#L58-L84","documentation":"During REST authentication, CAS resolved a multifactor trigger (a provider was selected by the MultifactorAuthenticationTriggerSelectionStrategy) but the credential factory could not extract the second-factor credentials from the request via fromAuthentication(...). Without MFA credentials the finalizeAuthenticationTransaction cannot proceed, so AuthenticationException is thrown.","triggerScenarios":"The request hits a service/flow that triggers an MFA provider (per service policy, principal attribute, request parameter like authnMethod/mfaProvider, etc.), yet the request body contains no credentials the provider's extractor recognizes — e.g. missing OTP/one-time-token field, wrong parameter name, or extractor module for that provider not registered in the REST credential factory.","commonSituations":"Client integrates the REST API and does not send the second-factor value (OTP) alongside the first-factor credentials; MFA provider forced globally by config but REST payloads never include OTP; provider changed (e.g. moving from webauthn to mfa-simple) so the expected request field changed; missing cas-server-support rest module for the specific MFA provider.","solutions":["Include the MFA provider's expected credential in the request body (e.g. otp / token / webauthn assertion parameter) alongside username/password.","Confirm the MFA provider's REST credential extractor module is a dependency and registered with the REST credential factory.","Check the multifactorTriggerSelectionStrategy configuration — if MFA should not be forced for this request, adjust the trigger strategy or service MFA policy.","Verify the parameter names against the provider's documentation; provider versions rename fields.","Test with debug logging on MultifactorAuthenticationTriggerSelectionStrategy and the credential factory to see which provider was selected and what it looked for."],"exampleFix":"// before: MFA triggered but no OTP supplied\ncurl -X POST https://cas/cas/v1/tickets -d 'username=u&password=p'\n// after: supply the second factor expected by the provider\ncurl -X POST https://cas/cas/v1/tickets -d 'username=u&password=p&otp=123456'","handlingStrategy":"validation","validationCode":"// ensure the MFA credential field the selected provider expects is present\nif (body == null || !body.containsKey(\"otp\")) { // adjust to provider field\n    // add second-factor credential before calling authenticate\n}","typeGuard":null,"tryCatchPattern":"try {\n    Optional<AuthenticationResult> r = restAuthenticationService.authenticate(body, req, res);\n} catch (AuthenticationException e) {\n    // prompt user for second factor and retry with MFA credential\n}","preventionTips":["Know which MFA providers are forced for your services and what request fields they need.","Ship the matching provider's REST support module so its extractor is registered.","Include the second-factor value in every REST auth request when MFA is enforced.","Align trigger strategy config with what REST clients can actually supply."],"tags":["rest","mfa","authentication","credentials"],"backgroundTag":"missing-mfa-credentials","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}