diff --git a/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/SecurityConfig.java b/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/SecurityConfig.java index bd7e9d1a..3c1c9ef7 100644 --- a/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/SecurityConfig.java +++ b/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/SecurityConfig.java @@ -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> diff --git a/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/application.yml b/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/application.yml new file mode 100644 index 00000000..92283fad --- /dev/null +++ b/docs/src/docs/asciidoc/examples/src/main/java/sample/gettingStarted/application.yml @@ -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 diff --git a/docs/src/docs/asciidoc/examples/src/test/java/sample/AuthorizationCodeGrantFlow.java b/docs/src/docs/asciidoc/examples/src/test/java/sample/AuthorizationCodeGrantFlow.java index e58a9208..e66bb000 100644 --- a/docs/src/docs/asciidoc/examples/src/test/java/sample/AuthorizationCodeGrantFlow.java +++ b/docs/src/docs/asciidoc/examples/src/test/java/sample/AuthorizationCodeGrantFlow.java @@ -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(); diff --git a/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java b/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java index fe494967..cf827c3a 100644 --- a/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java +++ b/docs/src/docs/asciidoc/examples/src/test/java/sample/gettingStarted/SecurityConfigTests.java @@ -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 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 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 assertThatAuthorization(String token, String tokenType) { return assertThat(findAuthorization(token, tokenType)); } diff --git a/docs/src/docs/asciidoc/getting-started.adoc b/docs/src/docs/asciidoc/getting-started.adoc index d7e003d7..43b95f23 100644 --- a/docs/src/docs/asciidoc/getting-started.adoc +++ b/docs/src/docs/asciidoc/getting-started.adoc @@ -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"] +---- + + org.springframework.boot + spring-boot-starter-oauth2-authorization-server + +---- + +[[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 <> 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[]