|
| 1 | +[[how-to-dynamic-client-registration]] |
| 2 | += How-to: Register a client dynamically |
| 3 | +:index-link: ../how-to.html |
| 4 | +:docs-dir: .. |
| 5 | + |
| 6 | +This guide shows how to configure OpenID Connect Dynamic Client Registration 1.0 in Spring Authorization Server and |
| 7 | +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] |
| 8 | +specification, gaining the ability to dynamically register and retrieve OpenID clients. |
| 9 | + |
| 10 | +- xref:guides/how-to-dynamic-client-registration.adoc#enable[Enable Dynamic Client Registration in Spring Authorization Server] |
| 11 | +- xref:guides/how-to-dynamic-client-registration.adoc#configure-initial-client[Configure initial client] |
| 12 | +- xref:guides/how-to-dynamic-client-registration.adoc#fetch-initial-access-token[Fetch initial access token] |
| 13 | +- xref:guides/how-to-dynamic-client-registration.adoc#register-client[Register a client] |
| 14 | +- xref:guides/how-to-dynamic-client-registration.adoc#fetch-client[Fetch client] |
| 15 | + |
| 16 | +[[enable]] |
| 17 | +== Enable Dynamic Client Registration in Spring Authorization Server |
| 18 | + |
| 19 | +By default, dynamic client registration functionality is turned off in Spring Authorization Server. |
| 20 | +To enable, add the following configuration: |
| 21 | + |
| 22 | +[[sample.dcrAuthServerConfig]] |
| 23 | +[source,java] |
| 24 | +---- |
| 25 | +include::{examples-dir}/main/java/sample/dcr/DcrConfiguration.java[] |
| 26 | +---- |
| 27 | + |
| 28 | +<1> Add a `SecurityFilterChain` `@Bean` that registers an `OAuth2AuthorizationServerConfigurer` |
| 29 | +<2> In the configurer, apply OIDC client registration endpoint customizer with default values. |
| 30 | +This enables dynamic client registration functionality. |
| 31 | + |
| 32 | +Please refer to xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[Client Registration Endpoint docs] for |
| 33 | +in-depth configuration details. |
| 34 | + |
| 35 | +[[configure-initial-client]] |
| 36 | +== Configure initial client |
| 37 | + |
| 38 | +An initial client is required in order to register new clients in the authorization server. The client must be configured |
| 39 | +with scopes `client.create` and optionally `client.read` for creating clients and reading clients, respectively. |
| 40 | +A programmatic example of such a client is below. |
| 41 | + |
| 42 | +[[sample.dcrRegisteredClientConfig]] |
| 43 | +[source,java] |
| 44 | +---- |
| 45 | +include::{examples-dir}/main/java/sample/dcr/RegisteredClientConfiguration.java[] |
| 46 | +---- |
| 47 | + |
| 48 | +<1> A `RegisteredClientRepository` `@Bean` is configured with a set of clients. |
| 49 | +<2> An initial client with client id `initial-client` is configured. |
| 50 | +<3> `client_credentials` grant type is set to fetch access tokens directly. |
| 51 | +<4> `client.create` scope is configured for the client to ensure they are able to create clients. |
| 52 | +<5> `client.read` scope is configured for the client to ensure they are able to fetch and read clients. |
| 53 | +<6> The initial client is saved into the data store. |
| 54 | + |
| 55 | +After configuring the above, run the authorization server in your preferred environment. |
| 56 | + |
| 57 | +[[fetch-initial-access-token]] |
| 58 | +== Fetch initial access token |
| 59 | + |
| 60 | +An initial access token is required to be able to create client registration requests. The token request must contain a |
| 61 | +request for scope `client.create` only. |
| 62 | + |
| 63 | +[source,console] |
| 64 | +---- |
| 65 | +curl -X POST --location "https://authserver.example.org/oauth2/token" --http1.1 \ |
| 66 | + -H "Authorization: Basic <base64-encoded-credentials>" \ |
| 67 | + -H "Content-Type: application/x-www-form-urlencoded" \ |
| 68 | + -d "grant_type=client_credentials&scope=client.create" |
| 69 | +---- |
| 70 | + |
| 71 | +[WARNING] |
| 72 | +==== |
| 73 | +If you provide more than one scope in the request, you will not be able to register a client. The client creation |
| 74 | +request requires an access token with a single scope of `client.create` |
| 75 | +==== |
| 76 | + |
| 77 | +[TIP] |
| 78 | +==== |
| 79 | +To obtain encoded credentials for the above request, `base64` encode the client credentials in the format of |
| 80 | +`<clientId>:<clientSecret>`. Below is an encoding operation for the example in this guide. |
| 81 | +
|
| 82 | +[source,console] |
| 83 | +---- |
| 84 | +echo -n "initial-app:secret" | base64 |
| 85 | +---- |
| 86 | +==== |
| 87 | + |
| 88 | +[[register-client]] |
| 89 | +== Register a client |
| 90 | + |
| 91 | +With an access token obtained from the previous step, register a new client with the following request. |
| 92 | + |
| 93 | +[NOTE] |
| 94 | +The access token can only be used once. After a single registration request, the access token is invalidated. |
| 95 | + |
| 96 | +[source,console] |
| 97 | +---- |
| 98 | +curl -X POST --location "https://authserver.example.org/connect/register" --http1.1 \ |
| 99 | + -H "Content-Type: application/json" \ |
| 100 | + -H "Accept: application/json" \ |
| 101 | + -H "Authorization: Bearer <initial-access-token>" \ |
| 102 | + -d "{ |
| 103 | + \"client_name\": \"My Example\", |
| 104 | + \"grant_types\": [ |
| 105 | + \"authorization_code\", |
| 106 | + \"client_credentials\", |
| 107 | + \"refresh_token\" |
| 108 | + ], |
| 109 | + \"scope\": \"openid profile email\", |
| 110 | + \"redirect_uris\": [ |
| 111 | + \"https://client.example.org/callback\", |
| 112 | + \"https://client.example.org/callback2\" |
| 113 | + ], |
| 114 | + \"token_endpoint_auth_method\": \"client_secret_basic\", |
| 115 | + \"post_logout_redirect_uris\": [ |
| 116 | + \"https://client.example.org/logout\" |
| 117 | + ] |
| 118 | + }" |
| 119 | +---- |
| 120 | + |
| 121 | +An example register client response may be as follows: |
| 122 | + |
| 123 | +[source,console] |
| 124 | +---- |
| 125 | +HTTP/1.1 201 |
| 126 | +... |
| 127 | +
|
| 128 | +{ |
| 129 | + "client_id": "Q_AQ0wUzbTXo-IE4rNJbU9Dv8BBex1zQrjDeJs0mDbM", |
| 130 | + "client_id_issued_at": 1690726915, |
| 131 | + "client_name": "My Example", |
| 132 | + "client_secret": "XleADJhomxA2Rmyom2hmpnS6_CDnyAFBI9JsGeC-XQ0QLa9p9JExXJABiYz7fOXA", |
| 133 | + "redirect_uris": [ |
| 134 | + "https://client.example.org/callback", |
| 135 | + "https://client.example.org/callback2" |
| 136 | + ], |
| 137 | + "post_logout_redirect_uris": [ |
| 138 | + "https://client.example.org/logout" |
| 139 | + ], |
| 140 | + "grant_types": [ |
| 141 | + "refresh_token", |
| 142 | + "client_credentials", |
| 143 | + "authorization_code" |
| 144 | + ], |
| 145 | + "response_types": [ |
| 146 | + "code" |
| 147 | + ], |
| 148 | + "scope": "openid profile email", |
| 149 | + "token_endpoint_auth_method": "client_secret_basic", |
| 150 | + "id_token_signed_response_alg": "RS256", |
| 151 | + "registration_client_uri": "https://authserver.example.org/connect/register?client_id=Q_AQ0wUzbTXo-IE4rNJbU9Dv8BBex1zQrjDeJs0mDbM", |
| 152 | + "registration_access_token": "<access-token>", |
| 153 | + "client_secret_expires_at": 0 |
| 154 | +} |
| 155 | +---- |
| 156 | + |
| 157 | +With the client registered, a `registration_access_token` and a `registration_client_uri` are provided to be able to |
| 158 | +read the created client in a follow up request. The next step is optional. |
| 159 | + |
| 160 | +[[fetch-client]] |
| 161 | +== Fetch client |
| 162 | + |
| 163 | +Using fields `registration_access_token` and `registration_client_uri` from the previous step's response, read the client |
| 164 | +with the following request: |
| 165 | + |
| 166 | +[source,console] |
| 167 | +---- |
| 168 | +curl -X GET --location "<registration_client_uri>" \ |
| 169 | + -H "Authorization: Bearer <registration_access_token>" \ |
| 170 | + -H "Accept: application/json" |
| 171 | +---- |
| 172 | + |
| 173 | +The response should contain the same information about the client as seen when the client was first registered, with |
| 174 | +the exception of `registration_access_token` field. |
0 commit comments