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-08-12 16:52:13 +00:00
package org.keycloak.authentication ;
import org.keycloak.events.Details ;
import org.keycloak.events.Errors ;
import org.keycloak.models.AuthenticationExecutionModel ;
import org.keycloak.models.AuthenticationFlowModel ;
import org.keycloak.models.ClientModel ;
2015-09-03 22:09:54 +00:00
import org.keycloak.models.utils.KeycloakModelUtils ;
2016-01-18 17:18:15 +00:00
import org.keycloak.services.ServicesLogger ;
2015-08-12 16:52:13 +00:00
2016-09-28 19:25:39 +00:00
import javax.ws.rs.core.Response ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.List ;
2015-08-12 16:52:13 +00:00
/ * *
* @author < a href = " mailto:mposolda@redhat.com " > Marek Posolda < / a >
* /
public class ClientAuthenticationFlow implements AuthenticationFlow {
2016-01-18 17:18:15 +00:00
protected static final ServicesLogger logger = ServicesLogger . ROOT_LOGGER ;
2015-08-12 16:52:13 +00:00
Response alternativeChallenge = null ;
AuthenticationProcessor processor ;
AuthenticationFlowModel flow ;
public ClientAuthenticationFlow ( AuthenticationProcessor processor , AuthenticationFlowModel flow ) {
this . processor = processor ;
this . flow = flow ;
}
@Override
public Response processAction ( String actionExecution ) {
throw new IllegalStateException ( " Not supposed to be invoked " ) ;
}
@Override
public Response processFlow ( ) {
2015-09-03 22:09:54 +00:00
List < AuthenticationExecutionModel > executions = findExecutionsToRun ( ) ;
2015-08-12 16:52:13 +00:00
2015-09-03 22:09:54 +00:00
for ( AuthenticationExecutionModel model : executions ) {
2015-08-12 16:52:13 +00:00
ClientAuthenticatorFactory factory = ( ClientAuthenticatorFactory ) processor . getSession ( ) . getKeycloakSessionFactory ( ) . getProviderFactory ( ClientAuthenticator . class , model . getAuthenticator ( ) ) ;
if ( factory = = null ) {
throw new AuthenticationFlowException ( " Could not find ClientAuthenticatorFactory for: " + model . getAuthenticator ( ) , AuthenticationFlowError . INTERNAL_ERROR ) ;
}
ClientAuthenticator authenticator = factory . create ( ) ;
2016-01-18 17:18:15 +00:00
logger . debugv ( " client authenticator: {0} " , factory . getId ( ) ) ;
2015-08-12 16:52:13 +00:00
2015-09-03 22:09:54 +00:00
AuthenticationProcessor . Result context = processor . createClientAuthenticatorContext ( model , authenticator , executions ) ;
authenticator . authenticateClient ( context ) ;
ClientModel client = processor . getClient ( ) ;
if ( client ! = null ) {
String expectedClientAuthType = client . getClientAuthenticatorType ( ) ;
// Fallback to secret just in case (for backwards compatibility)
if ( expectedClientAuthType = = null ) {
expectedClientAuthType = KeycloakModelUtils . getDefaultClientAuthenticatorType ( ) ;
2016-01-18 17:18:15 +00:00
logger . authMethodFallback ( client . getClientId ( ) , expectedClientAuthType ) ;
2015-08-12 16:52:13 +00:00
}
2015-09-03 22:09:54 +00:00
// Check if client authentication matches
if ( factory . getId ( ) . equals ( expectedClientAuthType ) ) {
2015-10-12 07:38:13 +00:00
Response response = processResult ( context ) ;
if ( response ! = null ) return response ;
if ( ! context . getStatus ( ) . equals ( FlowStatus . SUCCESS ) ) {
throw new AuthenticationFlowException ( " Expected success, but for an unknown reason the status was " + context . getStatus ( ) , AuthenticationFlowError . INTERNAL_ERROR ) ;
}
2016-01-18 17:18:15 +00:00
logger . debugv ( " Client {0} authenticated by {1} " , client . getClientId ( ) , factory . getId ( ) ) ;
2015-09-03 22:09:54 +00:00
processor . getEvent ( ) . detail ( Details . CLIENT_AUTH_METHOD , factory . getId ( ) ) ;
return null ;
2015-08-12 16:52:13 +00:00
}
}
2015-09-03 22:09:54 +00:00
}
2015-08-12 16:52:13 +00:00
2015-09-03 22:09:54 +00:00
// Check if any alternative challenge was identified
if ( alternativeChallenge ! = null ) {
processor . getEvent ( ) . error ( Errors . INVALID_CLIENT ) ;
return alternativeChallenge ;
}
throw new AuthenticationFlowException ( " Client was not identified by any client authenticator " , AuthenticationFlowError . UNKNOWN_CLIENT ) ;
}
protected List < AuthenticationExecutionModel > findExecutionsToRun ( ) {
List < AuthenticationExecutionModel > executions = processor . getRealm ( ) . getAuthenticationExecutions ( flow . getId ( ) ) ;
List < AuthenticationExecutionModel > executionsToRun = new ArrayList < > ( ) ;
for ( AuthenticationExecutionModel execution : executions ) {
if ( execution . isRequired ( ) ) {
executionsToRun = Arrays . asList ( execution ) ;
break ;
2015-08-12 16:52:13 +00:00
}
2015-09-03 22:09:54 +00:00
if ( execution . isAlternative ( ) ) {
executionsToRun . add ( execution ) ;
2015-08-12 16:52:13 +00:00
}
}
2016-01-18 17:18:15 +00:00
if ( logger . isTraceEnabled ( ) ) {
2015-09-03 22:09:54 +00:00
List < String > exIds = new ArrayList < > ( ) ;
for ( AuthenticationExecutionModel execution : executionsToRun ) {
exIds . add ( execution . getId ( ) ) ;
}
2016-01-18 17:18:15 +00:00
logger . tracef ( " Using executions for client authentication: %s " , exIds . toString ( ) ) ;
2015-09-03 22:09:54 +00:00
}
2015-08-12 16:52:13 +00:00
2015-09-03 22:09:54 +00:00
return executionsToRun ;
}
2015-08-12 16:52:13 +00:00
2015-09-03 22:09:54 +00:00
protected Response processResult ( AuthenticationProcessor . Result result ) {
2015-08-12 16:52:13 +00:00
AuthenticationExecutionModel execution = result . getExecution ( ) ;
FlowStatus status = result . getStatus ( ) ;
2015-09-03 22:09:54 +00:00
2016-01-18 17:18:15 +00:00
logger . debugv ( " client authenticator {0}: {1} " , status . toString ( ) , execution . getAuthenticator ( ) ) ;
2015-09-03 22:09:54 +00:00
2015-08-12 16:52:13 +00:00
if ( status = = FlowStatus . SUCCESS ) {
return null ;
2015-10-12 07:38:13 +00:00
}
if ( status = = FlowStatus . FAILED ) {
2015-08-12 16:52:13 +00:00
if ( result . getChallenge ( ) ! = null ) {
return sendChallenge ( result , execution ) ;
2015-09-03 22:09:54 +00:00
} else {
throw new AuthenticationFlowException ( result . getError ( ) ) ;
2015-08-12 16:52:13 +00:00
}
} else if ( status = = FlowStatus . FORCE_CHALLENGE ) {
return sendChallenge ( result , execution ) ;
} else if ( status = = FlowStatus . CHALLENGE ) {
2015-09-03 22:09:54 +00:00
2015-08-12 16:52:13 +00:00
// Make sure the first priority alternative challenge is used
2015-09-03 22:09:54 +00:00
if ( alternativeChallenge = = null ) {
2015-08-12 16:52:13 +00:00
alternativeChallenge = result . getChallenge ( ) ;
}
2015-10-12 07:38:13 +00:00
return sendChallenge ( result , execution ) ;
2015-08-12 16:52:13 +00:00
} else if ( status = = FlowStatus . FAILURE_CHALLENGE ) {
return sendChallenge ( result , execution ) ;
} else {
2016-01-18 17:18:15 +00:00
logger . unknownResultStatus ( ) ;
2015-08-12 16:52:13 +00:00
throw new AuthenticationFlowException ( AuthenticationFlowError . INTERNAL_ERROR ) ;
}
}
public Response sendChallenge ( AuthenticationProcessor . Result result , AuthenticationExecutionModel execution ) {
2016-01-18 17:18:15 +00:00
logger . debugv ( " client authenticator: sending challenge for authentication execution {0} " , execution . getAuthenticator ( ) ) ;
2015-08-12 16:52:13 +00:00
if ( result . getError ( ) ! = null ) {
String errorAsString = result . getError ( ) . toString ( ) . toLowerCase ( ) ;
result . getEvent ( ) . error ( errorAsString ) ;
} else {
if ( result . getClient ( ) = = null ) {
result . getEvent ( ) . error ( Errors . INVALID_CLIENT ) ;
} else {
result . getEvent ( ) . error ( Errors . INVALID_CLIENT_CREDENTIALS ) ;
}
}
return result . getChallenge ( ) ;
}
}