Merge pull request #3028 from mposolda/KEYCLOAK-3219

KEYCLOAK-3219 Added claims info to OIDCWellKnownProvider. More tests
This commit is contained in:
Marek Posolda 2016-07-13 12:28:29 +02:00 committed by GitHub
commit 912bc8464e
6 changed files with 95 additions and 4 deletions

View file

@ -104,6 +104,11 @@ public class OIDCLoginProtocolService {
return uriBuilder.path(OIDCLoginProtocolService.class, "certs"); return uriBuilder.path(OIDCLoginProtocolService.class, "certs");
} }
public static UriBuilder userInfoUrl(UriBuilder baseUriBuilder) {
UriBuilder uriBuilder = tokenServiceBaseUrl(baseUriBuilder);
return uriBuilder.path(OIDCLoginProtocolService.class, "issueUserInfo");
}
public static UriBuilder tokenIntrospectionUrl(UriBuilder baseUriBuilder) { public static UriBuilder tokenIntrospectionUrl(UriBuilder baseUriBuilder) {
return tokenUrl(baseUriBuilder).path(TokenEndpoint.class, "introspect"); return tokenUrl(baseUriBuilder).path(TokenEndpoint.class, "introspect");
} }

View file

@ -24,6 +24,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.endpoints.TokenEndpoint; import org.keycloak.protocol.oidc.endpoints.TokenEndpoint;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken;
import org.keycloak.services.clientregistration.ClientRegistrationService; import org.keycloak.services.clientregistration.ClientRegistrationService;
import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory; import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory;
import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.RealmsResource;
@ -55,6 +56,11 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
public static final List<String> DEFAULT_CLIENT_AUTH_SIGNING_ALG_VALUES_SUPPORTED = list(Algorithm.RS256.toString()); public static final List<String> DEFAULT_CLIENT_AUTH_SIGNING_ALG_VALUES_SUPPORTED = list(Algorithm.RS256.toString());
// The exact list depends on protocolMappers
public static final List<String> DEFAULT_CLAIMS_SUPPORTED= list("sub", "iss", IDToken.AUTH_TIME, IDToken.NAME, IDToken.GIVEN_NAME, IDToken.FAMILY_NAME, IDToken.PREFERRED_USERNAME, IDToken.EMAIL);
public static final List<String> DEFAULT_CLAIM_TYPES_SUPPORTED= list("normal");
private KeycloakSession session; private KeycloakSession session;
public OIDCWellKnownProvider(KeycloakSession session) { public OIDCWellKnownProvider(KeycloakSession session) {
@ -87,6 +93,10 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
config.setTokenEndpointAuthMethodsSupported(DEFAULT_CLIENT_AUTH_METHODS_SUPPORTED); config.setTokenEndpointAuthMethodsSupported(DEFAULT_CLIENT_AUTH_METHODS_SUPPORTED);
config.setTokenEndpointAuthSigningAlgValuesSupported(DEFAULT_CLIENT_AUTH_SIGNING_ALG_VALUES_SUPPORTED); config.setTokenEndpointAuthSigningAlgValuesSupported(DEFAULT_CLIENT_AUTH_SIGNING_ALG_VALUES_SUPPORTED);
config.setClaimsSupported(DEFAULT_CLAIMS_SUPPORTED);
config.setClaimTypesSupported(DEFAULT_CLAIM_TYPES_SUPPORTED);
config.setClaimsParameterSupported(false);
return config; return config;
} }

View file

@ -76,6 +76,15 @@ public class OIDCConfigurationRepresentation {
@JsonProperty("token_endpoint_auth_signing_alg_values_supported") @JsonProperty("token_endpoint_auth_signing_alg_values_supported")
private List<String> tokenEndpointAuthSigningAlgValuesSupported; private List<String> tokenEndpointAuthSigningAlgValuesSupported;
@JsonProperty("claims_supported")
private List<String> claimsSupported;
@JsonProperty("claim_types_supported")
private List<String> claimTypesSupported;
@JsonProperty("claims_parameter_supported")
private Boolean claimsParameterSupported;
protected Map<String, Object> otherClaims = new HashMap<String, Object>(); protected Map<String, Object> otherClaims = new HashMap<String, Object>();
public String getIssuer() { public String getIssuer() {
@ -198,6 +207,30 @@ public class OIDCConfigurationRepresentation {
this.tokenEndpointAuthSigningAlgValuesSupported = tokenEndpointAuthSigningAlgValuesSupported; this.tokenEndpointAuthSigningAlgValuesSupported = tokenEndpointAuthSigningAlgValuesSupported;
} }
public List<String> getClaimsSupported() {
return claimsSupported;
}
public void setClaimsSupported(List<String> claimsSupported) {
this.claimsSupported = claimsSupported;
}
public List<String> getClaimTypesSupported() {
return claimTypesSupported;
}
public void setClaimTypesSupported(List<String> claimTypesSupported) {
this.claimTypesSupported = claimTypesSupported;
}
public Boolean getClaimsParameterSupported() {
return claimsParameterSupported;
}
public void setClaimsParameterSupported(Boolean claimsParameterSupported) {
this.claimsParameterSupported = claimsParameterSupported;
}
@JsonAnyGetter @JsonAnyGetter
public Map<String, Object> getOtherClaims() { public Map<String, Object> getOtherClaims() {
return otherClaims; return otherClaims;

View file

@ -485,7 +485,7 @@ public class OAuthClient {
} }
public String getLoginFormUrl() { public String getLoginFormUrl() {
UriBuilder b = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(SERVER_ROOT + "/auth")); UriBuilder b = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(AUTH_SERVER_ROOT));
b.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE); b.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE);
if (clientId != null) { if (clientId != null) {
b.queryParam(OAuth2Constants.CLIENT_ID, clientId); b.queryParam(OAuth2Constants.CLIENT_ID, clientId);

View file

@ -44,8 +44,8 @@ public class UserInfoClientUtil {
public static WebTarget getUserInfoWebTarget(Client client) { public static WebTarget getUserInfoWebTarget(Client client) {
UriBuilder builder = UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT); UriBuilder builder = UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT);
UriBuilder uriBuilder = OIDCLoginProtocolService.tokenServiceBaseUrl(builder); UriBuilder uriBuilder = OIDCLoginProtocolService.userInfoUrl(builder);
URI userInfoUri = uriBuilder.path(OIDCLoginProtocolService.class, "issueUserInfo").build("test"); URI userInfoUri = uriBuilder.build("test");
return client.target(userInfoUri); return client.target(userInfoUri);
} }

View file

@ -26,17 +26,21 @@ import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.jose.jws.Algorithm; import org.keycloak.jose.jws.Algorithm;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory; import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation; import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.resources.RealmsResource; import org.keycloak.services.resources.RealmsResource;
import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.AbstractAdminTest; import org.keycloak.testsuite.admin.AbstractAdminTest;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
/** /**
@ -50,6 +54,18 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
testRealms.add(realm); testRealms.add(realm);
} }
@Before
public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true);
/*
* Configure the default client ID. Seems like OAuthClient is keeping the state of clientID
* For example: If some test case configure oauth.clientId("sample-public-client"), other tests
* will faile and the clientID will always be "sample-public-client
* @see AccessTokenTest#testAuthorizationNegotiateHeaderIgnored()
*/
oauth.clientId("test-app");
}
@Test @Test
public void testDiscovery() { public void testDiscovery() {
@ -57,6 +73,12 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
try { try {
OIDCConfigurationRepresentation oidcConfig = getOIDCDiscoveryConfiguration(client); OIDCConfigurationRepresentation oidcConfig = getOIDCDiscoveryConfiguration(client);
// URIs are filled
Assert.assertEquals(oidcConfig.getAuthorizationEndpoint(), OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT)).build("test").toString());
Assert.assertEquals(oidcConfig.getTokenEndpoint(), oauth.getAccessTokenUrl());
Assert.assertEquals(oidcConfig.getUserinfoEndpoint(), OIDCLoginProtocolService.userInfoUrl(UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT)).build("test").toString());
Assert.assertEquals(oidcConfig.getJwksUri(), oauth.getCertsUrl("test"));
// Support standard + implicit + hybrid flow // Support standard + implicit + hybrid flow
assertContains(oidcConfig.getResponseTypesSupported(), OAuth2Constants.CODE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token"); assertContains(oidcConfig.getResponseTypesSupported(), OAuth2Constants.CODE, OIDCResponseType.ID_TOKEN, "id_token token", "code id_token", "code token", "code id_token token");
assertContains(oidcConfig.getGrantTypesSupported(), OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT); assertContains(oidcConfig.getGrantTypesSupported(), OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT);
@ -68,7 +90,28 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
// Client authentication // Client authentication
Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt"); Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt");
Assert.assertNames(oidcConfig.getTokenEndpointAuthSigningAlgValuesSupported(), Algorithm.RS256.toString()); Assert.assertNames(oidcConfig.getTokenEndpointAuthSigningAlgValuesSupported(), Algorithm.RS256.toString());
System.out.println("Fopo");
// Claims
assertContains(oidcConfig.getClaimsSupported(), IDToken.NAME, IDToken.EMAIL, IDToken.PREFERRED_USERNAME, IDToken.FAMILY_NAME);
Assert.assertNames(oidcConfig.getClaimTypesSupported(), "normal");
Assert.assertFalse(oidcConfig.getClaimsParameterSupported());
} finally {
client.close();
}
}
@Test
public void testIssuerMatches() throws Exception {
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
Assert.assertEquals(200, response.getStatusCode());
IDToken idToken = oauth.verifyIDToken(response.getIdToken());
Client client = ClientBuilder.newClient();
try {
OIDCConfigurationRepresentation oidcConfig = getOIDCDiscoveryConfiguration(client);
// assert issuer matches
Assert.assertEquals(idToken.getIssuer(), oidcConfig.getIssuer());
} finally { } finally {
client.close(); client.close();
} }