Merge pull request #2110 from mposolda/master

KEYCLOAK-2351 Support for response_type=token to be OAuth2 compliant
This commit is contained in:
Marek Posolda 2016-01-26 17:54:08 +01:00
commit 4baeaded23
7 changed files with 27 additions and 16 deletions

View file

@ -28,6 +28,14 @@
and his browser and hence can't rely on sticky sessions.
</para>
<note>
<para>
To enable distributable (replicated) HTTP Sessions in your application, you may need to do some additional steps. Usually you need to put <![CDATA[<distributable />]]>
tag into <literal>WEB-INF/web.xml</literal> file of your application and possibly do some additional steps to configure underlying cluster cache (In case of
Wildfly, the implementation of cluster cache is based on Infinispan). These steps are server specific, so consult documentation of your application server for more details.
</para>
</note>
<section id="stateless-token-store">
<title>Stateless token store</title>
<para>

View file

@ -2,6 +2,7 @@ package org.keycloak.authentication.authenticators.client;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@ -120,7 +121,7 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
// Validate other things
String expectedAudience = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName());
if (!token.hasAudience(expectedAudience)) {
throw new RuntimeException("Token audience doesn't match domain. Realm audience is '" + expectedAudience + "' but audience from token is '" + token.getAudience() + "'");
throw new RuntimeException("Token audience doesn't match domain. Realm audience is '" + expectedAudience + "' but audience from token is '" + Arrays.asList(token.getAudience()).toString() + "'");
}
if (!token.isActive()) {

View file

@ -26,7 +26,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS);
public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token");
public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, OIDCResponseType.TOKEN, "id_token token", "code id_token", "code token", "code id_token token");
public static final List<String> DEFAULT_SUBJECT_TYPES_SUPPORTED = list("public");

View file

@ -63,9 +63,11 @@ public class OIDCResponseType {
if (responseTypes.contains(NONE) && responseTypes.size() > 1) {
throw new IllegalArgumentException("None not allowed with some other response_type");
}
if (responseTypes.contains(TOKEN) && responseTypes.size() == 1) {
throw new IllegalArgumentException("Not supported to use response_type=token alone");
}
// response_type value "token" alone is not mentioned in OIDC specification, however it is supported by OAuth2. We allow it just to be compatible with pure OAuth2 clients like swagger.ui
// if (responseTypes.contains(TOKEN) && responseTypes.size() == 1) {
// throw new IllegalArgumentException("Not supported to use response_type=token alone");
// }
}
@ -79,7 +81,7 @@ public class OIDCResponseType {
}
public boolean isImplicitFlow() {
return hasResponseType(ID_TOKEN) && !hasResponseType(CODE);
return (hasResponseType(TOKEN) || hasResponseType(ID_TOKEN)) && !hasResponseType(CODE);
}

View file

@ -20,7 +20,7 @@ public class ResponseTypeTest {
assertSuccess("code");
assertSuccess("none");
assertSuccess("id_token");
assertFail("token");
assertSuccess("token");
assertFail("refresh_token");
assertSuccess("id_token token");
assertSuccess("code token");
@ -32,13 +32,13 @@ public class ResponseTypeTest {
@Test
public void testMultipleResponseTypes() {
try {
OIDCResponseType.parse(Arrays.asList("code", "token"));
Assert.fail("Not expected to parse with success");
} catch (IllegalArgumentException iae) {
}
OIDCResponseType responseType = OIDCResponseType.parse(Arrays.asList("code", "token"));
Assert.assertTrue(responseType.hasResponseType("code"));
Assert.assertFalse(responseType.hasResponseType("none"));
Assert.assertTrue(responseType.isImplicitOrHybridFlow());
Assert.assertFalse(responseType.isImplicitFlow());
OIDCResponseType responseType = OIDCResponseType.parse(Collections.singletonList("code"));
responseType = OIDCResponseType.parse(Collections.singletonList("code"));
Assert.assertTrue(responseType.hasResponseType("code"));
Assert.assertFalse(responseType.hasResponseType("none"));
Assert.assertFalse(responseType.isImplicitOrHybridFlow());

View file

@ -90,7 +90,7 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
try {
OIDCClientRepresentation response = create();
reg.auth(Auth.token(response));
response.setResponseTypes(Arrays.asList("code", "token"));
response.setResponseTypes(Arrays.asList("code", "tokenn"));
reg.oidc().update(response);
fail("Not expected to end with success");
} catch (ClientRegistrationException cre) {

View file

@ -171,10 +171,10 @@ public class AuthorizationCodeTest {
@Test
public void authorizationRequestInvalidResponseType() throws IOException {
UriBuilder b = UriBuilder.fromUri(oauth.getLoginFormUrl());
b.replaceQueryParam(OAuth2Constants.RESPONSE_TYPE, "token");
b.replaceQueryParam(OAuth2Constants.RESPONSE_TYPE, "tokenn");
driver.navigate().to(b.build().toURL());
assertEquals("Invalid parameter: response_type", errorPage.getError());
events.expectLogin().error(Errors.INVALID_REQUEST).client((String) null).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "token").assertEvent();
events.expectLogin().error(Errors.INVALID_REQUEST).client((String) null).user((String) null).session((String) null).clearDetails().detail(Details.RESPONSE_TYPE, "tokenn").assertEvent();
}
private void assertCode(String expectedCodeId, String actualCode) {