Merge pull request #3028 from mposolda/KEYCLOAK-3219
KEYCLOAK-3219 Added claims info to OIDCWellKnownProvider. More tests
This commit is contained in:
commit
912bc8464e
6 changed files with 95 additions and 4 deletions
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue