parent
b2e55762f1
commit
8f221bb21e
3 changed files with 57 additions and 1 deletions
|
@ -53,6 +53,9 @@ public class OAuthErrorException extends Exception {
|
||||||
public static final String SLOW_DOWN = "slow_down";
|
public static final String SLOW_DOWN = "slow_down";
|
||||||
public static final String EXPIRED_TOKEN = "expired_token";
|
public static final String EXPIRED_TOKEN = "expired_token";
|
||||||
|
|
||||||
|
// CIBA
|
||||||
|
public static final String INVALID_BINDING_MESSAGE = "invalid_binding_message";
|
||||||
|
|
||||||
// Others
|
// Others
|
||||||
public static final String INVALID_CLIENT = "invalid_client";
|
public static final String INVALID_CLIENT = "invalid_client";
|
||||||
public static final String INVALID_GRANT = "invalid_grant";
|
public static final String INVALID_GRANT = "invalid_grant";
|
||||||
|
|
|
@ -54,6 +54,7 @@ import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.ID_TOKEN_HINT;
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.ID_TOKEN_HINT;
|
||||||
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.LOGIN_HINT_PARAM;
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocol.LOGIN_HINT_PARAM;
|
||||||
|
@ -62,6 +63,8 @@ public class BackchannelAuthenticationEndpoint extends AbstractCibaEndpoint {
|
||||||
|
|
||||||
private final RealmModel realm;
|
private final RealmModel realm;
|
||||||
|
|
||||||
|
private static final Pattern BINDING_MESSAGE_VALIDATION = Pattern.compile("^[a-zA-Z0-9-._+/!?#]{1,50}$");
|
||||||
|
|
||||||
public BackchannelAuthenticationEndpoint(KeycloakSession session, EventBuilder event) {
|
public BackchannelAuthenticationEndpoint(KeycloakSession session, EventBuilder event) {
|
||||||
super(session, event);
|
super(session, event);
|
||||||
this.realm = session.getContext().getRealm();
|
this.realm = session.getContext().getRealm();
|
||||||
|
@ -172,7 +175,10 @@ public class BackchannelAuthenticationEndpoint extends AbstractCibaEndpoint {
|
||||||
request.setScope(scope);
|
request.setScope(scope);
|
||||||
|
|
||||||
// optional parameters
|
// optional parameters
|
||||||
if (endpointRequest.getBindingMessage() != null) request.setBindingMessage(endpointRequest.getBindingMessage());
|
if (endpointRequest.getBindingMessage() != null) {
|
||||||
|
validateBindingMessage(endpointRequest.getBindingMessage());
|
||||||
|
request.setBindingMessage(endpointRequest.getBindingMessage());
|
||||||
|
}
|
||||||
if (endpointRequest.getAcr() != null) request.setAcrValues(endpointRequest.getAcr());
|
if (endpointRequest.getAcr() != null) request.setAcrValues(endpointRequest.getAcr());
|
||||||
|
|
||||||
CibaConfig policy = realm.getCibaPolicy();
|
CibaConfig policy = realm.getCibaPolicy();
|
||||||
|
@ -228,6 +234,13 @@ public class BackchannelAuthenticationEndpoint extends AbstractCibaEndpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void validateBindingMessage(String bindingMessage) {
|
||||||
|
if (!BINDING_MESSAGE_VALIDATION.matcher(bindingMessage).matches()) {
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_BINDING_MESSAGE, "the binding_message value has to be max 50 characters in length and must contain only basic plain-text characters without spaces",
|
||||||
|
Response.Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private UserModel resolveUser(BackchannelAuthenticationEndpointRequest endpointRequest, String authRequestedUserHint) {
|
private UserModel resolveUser(BackchannelAuthenticationEndpointRequest endpointRequest, String authRequestedUserHint) {
|
||||||
CIBALoginUserResolver resolver = session.getProvider(CIBALoginUserResolver.class);
|
CIBALoginUserResolver resolver = session.getProvider(CIBALoginUserResolver.class);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
@ -397,6 +398,45 @@ public class CIBATest extends AbstractClientPoliciesTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Corresponds to the test fapi-ciba-id1-ensure-authorization-request-with-potentially-bad-binding-message from the FAPI CIBA conformance testsuite
|
||||||
|
@Test
|
||||||
|
public void testBadBindingMessage() throws Exception {
|
||||||
|
ClientResource clientResource = null;
|
||||||
|
ClientRepresentation clientRep = null;
|
||||||
|
try {
|
||||||
|
final String username = "nutzername-schwarz";
|
||||||
|
clientResource = ApiUtil.findClientByClientId(adminClient.realm(TEST_REALM_NAME), TEST_CLIENT_NAME);
|
||||||
|
clientRep = clientResource.toRepresentation();
|
||||||
|
prepareCIBASettings(clientResource, clientRep);
|
||||||
|
|
||||||
|
// Binding message with non plain-text characters
|
||||||
|
String bindingMessage = "1234 \uD83D\uDC4D\uD83C\uDFFF 品川 Lor";
|
||||||
|
AuthenticationRequestAcknowledgement response = oauth.doBackchannelAuthenticationRequest(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, username, bindingMessage, null);
|
||||||
|
assertThat(response.getStatusCode(), is(equalTo(400)));
|
||||||
|
assertThat(response.getError(), is(OAuthErrorException.INVALID_BINDING_MESSAGE));
|
||||||
|
|
||||||
|
// Long binding message
|
||||||
|
bindingMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud.";
|
||||||
|
response = oauth.doBackchannelAuthenticationRequest(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, username, bindingMessage, null);
|
||||||
|
assertThat(response.getStatusCode(), is(equalTo(400)));
|
||||||
|
assertThat(response.getError(), is(OAuthErrorException.INVALID_BINDING_MESSAGE));
|
||||||
|
|
||||||
|
// Empty binding message
|
||||||
|
bindingMessage = "";
|
||||||
|
response = oauth.doBackchannelAuthenticationRequest(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, username, bindingMessage, null);
|
||||||
|
assertThat(response.getStatusCode(), is(equalTo(400)));
|
||||||
|
assertThat(response.getError(), is(OAuthErrorException.INVALID_BINDING_MESSAGE));
|
||||||
|
|
||||||
|
// Valid binding message
|
||||||
|
bindingMessage = "Lorem_ipsum";
|
||||||
|
response = oauth.doBackchannelAuthenticationRequest(TEST_CLIENT_NAME, TEST_CLIENT_PASSWORD, username, bindingMessage, null);
|
||||||
|
assertThat(response.getStatusCode(), is(equalTo(200)));
|
||||||
|
assertThat(response.getError(), is(nullValue()));
|
||||||
|
} finally {
|
||||||
|
revertCIBASettings(clientResource, clientRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Should never happen because the AD does not send any information about the user but only the status of the authentication")
|
@Ignore("Should never happen because the AD does not send any information about the user but only the status of the authentication")
|
||||||
public void testDifferentUserAuthenticated() throws Exception {
|
public void testDifferentUserAuthenticated() throws Exception {
|
||||||
|
|
Loading…
Reference in a new issue