2016-02-03 10:20:22 +00:00
/ *
* Copyright 2016 Red Hat , Inc . and / or its affiliates
* and other contributors as indicated by the @author tags .
*
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2015-07-17 11:45:43 +00:00
package org.keycloak.authentication ;
2016-10-17 13:19:54 +00:00
import org.jboss.logging.Logger ;
2018-03-28 20:45:52 +00:00
import org.keycloak.OAuth2Constants ;
2015-07-17 11:45:43 +00:00
import org.keycloak.models.AuthenticationExecutionModel ;
import org.keycloak.models.AuthenticationFlowModel ;
import org.keycloak.models.UserModel ;
2016-01-18 17:18:15 +00:00
import org.keycloak.services.ServicesLogger ;
2017-04-21 12:24:47 +00:00
import org.keycloak.sessions.AuthenticationSessionModel ;
2015-10-14 15:49:36 +00:00
2015-07-17 11:45:43 +00:00
import javax.ws.rs.core.Response ;
import java.util.Iterator ;
import java.util.List ;
/ * *
2016-01-13 21:18:24 +00:00
* @author < a href = " mailto:bill@burkecentral.com " > Bill Burke < / a >
* @version $Revision : 1 $
* /
2015-07-17 11:45:43 +00:00
public class DefaultAuthenticationFlow implements AuthenticationFlow {
2016-10-17 13:19:54 +00:00
private static final Logger logger = Logger . getLogger ( DefaultAuthenticationFlow . class ) ;
2015-07-17 11:45:43 +00:00
Response alternativeChallenge = null ;
AuthenticationExecutionModel challengedAlternativeExecution = null ;
boolean alternativeSuccessful = false ;
List < AuthenticationExecutionModel > executions ;
Iterator < AuthenticationExecutionModel > executionIterator ;
AuthenticationProcessor processor ;
AuthenticationFlowModel flow ;
public DefaultAuthenticationFlow ( AuthenticationProcessor processor , AuthenticationFlowModel flow ) {
this . processor = processor ;
this . flow = flow ;
this . executions = processor . getRealm ( ) . getAuthenticationExecutions ( flow . getId ( ) ) ;
this . executionIterator = executions . iterator ( ) ;
}
protected boolean isProcessed ( AuthenticationExecutionModel model ) {
if ( model . isDisabled ( ) ) return true ;
2017-04-21 12:24:47 +00:00
AuthenticationSessionModel . ExecutionStatus status = processor . getAuthenticationSession ( ) . getExecutionStatus ( ) . get ( model . getId ( ) ) ;
2015-07-17 11:45:43 +00:00
if ( status = = null ) return false ;
2017-04-21 12:24:47 +00:00
return status = = AuthenticationSessionModel . ExecutionStatus . SUCCESS | | status = = AuthenticationSessionModel . ExecutionStatus . SKIPPED
| | status = = AuthenticationSessionModel . ExecutionStatus . ATTEMPTED
| | status = = AuthenticationSessionModel . ExecutionStatus . SETUP_REQUIRED ;
2015-07-17 11:45:43 +00:00
}
2018-03-28 20:45:52 +00:00
protected Authenticator createAuthenticator ( AuthenticatorFactory factory ) {
2018-03-31 14:16:44 +00:00
String display = processor . getAuthenticationSession ( ) . getAuthNote ( OAuth2Constants . DISPLAY ) ;
2018-03-28 20:45:52 +00:00
if ( display = = null ) return factory . create ( processor . getSession ( ) ) ;
if ( factory instanceof DisplayTypeAuthenticatorFactory ) {
Authenticator authenticator = ( ( DisplayTypeAuthenticatorFactory ) factory ) . createDisplay ( processor . getSession ( ) , display ) ;
if ( authenticator ! = null ) return authenticator ;
}
// todo create a provider for handling lack of display support
if ( OAuth2Constants . DISPLAY_CONSOLE . equalsIgnoreCase ( display ) ) {
2018-03-31 14:16:44 +00:00
processor . getAuthenticationSession ( ) . removeAuthNote ( OAuth2Constants . DISPLAY ) ;
2018-03-29 21:14:36 +00:00
throw new AuthenticationFlowException ( AuthenticationFlowError . DISPLAY_NOT_SUPPORTED ,
ConsoleDisplayMode . browserContinue ( processor . getSession ( ) , processor . getRefreshUrl ( true ) . toString ( ) ) ) ;
2018-03-28 20:45:52 +00:00
} else {
return factory . create ( processor . getSession ( ) ) ;
}
}
2015-07-17 11:45:43 +00:00
@Override
public Response processAction ( String actionExecution ) {
2015-12-16 17:23:41 +00:00
logger . debugv ( " processAction: {0} " , actionExecution ) ;
2015-07-17 11:45:43 +00:00
while ( executionIterator . hasNext ( ) ) {
AuthenticationExecutionModel model = executionIterator . next ( ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " check: {0} requirement: {1} " , model . getAuthenticator ( ) , model . getRequirement ( ) . toString ( ) ) ;
2015-07-17 11:45:43 +00:00
if ( isProcessed ( model ) ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " execution is processed " ) ;
2015-07-17 11:45:43 +00:00
if ( ! alternativeSuccessful & & model . isAlternative ( ) & & processor . isSuccessful ( model ) )
alternativeSuccessful = true ;
continue ;
}
2015-08-02 23:03:33 +00:00
if ( model . isAuthenticatorFlow ( ) ) {
2015-07-17 11:45:43 +00:00
AuthenticationFlow authenticationFlow = processor . createFlowExecution ( model . getFlowId ( ) , model ) ;
2016-12-10 16:59:29 +00:00
Response flowChallenge = authenticationFlow . processAction ( actionExecution ) ;
if ( flowChallenge = = null ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SUCCESS ) ;
2016-12-10 16:59:29 +00:00
if ( model . isAlternative ( ) ) alternativeSuccessful = true ;
return processFlow ( ) ;
} else {
return flowChallenge ;
}
2015-07-17 11:45:43 +00:00
} else if ( model . getId ( ) . equals ( actionExecution ) ) {
AuthenticatorFactory factory = ( AuthenticatorFactory ) processor . getSession ( ) . getKeycloakSessionFactory ( ) . getProviderFactory ( Authenticator . class , model . getAuthenticator ( ) ) ;
2015-08-14 16:03:37 +00:00
if ( factory = = null ) {
throw new RuntimeException ( " Unable to find factory for AuthenticatorFactory: " + model . getAuthenticator ( ) + " did you forget to declare it in a META-INF/services file? " ) ;
}
2018-03-28 20:45:52 +00:00
Authenticator authenticator = createAuthenticator ( factory ) ;
2015-08-09 19:06:24 +00:00
AuthenticationProcessor . Result result = processor . createAuthenticatorContext ( model , authenticator , executions ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " action: {0} " , model . getAuthenticator ( ) ) ;
2015-07-17 11:45:43 +00:00
authenticator . action ( result ) ;
2017-03-27 09:23:25 +00:00
Response response = processResult ( result , true ) ;
2015-10-15 20:30:21 +00:00
if ( response = = null ) {
2017-03-27 09:23:25 +00:00
processor . getAuthenticationSession ( ) . removeAuthNote ( AuthenticationProcessor . CURRENT_AUTHENTICATION_EXECUTION ) ;
2016-01-13 21:18:24 +00:00
return processFlow ( ) ;
} else return response ;
2015-07-17 11:45:43 +00:00
}
}
2015-08-09 19:06:24 +00:00
throw new AuthenticationFlowException ( " action is not in current execution " , AuthenticationFlowError . INTERNAL_ERROR ) ;
2015-07-17 11:45:43 +00:00
}
@Override
public Response processFlow ( ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " processFlow " ) ;
2015-07-17 11:45:43 +00:00
while ( executionIterator . hasNext ( ) ) {
AuthenticationExecutionModel model = executionIterator . next ( ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " check execution: {0} requirement: {1} " , model . getAuthenticator ( ) , model . getRequirement ( ) . toString ( ) ) ;
2015-07-17 11:45:43 +00:00
if ( isProcessed ( model ) ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " execution is processed " ) ;
2015-07-17 11:45:43 +00:00
if ( ! alternativeSuccessful & & model . isAlternative ( ) & & processor . isSuccessful ( model ) )
alternativeSuccessful = true ;
continue ;
}
if ( model . isAlternative ( ) & & alternativeSuccessful ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " Skip alternative execution " ) ;
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SKIPPED ) ;
2015-07-17 11:45:43 +00:00
continue ;
}
2015-08-02 23:03:33 +00:00
if ( model . isAuthenticatorFlow ( ) ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " execution is flow " ) ;
2015-07-17 11:45:43 +00:00
AuthenticationFlow authenticationFlow = processor . createFlowExecution ( model . getFlowId ( ) , model ) ;
2017-06-22 20:23:20 +00:00
Response flowChallenge = null ;
try {
flowChallenge = authenticationFlow . processFlow ( ) ;
} catch ( AuthenticationFlowException afe ) {
if ( model . isAlternative ( ) ) {
logger . debug ( " Thrown exception in alternative Subflow. Ignoring Subflow " ) ;
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . ATTEMPTED ) ;
continue ;
} else {
throw afe ;
}
}
2015-07-17 11:45:43 +00:00
if ( flowChallenge = = null ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SUCCESS ) ;
2015-07-17 11:45:43 +00:00
if ( model . isAlternative ( ) ) alternativeSuccessful = true ;
continue ;
} else {
if ( model . isAlternative ( ) ) {
alternativeChallenge = flowChallenge ;
challengedAlternativeExecution = model ;
} else if ( model . isRequired ( ) ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . CHALLENGED ) ;
2015-07-17 11:45:43 +00:00
return flowChallenge ;
} else if ( model . isOptional ( ) ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SKIPPED ) ;
2015-07-17 11:45:43 +00:00
continue ;
} else {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SKIPPED ) ;
2015-07-17 11:45:43 +00:00
continue ;
}
return flowChallenge ;
}
}
AuthenticatorFactory factory = ( AuthenticatorFactory ) processor . getSession ( ) . getKeycloakSessionFactory ( ) . getProviderFactory ( Authenticator . class , model . getAuthenticator ( ) ) ;
2015-07-22 00:56:05 +00:00
if ( factory = = null ) {
2015-08-14 16:03:37 +00:00
throw new RuntimeException ( " Unable to find factory for AuthenticatorFactory: " + model . getAuthenticator ( ) + " did you forget to declare it in a META-INF/services file? " ) ;
2015-07-22 00:56:05 +00:00
}
2018-03-28 20:45:52 +00:00
Authenticator authenticator = createAuthenticator ( factory ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator: {0} " , factory . getId ( ) ) ;
2017-03-27 09:23:25 +00:00
UserModel authUser = processor . getAuthenticationSession ( ) . getAuthenticatedUser ( ) ;
2015-07-17 11:45:43 +00:00
if ( authenticator . requiresUser ( ) & & authUser = = null ) {
if ( alternativeChallenge ! = null ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( challengedAlternativeExecution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . CHALLENGED ) ;
2015-07-17 11:45:43 +00:00
return alternativeChallenge ;
}
2015-08-09 19:06:24 +00:00
throw new AuthenticationFlowException ( " authenticator: " + factory . getId ( ) , AuthenticationFlowError . UNKNOWN_USER ) ;
2015-07-17 11:45:43 +00:00
}
boolean configuredFor = false ;
if ( authenticator . requiresUser ( ) & & authUser ! = null ) {
configuredFor = authenticator . configuredFor ( processor . getSession ( ) , processor . getRealm ( ) , authUser ) ;
if ( ! configuredFor ) {
if ( model . isRequired ( ) ) {
2015-08-03 13:52:56 +00:00
if ( factory . isUserSetupAllowed ( ) ) {
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator SETUP_REQUIRED: {0} " , factory . getId ( ) ) ;
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SETUP_REQUIRED ) ;
2017-03-27 09:23:25 +00:00
authenticator . setRequiredActions ( processor . getSession ( ) , processor . getRealm ( ) , processor . getAuthenticationSession ( ) . getAuthenticatedUser ( ) ) ;
2015-07-17 11:45:43 +00:00
continue ;
} else {
2015-08-09 19:06:24 +00:00
throw new AuthenticationFlowException ( AuthenticationFlowError . CREDENTIAL_SETUP_REQUIRED ) ;
2015-07-17 11:45:43 +00:00
}
} else if ( model . isOptional ( ) ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( model . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SKIPPED ) ;
2015-07-17 11:45:43 +00:00
continue ;
}
}
}
2016-01-22 23:39:55 +00:00
// skip if action as successful already
2017-03-27 20:41:36 +00:00
// Response redirect = processor.checkWasSuccessfulBrowserAction();
// if (redirect != null) return redirect;
2016-01-22 23:39:55 +00:00
2015-08-09 19:06:24 +00:00
AuthenticationProcessor . Result context = processor . createAuthenticatorContext ( model , authenticator , executions ) ;
2017-06-22 20:23:20 +00:00
logger . debugv ( " invoke authenticator.authenticate: {0} " , factory . getId ( ) ) ;
2015-07-17 11:45:43 +00:00
authenticator . authenticate ( context ) ;
2017-03-27 09:23:25 +00:00
Response response = processResult ( context , false ) ;
2015-07-17 11:45:43 +00:00
if ( response ! = null ) return response ;
}
return null ;
}
2017-03-27 09:23:25 +00:00
public Response processResult ( AuthenticationProcessor . Result result , boolean isAction ) {
2015-07-17 11:45:43 +00:00
AuthenticationExecutionModel execution = result . getExecution ( ) ;
2015-08-11 17:04:40 +00:00
FlowStatus status = result . getStatus ( ) ;
2015-10-14 15:49:36 +00:00
switch ( status ) {
case SUCCESS :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator SUCCESS: {0} " , execution . getAuthenticator ( ) ) ;
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SUCCESS ) ;
2015-10-14 15:49:36 +00:00
if ( execution . isAlternative ( ) ) alternativeSuccessful = true ;
return null ;
case FAILED :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator FAILED: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
processor . logFailure ( ) ;
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . FAILED ) ;
2015-10-14 15:49:36 +00:00
if ( result . getChallenge ( ) ! = null ) {
return sendChallenge ( result , execution ) ;
}
throw new AuthenticationFlowException ( result . getError ( ) ) ;
case FORK :
2015-12-16 17:23:41 +00:00
logger . debugv ( " reset browser login from authenticator: {0} " , execution . getAuthenticator ( ) ) ;
2017-03-27 09:23:25 +00:00
processor . getAuthenticationSession ( ) . setAuthNote ( AuthenticationProcessor . CURRENT_AUTHENTICATION_EXECUTION , execution . getId ( ) ) ;
2015-10-14 15:49:36 +00:00
throw new ForkFlowException ( result . getSuccessMessage ( ) , result . getErrorMessage ( ) ) ;
case FORCE_CHALLENGE :
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . CHALLENGED ) ;
2015-07-17 11:45:43 +00:00
return sendChallenge ( result , execution ) ;
2015-10-14 15:49:36 +00:00
case CHALLENGE :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator CHALLENGE: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
if ( execution . isRequired ( ) ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . CHALLENGED ) ;
2015-10-14 15:49:36 +00:00
return sendChallenge ( result , execution ) ;
}
2017-03-27 09:23:25 +00:00
UserModel authenticatedUser = processor . getAuthenticationSession ( ) . getAuthenticatedUser ( ) ;
2015-10-14 15:49:36 +00:00
if ( execution . isOptional ( ) & & authenticatedUser ! = null & & result . getAuthenticator ( ) . configuredFor ( processor . getSession ( ) , processor . getRealm ( ) , authenticatedUser ) ) {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . CHALLENGED ) ;
2015-10-14 15:49:36 +00:00
return sendChallenge ( result , execution ) ;
}
if ( execution . isAlternative ( ) ) {
alternativeChallenge = result . getChallenge ( ) ;
challengedAlternativeExecution = execution ;
} else {
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . SKIPPED ) ;
2015-10-14 15:49:36 +00:00
}
return null ;
case FAILURE_CHALLENGE :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator FAILURE_CHALLENGE: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
processor . logFailure ( ) ;
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . CHALLENGED ) ;
2015-07-17 11:45:43 +00:00
return sendChallenge ( result , execution ) ;
2015-10-14 15:49:36 +00:00
case ATTEMPTED :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator ATTEMPTED: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
if ( execution . getRequirement ( ) = = AuthenticationExecutionModel . Requirement . REQUIRED ) {
throw new AuthenticationFlowException ( AuthenticationFlowError . INVALID_CREDENTIALS ) ;
}
2017-04-21 12:24:47 +00:00
processor . getAuthenticationSession ( ) . setExecutionStatus ( execution . getId ( ) , AuthenticationSessionModel . ExecutionStatus . ATTEMPTED ) ;
2015-10-14 15:49:36 +00:00
return null ;
case FLOW_RESET :
2017-03-27 20:41:36 +00:00
processor . resetFlow ( ) ;
2015-10-14 15:49:36 +00:00
return processor . authenticate ( ) ;
default :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator INTERNAL_ERROR: {0} " , execution . getAuthenticator ( ) ) ;
2016-10-17 13:19:54 +00:00
ServicesLogger . LOGGER . unknownResultStatus ( ) ;
2015-10-14 15:49:36 +00:00
throw new AuthenticationFlowException ( AuthenticationFlowError . INTERNAL_ERROR ) ;
2015-07-17 11:45:43 +00:00
}
}
2015-08-09 19:06:24 +00:00
public Response sendChallenge ( AuthenticationProcessor . Result result , AuthenticationExecutionModel execution ) {
2017-03-27 09:23:25 +00:00
processor . getAuthenticationSession ( ) . setAuthNote ( AuthenticationProcessor . CURRENT_AUTHENTICATION_EXECUTION , execution . getId ( ) ) ;
2015-07-17 11:45:43 +00:00
return result . getChallenge ( ) ;
}
}