Polish gh-1320
This commit is contained in:
@@ -3,60 +3,50 @@
|
||||
:index-link: ../how-to.html
|
||||
:docs-dir: ..
|
||||
|
||||
This guide shows how to configure OpenID Connect Dynamic Client Registration 1.0 in Spring Authorization Server and walks through an example of how to register a client.
|
||||
Spring Authorization Server implements https://openid.net/specs/openid-connect-registration-1_0.html[OpenID Connect Dynamic Client Registration 1.0]
|
||||
specification, gaining the ability to dynamically register and retrieve OpenID clients.
|
||||
This guide shows how to configure OpenID Connect Dynamic Client Registration in Spring Authorization Server and walks through an example of how to register a client.
|
||||
Spring Authorization Server implements the https://openid.net/specs/openid-connect-registration-1_0.html[OpenID Connect Dynamic Client Registration 1.0] specification, providing the capability to dynamically register and retrieve OpenID Connect clients.
|
||||
|
||||
- xref:guides/how-to-dynamic-client-registration.adoc#enable[Enable Dynamic Client Registration]
|
||||
- xref:guides/how-to-dynamic-client-registration.adoc#configure-initial-client[Configure initial client]
|
||||
- xref:guides/how-to-dynamic-client-registration.adoc#obtain-initial-access-token[Obtain initial access token]
|
||||
- xref:guides/how-to-dynamic-client-registration.adoc#register-client[Register a client]
|
||||
* xref:guides/how-to-dynamic-client-registration.adoc#enable-dynamic-client-registration[Enable Dynamic Client Registration]
|
||||
* xref:guides/how-to-dynamic-client-registration.adoc#configure-client-registrar[Configure client registrar]
|
||||
* xref:guides/how-to-dynamic-client-registration.adoc#obtain-initial-access-token[Obtain initial access token]
|
||||
* xref:guides/how-to-dynamic-client-registration.adoc#register-client[Register a client]
|
||||
|
||||
[[enable]]
|
||||
[[enable-dynamic-client-registration]]
|
||||
== Enable Dynamic Client Registration
|
||||
|
||||
By default, dynamic client registration functionality is disabled in Spring Authorization Server.
|
||||
To enable, add the following configuration:
|
||||
|
||||
[[sample.dcrAuthServerConfig]]
|
||||
[[sample.SecurityConfig]]
|
||||
[source,java]
|
||||
----
|
||||
include::{examples-dir}/main/java/sample/dcr/DcrConfiguration.java[]
|
||||
include::{examples-dir}/main/java/sample/registration/SecurityConfig.java[]
|
||||
----
|
||||
|
||||
<1> Add a `SecurityFilterChain` `@Bean` that registers an `OAuth2AuthorizationServerConfigurer`
|
||||
<2> In the configurer, apply OIDC client registration endpoint customizer with default values.
|
||||
This enables dynamic client registration functionality.
|
||||
<1> Enable the xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration Endpoint] with the default configuration.
|
||||
|
||||
Please refer to xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[Client Registration Endpoint docs] for in-depth configuration details.
|
||||
[[configure-client-registrar]]
|
||||
== Configure client registrar
|
||||
|
||||
[[configure-initial-client]]
|
||||
== Configure initial client
|
||||
An existing client is used to register new clients with the authorization server.
|
||||
The client must be configured with scopes `client.create` and optionally `client.read` for registering clients and retrieving clients, respectively.
|
||||
The following listing shows an example client:
|
||||
|
||||
An initial client is required in order to register new clients in the authorization server.
|
||||
The client must be configured with scopes `client.create` and optionally `client.read` for creating clients and reading clients, respectively.
|
||||
A programmatic example of such a client is below.
|
||||
|
||||
[[sample.dcrRegisteredClientConfig]]
|
||||
[[sample.ClientConfig]]
|
||||
[source,java]
|
||||
----
|
||||
include::{examples-dir}/main/java/sample/dcr/RegisteredClientConfiguration.java[]
|
||||
include::{examples-dir}/main/java/sample/registration/ClientConfig.java[]
|
||||
----
|
||||
|
||||
<1> A `RegisteredClientRepository` `@Bean` is configured with a set of clients.
|
||||
<2> An initial client with client id `dcr-client` is configured.
|
||||
<3> `client_credentials` grant type is set to fetch access tokens directly.
|
||||
<4> `client.create` scope is configured for the client to ensure they are able to create clients.
|
||||
<5> `client.read` scope is configured for the client to ensure they are able to fetch and read clients.
|
||||
<6> The initial client is saved into the data store.
|
||||
|
||||
After configuring the above, run the authorization server in your preferred environment.
|
||||
<1> `client_credentials` grant type is configured to obtain access tokens directly.
|
||||
<2> `client.create` scope is configured to allow the client to register a new client.
|
||||
<3> `client.read` scope is configured to allow the client to retrieve a registered client.
|
||||
|
||||
[[obtain-initial-access-token]]
|
||||
== Obtain initial access token
|
||||
|
||||
An initial access token is required to be able to create client registration requests.
|
||||
The token request must contain a request for scope `client.create` only.
|
||||
An "initial" access token is required for the client registration request.
|
||||
The access token request *MUST* contain the `scope` parameter value `client.create` only.
|
||||
|
||||
[source,httprequest]
|
||||
----
|
||||
@@ -69,18 +59,18 @@ grant_type=client_credentials&scope=client.create
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
If you provide more than one scope in the request, you will not be able to register a client.
|
||||
The client creation request requires an access token with a single scope of `client.create`
|
||||
The client registration request requires an access token with a single scope of `client.create`.
|
||||
If the access token contains additional scope, the client registration request will be denied.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
To obtain encoded credentials for the above request, `base64` encode the client credentials in the format of
|
||||
`<clientId>:<clientSecret>`. Below is an encoding operation for the example in this guide.
|
||||
To obtain encoded credentials for the above request, `base64` encode the client credentials in the format of `<clientId>:<clientSecret>`.
|
||||
Below is an encoding operation for the example in this guide.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
echo -n "initial-app:secret" | base64
|
||||
echo -n "registrar-client:secret" | base64
|
||||
----
|
||||
====
|
||||
|
||||
@@ -90,30 +80,26 @@ echo -n "initial-app:secret" | base64
|
||||
With an access token obtained from the previous step, a client can now be dynamically registered.
|
||||
|
||||
[NOTE]
|
||||
The access token can only be used once. After a single registration request, the access token is invalidated.
|
||||
The "initial" access token can only be used once.
|
||||
After the client is registered, the access token is invalidated.
|
||||
|
||||
[[sample.dcrClientRegistration]]
|
||||
[[sample.ClientRegistrar]]
|
||||
[source,java]
|
||||
----
|
||||
include::{examples-dir}/main/java/sample/dcr/DcrClient.java[]
|
||||
include::{examples-dir}/main/java/sample/registration/ClientRegistrar.java[]
|
||||
----
|
||||
|
||||
<1> A minimal client registration request object.
|
||||
You may add additional fields as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[OpenID Connect Dynamic Client Registration 1.0 spec - Client Registration Request].
|
||||
<2> A minimal client registration response object.
|
||||
You may add additional response fields as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[OpenID Connect Dynamic Client Registration 1.0 spec - Client Registration Response].
|
||||
<3> A sample client registration request object which will be used to register a sample client.
|
||||
<4> Example dynamic client registration procedure, demonstrating dynamic registration and client retrieval.
|
||||
<5> Register a client using sample request from step 2, using initial access token from previous step.
|
||||
Skip to step 10 for implementation.
|
||||
<6> After registration, assert on the fields that should be populated in the response upon successful registration.
|
||||
<7> Extract `registration_access_token` and `registration_client_uri` fields, for use in retrieval of the newly registered client.
|
||||
<8> Retrieve client. Skip to step 11 for implementation.
|
||||
<9> After client retrieval, assert on the fields that should be populated in the response.
|
||||
<10> Sample client registration procedure using Spring WebFlux's `WebClient`.
|
||||
Note that the `WebClient` must have `baseUrl` of the authorization server configured.
|
||||
<11> Sample client retrieval procedure using Spring WebFlux's `WebClient`.
|
||||
Note that the `WebClient` must have `baseUrl` of the authorization server configured.
|
||||
<1> A minimal representation of a client registration request. You may add additional client metadata parameters as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration Request].
|
||||
<2> A minimal representation of a client registration response. You may add additional client metadata parameters as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration Response].
|
||||
<3> Example demonstrating client registration and client retrieval.
|
||||
<4> A sample client registration request object.
|
||||
<5> Register the client using the "initial" access token and client registration request object.
|
||||
<6> After successful registration, assert on the client metadata parameters that should be populated in the response.
|
||||
<7> Extract `registration_access_token` and `registration_client_uri` response parameters, for use in retrieval of the newly registered client.
|
||||
<8> Retrieve the client using the `registration_access_token` and `registration_client_uri`.
|
||||
<9> After client retrieval, assert on the client metadata parameters that should be populated in the response.
|
||||
<10> Sample https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration Request] using `WebClient`.
|
||||
<11> Sample https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read Request] using `WebClient`.
|
||||
|
||||
The retrieve client response should contain the same information about the client as seen when the client was first
|
||||
registered, except for `registration_access_token` field.
|
||||
[NOTE]
|
||||
The https://openid.net/specs/openid-connect-registration-1_0.html#ReadResponse[Client Read Response] should contain the same client metadata parameters as the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration Response], except the `registration_access_token` parameter.
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2023 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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 sample.dcr;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class DcrConfiguration {
|
||||
@Bean // <1>
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
|
||||
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
|
||||
.oidc(oidc -> oidc.clientRegistrationEndpoint(Customizer.withDefaults())); // <2>
|
||||
http.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer.jwt(Customizer.withDefaults()));
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// @fold:on
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
// This example uses client credentials grant type - no need for any users.
|
||||
return new InMemoryUserDetailsManager(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JWKSource<SecurityContext> jwkSource() {
|
||||
// @formatter:off
|
||||
KeyPair keyPair;
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(2048);
|
||||
keyPair = keyPairGenerator.generateKeyPair();
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
RSAKey rsaKey = new RSAKey.Builder(publicKey)
|
||||
.privateKey(privateKey)
|
||||
.keyID(UUID.randomUUID().toString())
|
||||
.build();
|
||||
// @formatter:on
|
||||
JWKSet jwkSet = new JWKSet(rsaKey);
|
||||
return new ImmutableJWKSet<>(jwkSet);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
|
||||
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().build();
|
||||
}
|
||||
// @fold:off
|
||||
}
|
||||
@@ -13,7 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.dcr;
|
||||
package sample.registration;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -23,21 +25,21 @@ import org.springframework.security.oauth2.server.authorization.client.InMemoryR
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Configuration
|
||||
public class RegisteredClientConfiguration {
|
||||
@Bean // <1>
|
||||
public class ClientConfig {
|
||||
|
||||
@Bean
|
||||
public RegisteredClientRepository registeredClientRepository() {
|
||||
RegisteredClient initialClient = RegisteredClient.withId(UUID.randomUUID().toString())
|
||||
.clientId("dcr-client") // <2>
|
||||
RegisteredClient registrarClient = RegisteredClient.withId(UUID.randomUUID().toString())
|
||||
.clientId("registrar-client")
|
||||
.clientSecret("{noop}secret")
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
|
||||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) // <3>
|
||||
.scope("client.create") // <4>
|
||||
.scope("client.read") // <5>
|
||||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) // <1>
|
||||
.scope("client.create") // <2>
|
||||
.scope("client.read") // <3>
|
||||
.build();
|
||||
|
||||
return new InMemoryRegisteredClientRepository(initialClient); // <6>
|
||||
return new InMemoryRegisteredClientRepository(registrarClient);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,56 +13,58 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.dcr;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
package sample.registration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DcrClient {
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
public class ClientRegistrar {
|
||||
// @fold:on
|
||||
private final WebClient webClient;
|
||||
|
||||
public DcrClient(final WebClient webClient) {
|
||||
public ClientRegistrar(WebClient webClient) {
|
||||
this.webClient = webClient;
|
||||
}
|
||||
// @fold:off
|
||||
|
||||
public record DcrRequest( // <1>
|
||||
public record ClientRegistrationRequest( // <1>
|
||||
@JsonProperty("client_name") String clientName,
|
||||
@JsonProperty("grant_types") List<String> grantTypes,
|
||||
@JsonProperty("redirect_uris") List<String> redirectUris,
|
||||
String scope) {
|
||||
}
|
||||
|
||||
public record DcrResponse( // <2>
|
||||
public record ClientRegistrationResponse( // <2>
|
||||
@JsonProperty("registration_access_token") String registrationAccessToken,
|
||||
@JsonProperty("registration_client_uri") String registrationClientUri,
|
||||
@JsonProperty("client_name") String clientName,
|
||||
@JsonProperty("client_id") String clientId,
|
||||
@JsonProperty("client_secret") String clientSecret,
|
||||
@JsonProperty("grant_types") List<String> grantTypes,
|
||||
@JsonProperty("redirect_uris") List<String> redirectUris,
|
||||
String scope) {
|
||||
}
|
||||
|
||||
public static final DcrRequest SAMPLE_CLIENT_REGISTRATION_REQUEST = new DcrRequest( // <3>
|
||||
"client-1",
|
||||
List.of(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
|
||||
List.of("https://client.example.org/callback", "https://client.example.org/callback2"),
|
||||
"openid email profile"
|
||||
);
|
||||
public void exampleRegistration(String initialAccessToken) { // <3>
|
||||
ClientRegistrationRequest clientRegistrationRequest = new ClientRegistrationRequest( // <4>
|
||||
"client-1",
|
||||
List.of(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
|
||||
List.of("https://client.example.org/callback", "https://client.example.org/callback2"),
|
||||
"openid email profile"
|
||||
);
|
||||
|
||||
public void exampleRegistration(String initialAccessToken) { // <4>
|
||||
DcrResponse clientRegistrationResponse =
|
||||
this.registerClient(initialAccessToken, SAMPLE_CLIENT_REGISTRATION_REQUEST); // <5>
|
||||
ClientRegistrationResponse clientRegistrationResponse =
|
||||
registerClient(initialAccessToken, clientRegistrationRequest); // <5>
|
||||
|
||||
assert (clientRegistrationResponse.clientName().contentEquals("client-1")); // <6>
|
||||
assert (clientRegistrationResponse.clientName().contentEquals("client-1")); // <6>
|
||||
assert (!Objects.isNull(clientRegistrationResponse.clientSecret()));
|
||||
assert (clientRegistrationResponse.scope().contentEquals("openid profile email"));
|
||||
assert (clientRegistrationResponse.grantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()));
|
||||
@@ -71,12 +73,13 @@ public class DcrClient {
|
||||
assert (!clientRegistrationResponse.registrationAccessToken().isEmpty());
|
||||
assert (!clientRegistrationResponse.registrationClientUri().isEmpty());
|
||||
|
||||
String registrationAccessToken = clientRegistrationResponse.registrationAccessToken(); // <7>
|
||||
String registrationAccessToken = clientRegistrationResponse.registrationAccessToken(); // <7>
|
||||
String registrationClientUri = clientRegistrationResponse.registrationClientUri();
|
||||
|
||||
DcrResponse retrievedClient = this.retrieveClient(registrationAccessToken, registrationClientUri); // <8>
|
||||
ClientRegistrationResponse retrievedClient = retrieveClient(registrationAccessToken, registrationClientUri); // <8>
|
||||
|
||||
assert (retrievedClient.clientName().contentEquals("client-1")); // <9>
|
||||
assert (retrievedClient.clientName().contentEquals("client-1")); // <9>
|
||||
assert (!Objects.isNull(retrievedClient.clientId()));
|
||||
assert (!Objects.isNull(retrievedClient.clientSecret()));
|
||||
assert (retrievedClient.scope().contentEquals("openid profile email"));
|
||||
assert (retrievedClient.grantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()));
|
||||
@@ -86,26 +89,27 @@ public class DcrClient {
|
||||
assert (!retrievedClient.registrationClientUri().isEmpty());
|
||||
}
|
||||
|
||||
public DcrResponse registerClient(String initialAccessToken, DcrRequest request) { // <10>
|
||||
public ClientRegistrationResponse registerClient(String initialAccessToken, ClientRegistrationRequest request) { // <10>
|
||||
return this.webClient
|
||||
.post()
|
||||
.uri("/connect/register")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer %s".formatted(initialAccessToken))
|
||||
.body(Mono.just(request), DcrRequest.class)
|
||||
.body(Mono.just(request), ClientRegistrationRequest.class)
|
||||
.retrieve()
|
||||
.bodyToMono(DcrResponse.class)
|
||||
.bodyToMono(ClientRegistrationResponse.class)
|
||||
.block();
|
||||
}
|
||||
|
||||
public DcrResponse retrieveClient(String registrationAccessToken, String registrationClientUri) { // <11>
|
||||
public ClientRegistrationResponse retrieveClient(String registrationAccessToken, String registrationClientUri) { // <11>
|
||||
return this.webClient
|
||||
.get()
|
||||
.uri(registrationClientUri)
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer %s".formatted(registrationAccessToken))
|
||||
.retrieve()
|
||||
.bodyToMono(DcrResponse.class)
|
||||
.bodyToMono(ClientRegistrationResponse.class)
|
||||
.block();
|
||||
}
|
||||
|
||||
}
|
||||
42
docs/src/main/java/sample/registration/SecurityConfig.java
Normal file
42
docs/src/main/java/sample/registration/SecurityConfig.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020-2023 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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 sample.registration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
|
||||
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
|
||||
.oidc(oidc -> oidc.clientRegistrationEndpoint(Customizer.withDefaults())); // <1>
|
||||
http.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer.jwt(Customizer.withDefaults()));
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,19 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package sample.dcr;
|
||||
package sample.registration;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
@@ -34,9 +37,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for Dynamic Client Registration how-to guide
|
||||
* Tests for Dynamic Client Registration how-to guide.
|
||||
*
|
||||
* @author Dmitriy Dubson
|
||||
*/
|
||||
@@ -54,12 +56,12 @@ public class DynamicClientRegistrationTests {
|
||||
private String port;
|
||||
|
||||
@Test
|
||||
public void dynamicallyRegisterAClient() throws Exception {
|
||||
String tokenRequestBody = "scope=client.create&grant_type=client_credentials" ;
|
||||
public void dynamicallyRegisterClient() throws Exception {
|
||||
MockHttpServletResponse tokenResponse = this.mvc.perform(post("/oauth2/token")
|
||||
.with(httpBasic("dcr-client", "secret"))
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.content(tokenRequestBody))
|
||||
.with(httpBasic("registrar-client", "secret"))
|
||||
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
|
||||
.param(OAuth2ParameterNames.SCOPE, "client.create")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.access_token").isNotEmpty())
|
||||
.andReturn()
|
||||
@@ -67,15 +69,16 @@ public class DynamicClientRegistrationTests {
|
||||
|
||||
String initialAccessToken = JsonPath.parse(tokenResponse.getContentAsString()).read("$.access_token");
|
||||
|
||||
WebClient webClient = WebClient.builder().baseUrl("http://127.0.0.1:%s".formatted(port)).build();
|
||||
DcrClient dcrClient = new DcrClient(webClient);
|
||||
WebClient webClient = WebClient.builder().baseUrl("http://127.0.0.1:%s".formatted(this.port)).build();
|
||||
ClientRegistrar clientRegistrar = new ClientRegistrar(webClient);
|
||||
|
||||
dcrClient.exampleRegistration(initialAccessToken);
|
||||
clientRegistrar.exampleRegistration(initialAccessToken);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableWebSecurity
|
||||
@Import({DcrConfiguration.class, RegisteredClientConfiguration.class})
|
||||
@ComponentScan
|
||||
static class AuthorizationServerConfig {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user