OIDCDynamicRegistrationTest.java
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.cxf.systest.jaxrs.security.oidc;
import java.net.URL;
import java.util.Collections;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.provider.json.JsonMapObjectProvider;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.services.ClientRegistration;
import org.apache.cxf.rs.security.oauth2.services.ClientRegistrationResponse;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
import org.apache.cxf.rs.security.oidc.utils.OidcUtils;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.junit.BeforeClass;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class OIDCDynamicRegistrationTest extends AbstractBusClientServerTestBase {
private static final SpringBusTestServer DYNREG_SERVER = new SpringBusTestServer("oidc-server-dynreg");
private static final String ACCESS_TOKEN = "123456789";
@BeforeClass
public static void startServers() throws Exception {
System.setProperty("accessToken", ACCESS_TOKEN);
assertTrue("server did not launch correctly", launchServer(DYNREG_SERVER));
}
@org.junit.Test
public void testGetClientRegNotAvail() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamic/register";
WebClient wc = WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()),
busFile.toString());
Response r = wc.accept("application/json").path("some-client-id").get();
assertEquals(401, r.getStatus());
}
@org.junit.Test
public void testRegisterClientNoInitialAccessToken() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamic/register";
WebClient wc = WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()),
busFile.toString());
wc.accept("application/json").type("application/json");
assertEquals(401, wc.post(newClientRegistrationCodeGrant()).getStatus());
}
@org.junit.Test
public void testRegisterClientInitialAccessTokenCodeGrant() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
WebClient wc =
WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
.accept("application/json").type("application/json")
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistration reg = newClientRegistrationCodeGrant();
ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
assertNotNull(resp.getClientSecret());
assertEquals(address + "/" + resp.getClientId(),
resp.getRegistrationClientUri());
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
wc.path(resp.getClientId());
assertEquals(401, wc.get().getStatus());
ClientRegistration clientRegResp = wc
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
.get(ClientRegistration.class);
testCommonRegCodeGrantProperties(clientRegResp);
assertNull(clientRegResp.getTokenEndpointAuthMethod());
assertEquals(200, wc.delete().getStatus());
}
@org.junit.Test
public void testRegisterClientPasswordGrant() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
WebClient wc =
WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
.accept("application/json").type("application/json")
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistration reg = new ClientRegistration();
reg.setClientName("dynamic_client");
reg.setGrantTypes(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT));
ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
assertNotNull(resp.getClientSecret());
assertEquals(address + "/" + resp.getClientId(),
resp.getRegistrationClientUri());
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
ClientRegistration clientRegResp = wc.path(resp.getClientId())
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
.get(ClientRegistration.class);
assertEquals("web", clientRegResp.getApplicationType());
assertEquals("dynamic_client", clientRegResp.getClientName());
assertEquals(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT),
clientRegResp.getGrantTypes());
assertNull(clientRegResp.getTokenEndpointAuthMethod());
assertNull(clientRegResp.getScope());
assertNull(clientRegResp.getRedirectUris());
assertEquals(200, wc.delete().getStatus());
}
@org.junit.Test
public void testRegisterClientPasswordGrantPublic() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
WebClient wc =
WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
.accept("application/json").type("application/json")
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistration reg = new ClientRegistration();
reg.setClientName("dynamic_client");
reg.setGrantTypes(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT));
reg.setTokenEndpointAuthMethod(OAuthConstants.TOKEN_ENDPOINT_AUTH_NONE);
ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
assertNull(resp.getClientSecret());
assertEquals(address + "/" + resp.getClientId(), resp.getRegistrationClientUri());
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
ClientRegistration clientRegResp = wc.path(resp.getClientId())
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
.get(ClientRegistration.class);
assertEquals("native", clientRegResp.getApplicationType());
assertEquals("dynamic_client", clientRegResp.getClientName());
assertEquals(Collections.singletonList(OAuthConstants.RESOURCE_OWNER_GRANT),
clientRegResp.getGrantTypes());
assertEquals(OAuthConstants.TOKEN_ENDPOINT_AUTH_NONE, clientRegResp.getTokenEndpointAuthMethod());
assertNull(clientRegResp.getScope());
assertNull(clientRegResp.getRedirectUris());
assertEquals(200, wc.delete().getStatus());
}
@org.junit.Test
public void testRegisterClientInitialAccessTokenCodeGrantTls() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
WebClient wc =
WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
.accept("application/json").type("application/json")
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
ClientRegistration reg = newClientRegistrationCodeGrant();
reg.setTokenEndpointAuthMethod(OAuthConstants.TOKEN_ENDPOINT_AUTH_TLS);
reg.setProperty(OAuthConstants.TLS_CLIENT_AUTH_SUBJECT_DN,
"CN=whateverhost.com,OU=Morpit,O=ApacheTest,L=Syracuse,C=US");
ClientRegistrationResponse resp = wc.post(reg, ClientRegistrationResponse.class);
assertNotNull(resp.getClientId());
assertNull(resp.getClientSecret());
assertEquals(address + "/" + resp.getClientId(),
resp.getRegistrationClientUri());
String regAccessToken = resp.getRegistrationAccessToken();
assertNotNull(regAccessToken);
ClientRegistration clientRegResp = wc.path(resp.getClientId())
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
.get(ClientRegistration.class);
testCommonRegCodeGrantProperties(clientRegResp);
assertEquals(OAuthConstants.TOKEN_ENDPOINT_AUTH_TLS, clientRegResp.getTokenEndpointAuthMethod());
assertEquals("CN=whateverhost.com,OU=Morpit,O=ApacheTest,L=Syracuse,C=US",
clientRegResp.getProperty(OAuthConstants.TLS_CLIENT_AUTH_SUBJECT_DN));
assertEquals(200, wc.delete().getStatus());
}
@org.junit.Test
public void testUpdateClient() throws Exception {
URL busFile = OIDCDynamicRegistrationTest.class.getResource("client.xml");
String address = "https://localhost:" + DYNREG_SERVER.getPort() + "/services/dynamicWithAt/register";
WebClient wc =
WebClient.create(address, Collections.singletonList(new JsonMapObjectProvider()), busFile.toString())
.accept("application/json").type("application/json")
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, ACCESS_TOKEN));
final ClientRegistration reg = newClientRegistrationCodeGrant();
final ClientRegistrationResponse clientRegistrationResponse = wc
.post(reg, ClientRegistrationResponse.class);
final String regAccessToken = clientRegistrationResponse.getRegistrationAccessToken();
assertNotNull(regAccessToken);
reg.setScope(OidcUtils.getEmailScope());
final ClientRegistration updatedClientRegistration = wc.path(clientRegistrationResponse.getClientId())
.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken))
.put(reg, ClientRegistration.class);
assertEquals(OidcUtils.getEmailScope(), updatedClientRegistration.getScope());
// https://tools.ietf.org/html/rfc7592#section-2.2
assertNull(updatedClientRegistration.getProperty("registration_access_token"));
assertNull(updatedClientRegistration.getProperty("registration_client_uri"));
assertNull(updatedClientRegistration.getProperty("client_secret_expires_at"));
assertNull(updatedClientRegistration.getProperty("client_id_issued_at"));
wc.authorization(null);
assertEquals(Status.UNAUTHORIZED.getStatusCode(),
wc.put(reg).getStatus());
assertEquals(Status.UNAUTHORIZED.getStatusCode(),
wc.delete().getStatus());
wc.authorization(new ClientAccessToken(OAuthConstants.BEARER_AUTHORIZATION_SCHEME, regAccessToken));
assertEquals(200, wc.delete().getStatus());
}
private static ClientRegistration newClientRegistrationCodeGrant() {
final ClientRegistration reg = new ClientRegistration();
reg.setApplicationType("web");
reg.setScope(OidcUtils.getOpenIdScope());
reg.setClientName("dynamic_client");
reg.setGrantTypes(Collections.singletonList(OAuthConstants.AUTHORIZATION_CODE_GRANT));
// reg.setResponseTypes(Collections.singletonList(OAuthConstants.CODE_RESPONSE_TYPE));
reg.setRedirectUris(Collections.singletonList("https://a/b/c"));
reg.setProperty("post_logout_redirect_uris",
Collections.singletonList("https://rp/logout"));
return reg;
}
private static void testCommonRegCodeGrantProperties(ClientRegistration clientRegResp) {
assertNotNull(clientRegResp);
assertEquals("web", clientRegResp.getApplicationType());
assertEquals("openid", clientRegResp.getScope());
assertEquals("dynamic_client", clientRegResp.getClientName());
assertEquals(Collections.singletonList(OAuthConstants.AUTHORIZATION_CODE_GRANT),
clientRegResp.getGrantTypes());
// assertEquals(Collections.singletonList(OAuthConstants.CODE_RESPONSE_TYPE),
// clientRegResp.getResponseTypes());
assertEquals(Collections.singletonList("https://a/b/c"),
clientRegResp.getRedirectUris());
assertEquals(Collections.singletonList("https://rp/logout"),
clientRegResp.getListStringProperty("post_logout_redirect_uris"));
}
}