Refresh Getting Started example

Closes gh-1186
This commit is contained in:
Steve Riesenberg
2023-05-02 14:42:15 -05:00
parent 697d6e91b6
commit 2814bc31b8
5 changed files with 81 additions and 68 deletions

View File

@@ -32,7 +32,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -52,6 +52,7 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean // <1>
@@ -70,7 +71,7 @@ public class SecurityConfig {
new LoginUrlAuthenticationEntryPoint("/login"))
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
// @formatter:on
return http.build();
@@ -109,26 +110,21 @@ public class SecurityConfig {
@Bean // <4>
public RegisteredClientRepository registeredClientRepository() {
// @formatter:off
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("messaging-client")
RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oidc-client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc")
.redirectUri("http://127.0.0.1:8080/authorized")
.postLogoutRedirectUri("http://127.0.0.1:8080/index")
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
.postLogoutRedirectUri("http://127.0.0.1:8080/")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
// @formatter:on
return new InMemoryRegisteredClientRepository(registeredClient);
return new InMemoryRegisteredClientRepository(oidcClient);
}
@Bean // <5>

View File

@@ -0,0 +1,29 @@
server:
port: 9000
logging:
level:
org.springframework.security: trace
spring:
security:
oauth2:
authorizationserver:
client:
oidc-client:
registration:
client-id: "oidc-client"
client-secret: "{noop}secret"
client-authentication-methods:
- "client_secret_basic"
authorization-grant-types:
- "authorization_code"
- "refresh_token"
redirect-uris:
- "http://127.0.0.1:8080/login/oauth2/code/oidc-client"
post-logout-redirect-uris:
- "http://127.0.0.1:8080/"
scopes:
- "openid"
- "profile"
require-authorization-consent: true

View File

@@ -127,7 +127,7 @@ public class AuthorizationCodeGrantFlow {
.andReturn();
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
assertThat(redirectedUrl).isNotNull();
assertThat(redirectedUrl).matches("http://127.0.0.1:8080/authorized\\?code=.{15,}&state=state");
assertThat(redirectedUrl).matches("http://127.0.0.1:8080/\\S+\\?code=.{15,}&state=state");
String locationHeader = URLDecoder.decode(redirectedUrl, StandardCharsets.UTF_8.name());
UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();

View File

@@ -21,7 +21,6 @@ import org.assertj.core.api.ObjectAssert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import sample.AuthorizationCodeGrantFlow;
import sample.DeviceAuthorizationGrantFlow;
import sample.test.SpringTestContext;
import sample.test.SpringTestContextExtension;
@@ -32,6 +31,7 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;
@@ -77,13 +77,13 @@ public class SecurityConfigTests {
assertThat(this.authorizationService).isInstanceOf(InMemoryOAuth2AuthorizationService.class);
assertThat(this.authorizationConsentService).isInstanceOf(InMemoryOAuth2AuthorizationConsentService.class);
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId("messaging-client");
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId("oidc-client");
assertThat(registeredClient).isNotNull();
AuthorizationCodeGrantFlow authorizationCodeGrantFlow = new AuthorizationCodeGrantFlow(this.mockMvc);
authorizationCodeGrantFlow.setUsername("user");
authorizationCodeGrantFlow.addScope("message.read");
authorizationCodeGrantFlow.addScope("message.write");
authorizationCodeGrantFlow.addScope(OidcScopes.OPENID);
authorizationCodeGrantFlow.addScope(OidcScopes.PROFILE);
String state = authorizationCodeGrantFlow.authorize(registeredClient);
assertThatAuthorization(state, OAuth2ParameterNames.STATE).isNotNull();
@@ -117,53 +117,6 @@ public class SecurityConfigTests {
StringUtils.delimitedListToStringArray(scopes, " "));
}
@Test
public void deviceAuthorizationWhenGettingStartedConfigUsedThenSuccess() throws Exception {
this.spring.register(AuthorizationServerConfig.class).autowire();
assertThat(this.registeredClientRepository).isInstanceOf(InMemoryRegisteredClientRepository.class);
assertThat(this.authorizationService).isInstanceOf(InMemoryOAuth2AuthorizationService.class);
assertThat(this.authorizationConsentService).isInstanceOf(InMemoryOAuth2AuthorizationConsentService.class);
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId("messaging-client");
assertThat(registeredClient).isNotNull();
DeviceAuthorizationGrantFlow deviceAuthorizationGrantFlow = new DeviceAuthorizationGrantFlow(this.mockMvc);
deviceAuthorizationGrantFlow.setUsername("user");
deviceAuthorizationGrantFlow.addScope("message.read");
deviceAuthorizationGrantFlow.addScope("message.write");
Map<String, Object> deviceAuthorizationResponse = deviceAuthorizationGrantFlow.authorize(registeredClient);
String userCode = (String) deviceAuthorizationResponse.get(OAuth2ParameterNames.USER_CODE);
assertThatAuthorization(userCode, OAuth2ParameterNames.USER_CODE).isNotNull();
assertThatAuthorization(userCode, null).isNotNull();
String deviceCode = (String) deviceAuthorizationResponse.get(OAuth2ParameterNames.DEVICE_CODE);
assertThatAuthorization(deviceCode, OAuth2ParameterNames.DEVICE_CODE).isNotNull();
assertThatAuthorization(deviceCode, null).isNotNull();
String state = deviceAuthorizationGrantFlow.submitCode(userCode);
assertThatAuthorization(state, OAuth2ParameterNames.STATE).isNotNull();
assertThatAuthorization(state, null).isNotNull();
deviceAuthorizationGrantFlow.submitConsent(registeredClient, state, userCode);
Map<String, Object> tokenResponse = deviceAuthorizationGrantFlow.getTokenResponse(registeredClient, deviceCode);
String accessToken = (String) tokenResponse.get(OAuth2ParameterNames.ACCESS_TOKEN);
assertThatAuthorization(accessToken, OAuth2ParameterNames.ACCESS_TOKEN).isNotNull();
assertThatAuthorization(accessToken, null).isNotNull();
String refreshToken = (String) tokenResponse.get(OAuth2ParameterNames.REFRESH_TOKEN);
assertThatAuthorization(refreshToken, OAuth2ParameterNames.REFRESH_TOKEN).isNotNull();
assertThatAuthorization(refreshToken, null).isNotNull();
String scopes = (String) tokenResponse.get(OAuth2ParameterNames.SCOPE);
OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById(
registeredClient.getId(), "user");
assertThat(authorizationConsent).isNotNull();
assertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrder(
StringUtils.delimitedListToStringArray(scopes, " "));
}
private ObjectAssert<OAuth2Authorization> assertThatAuthorization(String token, String tokenType) {
return assertThat(findAuthorization(token, tokenType));
}

View File

@@ -15,7 +15,28 @@ Spring Authorization Server can be used anywhere you already use https://docs.sp
The easiest way to begin using Spring Authorization Server is by creating a https://spring.io/projects/spring-boot[Spring Boot]-based application.
You can use https://start.spring.io[start.spring.io] to generate a basic project or use the https://github.com/spring-projects/spring-authorization-server/tree/main/samples/default-authorizationserver[default authorization server sample] as a guide.
Then add Spring Authorization Server as a dependency, as in the following example:
Then add Spring Boot's starter for Spring Authorization Server as a dependency:
[[spring-boot-maven-dependency]]
.Maven
[source,xml,role="primary",subs="attributes,verbatim"]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
----
[[spring-boot-gradle-dependency]]
.Gradle
[source,gradle,role="secondary",subs="attributes,verbatim"]
----
implementation "org.springframework.boot:spring-boot-starter-oauth2-authorization-server"
----
TIP: See https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.installing[Installing Spring Boot] for more information on using Spring Boot with Maven or Gradle.
Alternatively, you can add Spring Authorization Server without Spring Boot using the following example:
[[maven-dependency]]
.Maven
@@ -35,15 +56,29 @@ Then add Spring Authorization Server as a dependency, as in the following exampl
implementation "org.springframework.security:spring-security-oauth2-authorization-server:{spring-authorization-server-version}"
----
TIP: See https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.installing[Installing Spring Boot] for more information on using Spring Boot with Maven or Gradle.
[[developing-your-first-application]]
== Developing Your First Application
To get started, you need the minimum required components defined as a `@Bean` in a Spring `@Configuration`. These components can be defined as follows:
To get started, you need the minimum required components defined as a `@Bean`. When using the `spring-boot-starter-oauth2-authorization-server` dependency, define the following properties and Spring Boot will provide the necessary `@Bean` definitions for you:
[[application-yml]]
.application.yml
[source,yaml]
----
include::{docs-java}/sample/gettingStarted/application.yml[]
----
TIP: Beyond the Getting Started experience, most users will want to customize the default configuration. The <<defining-required-components,next section>> demonstrates providing all of the necessary beans yourself.
[[defining-required-components]]
== Defining Required Components
If you want to customize the default configuration (regardless of whether you're using Spring Boot), you can define the minimum required components as a `@Bean` in a Spring `@Configuration`.
TIP: To skip the setup and run a working example, see the https://github.com/spring-projects/spring-authorization-server/tree/main/samples/default-authorizationserver[default authorization server sample].
These components can be defined as follows:
[[sample.gettingStarted]]
include::code:SecurityConfig[]