From 24950258451f07ad03de0bb400b5bcb1d3888f25 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 6 Sep 2018 15:10:31 -0500 Subject: [PATCH] authcodegrant samples->oauth2webclient samples The authcodegrant samples were initially meant to be very simple demonstration of authorization code flow. However, it has become obvious since then that the real intent of the demo is how to use the WebClient with OAuth (there is no other reason to do authorization code flow unless you use the token to make a request). The samples have been migrated to oauth2webclient and oauth2webclient-webflux respectively. They have been improved: * The sample demonstrates usage with annotations, webclient directly, form login oauth2Login, and public APIs * The samples externalize the endpoint that is requested in the sample making it easier to try other endpoints * The UI no longer relies on a data structure for the result of the endpoint also making it easier to try other endpoints Issue: gh-4921 --- ...uthorizationCodeGrantApplicationTests.java | 57 ------ .../src/main/resources/application.yml | 23 --- .../resources/templates/github-repos.html | 26 --- ...uthorizationCodeGrantApplicationTests.java | 183 ------------------ .../src/main/resources/application.yml | 23 --- .../resources/templates/github-repos.html | 28 --- .../README.adoc | 19 +- ...mples-boot-oauth2webclient-webflux.gradle} | 1 + .../OAuth2WebClientWebFluxApplication.java} | 4 +- .../java/sample/config/SecurityConfig.java | 3 + .../java/sample/config/WebClientConfig.java | 5 +- .../main/java/sample/web/IndexController.java | 31 +++ .../web/OAuth2WebClientController.java} | 52 ++--- ...teredOAuth2AuthorizedClientController.java | 70 +++++++ .../src/main/resources/application.yml} | 13 +- .../src/main/resources/templates/index.html | 51 +++++ .../main/resources/templates/response.html | 31 +++ ...Auth2WebClientWebFluxApplicationTests.java | 43 ++++ .../README.adoc | 19 +- ...urity-samples-boot-oauth2webclient.gradle} | 1 + .../sample/OAuth2WebClientApplication.java} | 6 +- .../java/sample/config/SecurityConfig.java | 3 + .../java/sample/config/WebClientConfig.java | 8 +- .../main/java/sample/web/IndexController.java | 31 +++ .../web/OAuth2WebClientController.java} | 53 ++--- ...teredOAuth2AuthorizedClientController.java | 70 +++++++ .../src/main/resources/application.yml | 21 ++ .../src/main/resources/templates/index.html | 51 +++++ .../main/resources/templates/response.html | 31 +++ .../OAuth2WebClientApplicationTests.java | 46 +++++ 30 files changed, 582 insertions(+), 421 deletions(-) delete mode 100644 samples/boot/authcodegrant-webflux/src/integration-test/java/sample/OAuth2AuthorizationCodeGrantApplicationTests.java delete mode 100644 samples/boot/authcodegrant-webflux/src/main/resources/application.yml delete mode 100644 samples/boot/authcodegrant-webflux/src/main/resources/templates/github-repos.html delete mode 100644 samples/boot/authcodegrant/src/integration-test/java/org/springframework/security/samples/OAuth2AuthorizationCodeGrantApplicationTests.java delete mode 100644 samples/boot/authcodegrant/src/main/resources/application.yml delete mode 100644 samples/boot/authcodegrant/src/main/resources/templates/github-repos.html rename samples/boot/{authcodegrant-webflux => oauth2webclient-webflux}/README.adoc (78%) rename samples/boot/{authcodegrant-webflux/spring-security-samples-boot-authcodegrant-webflux.gradle => oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle} (91%) rename samples/boot/{authcodegrant/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java => oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java} (86%) rename samples/boot/{authcodegrant-webflux => oauth2webclient-webflux}/src/main/java/sample/config/SecurityConfig.java (96%) rename samples/boot/{authcodegrant-webflux => oauth2webclient-webflux}/src/main/java/sample/config/WebClientConfig.java (85%) create mode 100644 samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java rename samples/boot/{authcodegrant-webflux/src/main/java/sample/web/GitHubReposController.java => oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java} (55%) create mode 100644 samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java rename samples/boot/{authcodegrant-webflux/src/integration-test/resources/application-test.yml => oauth2webclient-webflux/src/main/resources/application.yml} (51%) create mode 100644 samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html create mode 100644 samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html create mode 100644 samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java rename samples/boot/{authcodegrant => oauth2webclient}/README.adoc (78%) rename samples/boot/{authcodegrant/spring-security-samples-boot-authcodegrant.gradle => oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle} (92%) rename samples/boot/{authcodegrant-webflux/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java => oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java} (84%) rename samples/boot/{authcodegrant => oauth2webclient}/src/main/java/sample/config/SecurityConfig.java (96%) rename samples/boot/{authcodegrant => oauth2webclient}/src/main/java/sample/config/WebClientConfig.java (65%) create mode 100644 samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java rename samples/boot/{authcodegrant/src/main/java/sample/web/GitHubReposController.java => oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java} (55%) create mode 100644 samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java create mode 100644 samples/boot/oauth2webclient/src/main/resources/application.yml create mode 100644 samples/boot/oauth2webclient/src/main/resources/templates/index.html create mode 100644 samples/boot/oauth2webclient/src/main/resources/templates/response.html create mode 100644 samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java diff --git a/samples/boot/authcodegrant-webflux/src/integration-test/java/sample/OAuth2AuthorizationCodeGrantApplicationTests.java b/samples/boot/authcodegrant-webflux/src/integration-test/java/sample/OAuth2AuthorizationCodeGrantApplicationTests.java deleted file mode 100644 index 7bf2cfee12..0000000000 --- a/samples/boot/authcodegrant-webflux/src/integration-test/java/sample/OAuth2AuthorizationCodeGrantApplicationTests.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2002-2018 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 - * - * 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 sample; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.reactive.server.WebTestClient; - -/** - * Integration tests for the OAuth 2.0 client filters {@link OAuth2AuthorizationRequestRedirectFilter} - * and {@link OAuth2AuthorizationCodeGrantFilter}. These filters work together to realize - * the OAuth 2.0 Authorization Code Grant flow. - * - * @author Joe Grandja - * @since 5.1 - */ -@SpringBootTest -@ActiveProfiles("test") -@AutoConfigureWebTestClient -@RunWith(SpringRunner.class) -public class OAuth2AuthorizationCodeGrantApplicationTests { - @Autowired - private WebTestClient rest; - - @Test - @WithMockUser - public void requestWhenClientNotAuthorizedThenRedirectForAuthorization() throws Exception { - this.rest.get() - .uri("http://localhost/repos") - .exchange() - .expectStatus().is3xxRedirection() - .expectHeader().valueMatches(HttpHeaders.LOCATION, "https://github.com/login/oauth/authorize\\?response_type=code&client_id=client-id&scope=public_repo&state=.{15,}&redirect_uri=http%3A%2F%2Flocalhost%2Fauthorize%2Foauth2%2Fcode%2Fgithub"); - } - -} diff --git a/samples/boot/authcodegrant-webflux/src/main/resources/application.yml b/samples/boot/authcodegrant-webflux/src/main/resources/application.yml deleted file mode 100644 index c04b496227..0000000000 --- a/samples/boot/authcodegrant-webflux/src/main/resources/application.yml +++ /dev/null @@ -1,23 +0,0 @@ -server: - port: 8080 - -logging: - level: - root: INFO - org.springframework.web: INFO - org.springframework.security: INFO -# org.springframework.boot.autoconfigure: DEBUG - -spring: - thymeleaf: - cache: false - security: - oauth2: - client: - registration: - github: - client-id: your-app-client-id - client-secret: your-app-client-secret - scope: public_repo - redirect-uri-template: "{baseUrl}/authorize/oauth2/code/github" - client-name: GitHub Repositories diff --git a/samples/boot/authcodegrant-webflux/src/main/resources/templates/github-repos.html b/samples/boot/authcodegrant-webflux/src/main/resources/templates/github-repos.html deleted file mode 100644 index 1ea746b53b..0000000000 --- a/samples/boot/authcodegrant-webflux/src/main/resources/templates/github-repos.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - Spring Security - OAuth 2.0 Authorization Code Grant - - - -
-
- User: -
-
 
-
- Log Out -
-
-

GitHub Repositories

-
- -
- - diff --git a/samples/boot/authcodegrant/src/integration-test/java/org/springframework/security/samples/OAuth2AuthorizationCodeGrantApplicationTests.java b/samples/boot/authcodegrant/src/integration-test/java/org/springframework/security/samples/OAuth2AuthorizationCodeGrantApplicationTests.java deleted file mode 100644 index f73496c703..0000000000 --- a/samples/boot/authcodegrant/src/integration-test/java/org/springframework/security/samples/OAuth2AuthorizationCodeGrantApplicationTests.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2002-2018 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 - * - * 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.springframework.security.samples; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringBootConfiguration; -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.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Import; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockHttpSession; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; -import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; -import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; -import org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import sample.config.WebClientConfig; - -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Integration tests for the OAuth 2.0 client filters {@link OAuth2AuthorizationRequestRedirectFilter} - * and {@link OAuth2AuthorizationCodeGrantFilter}. These filters work together to realize - * the OAuth 2.0 Authorization Code Grant flow. - * - * @author Joe Grandja - * @since 5.1 - */ -@RunWith(SpringRunner.class) -@SpringBootTest -@AutoConfigureMockMvc -public class OAuth2AuthorizationCodeGrantApplicationTests { - @Autowired - private ClientRegistrationRepository clientRegistrationRepository; - - @Autowired - private OAuth2AuthorizedClientRepository authorizedClientRepository; - - @Autowired - private MockMvc mockMvc; - - @Test - public void requestWhenClientNotAuthorizedThenRedirectForAuthorization() throws Exception { - MvcResult mvcResult = this.mockMvc.perform(get("/repos").with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); - assertThat(mvcResult.getResponse().getRedirectedUrl()).matches("https://github.com/login/oauth/authorize\\?response_type=code&client_id=your-app-client-id&scope=public_repo&state=.{15,}&redirect_uri=http%3A%2F%2Flocalhost%2Fgithub-repos"); - } - - @Test - @DirtiesContext - public void requestWhenClientGrantedAuthorizationThenAuthorizedClientSaved() throws Exception { - // Setup the Authorization Request in the session - ClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId("github"); - Map additionalParameters = new HashMap<>(); - additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID, registration.getRegistrationId()); - OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode() - .authorizationUri(registration.getProviderDetails().getAuthorizationUri()) - .clientId(registration.getClientId()) - .redirectUri("http://localhost/github-repos") - .scopes(registration.getScopes()) - .state("state") - .additionalParameters(additionalParameters) - .build(); - - AuthorizationRequestRepository authorizationRequestRepository = - new HttpSessionOAuth2AuthorizationRequestRepository(); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response); - - MockHttpSession session = (MockHttpSession) request.getSession(); - - String principalName = "user"; - TestingAuthenticationToken authentication = new TestingAuthenticationToken(principalName, "password"); - - // Authorization Response - this.mockMvc.perform(get("/github-repos") - .param(OAuth2ParameterNames.CODE, "code") - .param(OAuth2ParameterNames.STATE, "state") - .with(authentication(authentication)) - .session(session)) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/github-repos")); - - OAuth2AuthorizedClient authorizedClient = this.authorizedClientRepository.loadAuthorizedClient( - registration.getRegistrationId(), authentication, request); - assertThat(authorizedClient).isNotNull(); - } - - @EnableWebSecurity - static class OAuth2ClientConfig extends WebSecurityConfigurerAdapter { - // @formatter:off - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .oauth2Client() - .authorizationCodeGrant() - .accessTokenResponseClient(this.accessTokenResponseClient()); - } - // @formatter:on - - private OAuth2AccessTokenResponseClient accessTokenResponseClient() { - OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken("access-token-1234") - .tokenType(OAuth2AccessToken.TokenType.BEARER) - .expiresIn(60 * 1000) - .build(); - OAuth2AccessTokenResponseClient tokenResponseClient = mock(OAuth2AccessTokenResponseClient.class); - when(tokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse); - return tokenResponseClient; - } - } - - @SpringBootConfiguration - @EnableAutoConfiguration - @ComponentScan(basePackages = "sample.web") - @Import(WebClientConfig.class) - public static class SpringBootApplicationTestConfig { - - @Bean - public OAuth2AuthorizedClientService authorizedClientService(ClientRegistrationRepository clientRegistrationRepository) { - return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository); - } - - @Bean - public OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) { - return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService); - } - } -} diff --git a/samples/boot/authcodegrant/src/main/resources/application.yml b/samples/boot/authcodegrant/src/main/resources/application.yml deleted file mode 100644 index 9ec59b1a1a..0000000000 --- a/samples/boot/authcodegrant/src/main/resources/application.yml +++ /dev/null @@ -1,23 +0,0 @@ -server: - port: 8080 - -logging: - level: - root: INFO - org.springframework.web: INFO - org.springframework.security: INFO -# org.springframework.boot.autoconfigure: DEBUG - -spring: - thymeleaf: - cache: false - security: - oauth2: - client: - registration: - github: - client-id: your-app-client-id - client-secret: your-app-client-secret - scope: public_repo - redirect-uri-template: "{baseUrl}/github-repos" - client-name: GitHub Repositories diff --git a/samples/boot/authcodegrant/src/main/resources/templates/github-repos.html b/samples/boot/authcodegrant/src/main/resources/templates/github-repos.html deleted file mode 100644 index 6cdd27bd89..0000000000 --- a/samples/boot/authcodegrant/src/main/resources/templates/github-repos.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - Spring Security - OAuth 2.0 Authorization Code Grant - - - -
-
- User: -
-
 
-
-
- -
-
-
-

GitHub Repositories

-
-
    -
  • - : -
  • -
-
- - diff --git a/samples/boot/authcodegrant-webflux/README.adoc b/samples/boot/oauth2webclient-webflux/README.adoc similarity index 78% rename from samples/boot/authcodegrant-webflux/README.adoc rename to samples/boot/oauth2webclient-webflux/README.adoc index 36920acc46..a9778ca442 100644 --- a/samples/boot/authcodegrant-webflux/README.adoc +++ b/samples/boot/oauth2webclient-webflux/README.adoc @@ -1,8 +1,8 @@ -= OAuth 2.0 Authorization Code Grant Sample += OAuth 2.0 WebClient (WebFlux) Sample == GitHub Repositories -This guide provides instructions on setting up the sample application, which leverages the OAuth 2.0 Authorization Code Grant, and displays a list of public GitHub repositories that are accessible to the authenticated user. +This guide provides instructions on setting up the sample application, which leverages WebClient OAuth2 integration to display a list of public GitHub repositories that are accessible to the authenticated user. This includes repositories owned by the authenticated user, repositories where the authenticated user is a collaborator, and repositories that the authenticated user has access to through an organization membership. @@ -17,7 +17,7 @@ The following sections provide detailed steps for setting up the sample and cove To use GitHub's OAuth 2.0 authorization system, you must https://github.com/settings/applications/new[Register a new OAuth application]. -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/github-repos`. +When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/client-id`. The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub and have granted access to the OAuth application on the _Authorize application_ page. @@ -36,12 +36,11 @@ spring: oauth2: client: registration: <1> - github: <2> - client-id: github-client-id - client-secret: github-client-secret - scope: public_repo - redirect-uri-template: "{baseUrl}/github-repos" - client-name: GitHub Repositories + client-id: <2> + client-id: replace-with-client-id + client-secret: replace-with-client-secret + provider: github + scopes: read:user,public_repo ---- + .OAuth Client properties @@ -57,7 +56,7 @@ spring: Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. You are then redirected to the default _auto-generated_ form login page. -Log in using *'user'* (username) and *'password'* (password) and then you'll be redirected to GitHub for authentication. +Log in using *'user'* (username) and *'password'* (password) or click the link to authenticate with GitHub and then you'll be redirected to GitHub for authentication. After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". This page will ask you to *Authorize* the application you created in the previous step. diff --git a/samples/boot/authcodegrant-webflux/spring-security-samples-boot-authcodegrant-webflux.gradle b/samples/boot/oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle similarity index 91% rename from samples/boot/authcodegrant-webflux/spring-security-samples-boot-authcodegrant-webflux.gradle rename to samples/boot/oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle index 5c4ca44cbd..fd5ba979d9 100644 --- a/samples/boot/authcodegrant-webflux/spring-security-samples-boot-authcodegrant-webflux.gradle +++ b/samples/boot/oauth2webclient-webflux/spring-security-samples-boot-oauth2webclient-webflux.gradle @@ -3,6 +3,7 @@ apply plugin: 'io.spring.convention.spring-sample-boot' dependencies { compile project(':spring-security-config') compile project(':spring-security-oauth2-client') + compile project(':spring-security-oauth2-jose') compile 'org.springframework.boot:spring-boot-starter-thymeleaf' compile 'org.springframework.boot:spring-boot-starter-webflux' compile 'org.thymeleaf.extras:thymeleaf-extras-springsecurity4' diff --git a/samples/boot/authcodegrant/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java similarity index 86% rename from samples/boot/authcodegrant/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java rename to samples/boot/oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java index d16dc5ad73..0369108802 100644 --- a/samples/boot/authcodegrant/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java +++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/OAuth2WebClientWebFluxApplication.java @@ -22,9 +22,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * @author Joe Grandja */ @SpringBootApplication -public class OAuth2AuthorizationCodeGrantApplication { +public class OAuth2WebClientWebFluxApplication { public static void main(String[] args) { - SpringApplication.run(OAuth2AuthorizationCodeGrantApplication.class, args); + SpringApplication.run(OAuth2WebClientWebFluxApplication.class, args); } } diff --git a/samples/boot/authcodegrant-webflux/src/main/java/sample/config/SecurityConfig.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java similarity index 96% rename from samples/boot/authcodegrant-webflux/src/main/java/sample/config/SecurityConfig.java rename to samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java index 400d9008e4..c560daf308 100644 --- a/samples/boot/authcodegrant-webflux/src/main/java/sample/config/SecurityConfig.java +++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java @@ -36,8 +36,11 @@ public class SecurityConfig { SecurityWebFilterChain configure(ServerHttpSecurity http) throws Exception { http .authorizeExchange() + .pathMatchers("/", "/public/**").permitAll() .anyExchange().authenticated() .and() + .oauth2Login() + .and() .formLogin() .and() .oauth2Client(); diff --git a/samples/boot/authcodegrant-webflux/src/main/java/sample/config/WebClientConfig.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/WebClientConfig.java similarity index 85% rename from samples/boot/authcodegrant-webflux/src/main/java/sample/config/WebClientConfig.java rename to samples/boot/oauth2webclient-webflux/src/main/java/sample/config/WebClientConfig.java index 0574cf75e3..7e0706a939 100644 --- a/samples/boot/authcodegrant-webflux/src/main/java/sample/config/WebClientConfig.java +++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/WebClientConfig.java @@ -33,8 +33,11 @@ public class WebClientConfig { @Bean WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository, ServerOAuth2AuthorizedClientRepository authorizedClientRepository) { + ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = + new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository); + oauth.setDefaultOAuth2AuthorizedClient(true); return WebClient.builder() - .filter(new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository)) + .filter(oauth) .build(); } } diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java new file mode 100644 index 0000000000..94213d255c --- /dev/null +++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/IndexController.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2018 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 + * + * 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 sample.web; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * @author Rob Winch + */ +@Controller +public class IndexController { + @GetMapping("/") + String index() { + return "index"; + } +} diff --git a/samples/boot/authcodegrant-webflux/src/main/java/sample/web/GitHubReposController.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java similarity index 55% rename from samples/boot/authcodegrant-webflux/src/main/java/sample/web/GitHubReposController.java rename to samples/boot/oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java index 7bd8abd7ce..4a7bce2ed2 100644 --- a/samples/boot/authcodegrant-webflux/src/main/java/sample/web/GitHubReposController.java +++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/OAuth2WebClientController.java @@ -15,48 +15,52 @@ */ package sample.web; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; -import java.security.Principal; -import java.util.List; - -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; +import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId; /** * @author Joe Grandja * @author Rob Winch */ @Controller -public class GitHubReposController { +@RequestMapping(path = {"/webclient", "/public/webclient"}) +public class OAuth2WebClientController { private final WebClient webClient; - public GitHubReposController(WebClient webClient) { + private final String uri; + + public OAuth2WebClientController(WebClient webClient, @Value("${resource-uri}") String uri) { this.webClient = webClient; + this.uri = uri; } - @GetMapping("/") - public String index() { - return "redirect:/repos"; + @GetMapping("/explicit") + String explicit(Model model) { + Mono body = this.webClient + .get() + .uri(this.uri) + .attributes(clientRegistrationId("client-id")) + .retrieve() + .bodyToMono(String.class); + model.addAttribute("body", body); + return "response"; } - @GetMapping("/repos") - public String gitHubRepos(Model model, @RegisteredOAuth2AuthorizedClient("github") OAuth2AuthorizedClient authorizedClient, Principal principal) { - String endpointUri = "https://api.github.com/user/repos"; - Mono repos = this.webClient - .get() - .uri(endpointUri) - .attributes(oauth2AuthorizedClient(authorizedClient)) - .retrieve() - .bodyToMono(List.class); - model.addAttribute("repos", repos); - model.addAttribute("username", principal.getName()); - - return "github-repos"; + @GetMapping("/implicit") + String implicit(Model model) { + Mono body = this.webClient + .get() + .uri(this.uri) + .retrieve() + .bodyToMono(String.class); + model.addAttribute("body", body); + return "response"; } } diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java new file mode 100644 index 0000000000..a74e9b3071 --- /dev/null +++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2018 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 + * + * 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 sample.web; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; + +/** + * @author Joe Grandja + * @author Rob Winch + */ +@Controller +@RequestMapping(path = {"/annotation", "/public/annotation"}) +public class RegisteredOAuth2AuthorizedClientController { + + private final WebClient webClient; + + private final String uri; + + public RegisteredOAuth2AuthorizedClientController(WebClient webClient, @Value("${resource-uri}") String uri) { + this.webClient = webClient; + this.uri = uri; + } + + @GetMapping("/explicit") + String explicit(Model model, @RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) { + Mono body = this.webClient + .get() + .uri(this.uri) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .retrieve() + .bodyToMono(String.class); + model.addAttribute("body", body); + return "response"; + } + + @GetMapping("/implicit") + String implicit(Model model, @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) { + Mono body = this.webClient + .get() + .uri(this.uri) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .retrieve() + .bodyToMono(String.class); + model.addAttribute("body", body); + return "response"; + } +} diff --git a/samples/boot/authcodegrant-webflux/src/integration-test/resources/application-test.yml b/samples/boot/oauth2webclient-webflux/src/main/resources/application.yml similarity index 51% rename from samples/boot/authcodegrant-webflux/src/integration-test/resources/application-test.yml rename to samples/boot/oauth2webclient-webflux/src/main/resources/application.yml index 3a8852624d..7144c09a0f 100644 --- a/samples/boot/authcodegrant-webflux/src/integration-test/resources/application-test.yml +++ b/samples/boot/oauth2webclient-webflux/src/main/resources/application.yml @@ -12,9 +12,10 @@ spring: oauth2: client: registration: - github: - client-id: client-id - client-secret: client-secret - scope: public_repo - redirect-uri-template: "{baseUrl}/authorize/oauth2/code/github" - client-name: GitHub Repositories + client-id: + client-id: replace-with-client-id + client-secret: replace-with-client-secret + provider: github + scopes: read:user,public_repo + +resource-uri: https://api.github.com/user/repos diff --git a/samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html b/samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html new file mode 100644 index 0000000000..73eb0fb928 --- /dev/null +++ b/samples/boot/oauth2webclient-webflux/src/main/resources/templates/index.html @@ -0,0 +1,51 @@ + + + + OAuth2 WebClient Showcase + + + +Log Out +

Examples

+ +

@RegisteredOAuth2AuthorizedClient

+

+Examples on RegisteredOAuth2AuthorizedClientController +

Authenticated

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - Use the currently logged in user's OAuth Token. This will + only work if the user authenticates with oauth2Login and the token provided is the correct token provided at + log in is authorized.
  • +
+

Public

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - This will fail if the user is not authenticated. + Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then + authenticates with oauth2Login()
  • +
+ +

ServerOAuth2AuthorizedClientExchangeFilterFunction

+

+ Examples on OAuth2WebClientController that demonstrate how to use ServerOAuth2AuthorizedClientExchangeFilterFunction +

Authenticated

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - Use the currently logged in user's OAuth Token. This will + only work if the user authenticates with oauth2Login and the token provided is the correct token provided at + log in is authorized.
  • +
+

Public

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - This will fail if the user is not authenticated. + Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then + authenticates with oauth2Login()
  • +
+ + diff --git a/samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html b/samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html new file mode 100644 index 0000000000..ef5a21c74a --- /dev/null +++ b/samples/boot/oauth2webclient-webflux/src/main/resources/templates/response.html @@ -0,0 +1,31 @@ + + + + + + OAuth2 WebClient Showcase + + + +Back +

Response

+
+ + + diff --git a/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java b/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java new file mode 100644 index 0000000000..fb14cba5ab --- /dev/null +++ b/samples/boot/oauth2webclient-webflux/src/test/java/sample/OAuth2WebClientWebFluxApplicationTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2018 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 + * + * 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 sample; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * @author Rob Winch + */ +@SpringBootTest +@AutoConfigureWebTestClient +@RunWith(SpringRunner.class) +public class OAuth2WebClientWebFluxApplicationTests { + @Autowired + private WebTestClient client; + + @Test + public void annotationExplicitWhenNotAuthenticatedThenLoginRequested() { + this.client.get().uri("/annotation/explicit") + .exchange() + .expectStatus().is3xxRedirection(); + } +} diff --git a/samples/boot/authcodegrant/README.adoc b/samples/boot/oauth2webclient/README.adoc similarity index 78% rename from samples/boot/authcodegrant/README.adoc rename to samples/boot/oauth2webclient/README.adoc index 36920acc46..db032bde94 100644 --- a/samples/boot/authcodegrant/README.adoc +++ b/samples/boot/oauth2webclient/README.adoc @@ -1,8 +1,8 @@ -= OAuth 2.0 Authorization Code Grant Sample += OAuth 2.0 WebClient (Servlet) Sample == GitHub Repositories -This guide provides instructions on setting up the sample application, which leverages the OAuth 2.0 Authorization Code Grant, and displays a list of public GitHub repositories that are accessible to the authenticated user. +This guide provides instructions on setting up the sample application, which leverages WebClient OAuth2 integration to display a list of public GitHub repositories that are accessible to the authenticated user. This includes repositories owned by the authenticated user, repositories where the authenticated user is a collaborator, and repositories that the authenticated user has access to through an organization membership. @@ -17,7 +17,7 @@ The following sections provide detailed steps for setting up the sample and cove To use GitHub's OAuth 2.0 authorization system, you must https://github.com/settings/applications/new[Register a new OAuth application]. -When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/github-repos`. +When registering the OAuth application, ensure the *Authorization callback URL* is set to `http://localhost:8080/login/oauth2/code/client-id`. The Authorization callback URL (redirect URI) is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with GitHub and have granted access to the OAuth application on the _Authorize application_ page. @@ -36,12 +36,11 @@ spring: oauth2: client: registration: <1> - github: <2> - client-id: github-client-id - client-secret: github-client-secret - scope: public_repo - redirect-uri-template: "{baseUrl}/github-repos" - client-name: GitHub Repositories + client-id: <2> + client-id: replace-with-client-id + client-secret: replace-with-client-secret + provider: github + scopes: read:user,public_repo ---- + .OAuth Client properties @@ -57,7 +56,7 @@ spring: Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. You are then redirected to the default _auto-generated_ form login page. -Log in using *'user'* (username) and *'password'* (password) and then you'll be redirected to GitHub for authentication. +Log in using *'user'* (username) and *'password'* (password) or click the link to authenticate with GitHub and then you'll be redirected to GitHub for authentication. After authenticating with your GitHub credentials, the next page presented to you is "Authorize application". This page will ask you to *Authorize* the application you created in the previous step. diff --git a/samples/boot/authcodegrant/spring-security-samples-boot-authcodegrant.gradle b/samples/boot/oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle similarity index 92% rename from samples/boot/authcodegrant/spring-security-samples-boot-authcodegrant.gradle rename to samples/boot/oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle index dc46d8f456..b5ad5e1850 100644 --- a/samples/boot/authcodegrant/spring-security-samples-boot-authcodegrant.gradle +++ b/samples/boot/oauth2webclient/spring-security-samples-boot-oauth2webclient.gradle @@ -5,6 +5,7 @@ ext['thymeleaf.version'] = '3.0.9.RELEASE' dependencies { compile project(':spring-security-config') compile project(':spring-security-oauth2-client') + compile project(':spring-security-oauth2-jose') compile 'org.springframework:spring-webflux' compile 'org.springframework.boot:spring-boot-starter-thymeleaf' compile 'org.springframework.boot:spring-boot-starter-web' diff --git a/samples/boot/authcodegrant-webflux/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java b/samples/boot/oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java similarity index 84% rename from samples/boot/authcodegrant-webflux/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java rename to samples/boot/oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java index bfb8fce3e8..fd1bcf611f 100644 --- a/samples/boot/authcodegrant-webflux/src/main/java/sample/OAuth2AuthorizationCodeGrantApplication.java +++ b/samples/boot/oauth2webclient/src/main/java/sample/OAuth2WebClientApplication.java @@ -19,12 +19,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** - * @author Rob Winch + * @author Joe Grandja */ @SpringBootApplication -public class OAuth2AuthorizationCodeGrantApplication { +public class OAuth2WebClientApplication { public static void main(String[] args) { - SpringApplication.run(OAuth2AuthorizationCodeGrantApplication.class, args); + SpringApplication.run(OAuth2WebClientApplication.class, args); } } diff --git a/samples/boot/authcodegrant/src/main/java/sample/config/SecurityConfig.java b/samples/boot/oauth2webclient/src/main/java/sample/config/SecurityConfig.java similarity index 96% rename from samples/boot/authcodegrant/src/main/java/sample/config/SecurityConfig.java rename to samples/boot/oauth2webclient/src/main/java/sample/config/SecurityConfig.java index 0e889d1c97..b4d8bd00a5 100644 --- a/samples/boot/authcodegrant/src/main/java/sample/config/SecurityConfig.java +++ b/samples/boot/oauth2webclient/src/main/java/sample/config/SecurityConfig.java @@ -37,10 +37,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() + .mvcMatchers("/", "/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .and() + .oauth2Login() + .and() .oauth2Client(); } diff --git a/samples/boot/authcodegrant/src/main/java/sample/config/WebClientConfig.java b/samples/boot/oauth2webclient/src/main/java/sample/config/WebClientConfig.java similarity index 65% rename from samples/boot/authcodegrant/src/main/java/sample/config/WebClientConfig.java rename to samples/boot/oauth2webclient/src/main/java/sample/config/WebClientConfig.java index fe714c7b5b..d0270e8acc 100644 --- a/samples/boot/authcodegrant/src/main/java/sample/config/WebClientConfig.java +++ b/samples/boot/oauth2webclient/src/main/java/sample/config/WebClientConfig.java @@ -18,6 +18,8 @@ package sample.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; @@ -29,9 +31,11 @@ import org.springframework.web.reactive.function.client.WebClient; public class WebClientConfig { @Bean - WebClient webClient() { + WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { + ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository); + oauth2.setDefaultOAuth2AuthorizedClient(true); return WebClient.builder() - .filter(new ServletOAuth2AuthorizedClientExchangeFilterFunction()) + .apply(oauth2.oauth2Configuration()) .build(); } } diff --git a/samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java b/samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java new file mode 100644 index 0000000000..94213d255c --- /dev/null +++ b/samples/boot/oauth2webclient/src/main/java/sample/web/IndexController.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2018 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 + * + * 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 sample.web; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * @author Rob Winch + */ +@Controller +public class IndexController { + @GetMapping("/") + String index() { + return "index"; + } +} diff --git a/samples/boot/authcodegrant/src/main/java/sample/web/GitHubReposController.java b/samples/boot/oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java similarity index 55% rename from samples/boot/authcodegrant/src/main/java/sample/web/GitHubReposController.java rename to samples/boot/oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java index 2e85edc0f9..33c4d1354d 100644 --- a/samples/boot/authcodegrant/src/main/java/sample/web/GitHubReposController.java +++ b/samples/boot/oauth2webclient/src/main/java/sample/web/OAuth2WebClientController.java @@ -15,46 +15,53 @@ */ package sample.web; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.reactive.function.client.WebClient; -import java.util.List; - -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; +import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId; /** * @author Joe Grandja * @author Rob Winch */ @Controller -public class GitHubReposController { +@RequestMapping(path = {"/webclient", "/public/webclient"}) +public class OAuth2WebClientController { private final WebClient webClient; - public GitHubReposController(WebClient webClient) { + private final String uri; + + public OAuth2WebClientController(WebClient webClient, @Value("${resource-uri}") String uri) { this.webClient = webClient; + this.uri = uri; } - @GetMapping("/") - public String index() { - return "redirect:/repos"; + @GetMapping("/explicit") + String explicit(Model model) { + String body = this.webClient + .get() + .uri(this.uri) + .attributes(clientRegistrationId("client-id")) + .retrieve() + .bodyToMono(String.class) + .block(); + model.addAttribute("body", body); + return "response"; } - @GetMapping("/repos") - public String gitHubRepos(Model model, @RegisteredOAuth2AuthorizedClient("github") OAuth2AuthorizedClient authorizedClient) { - String endpointUri = "https://api.github.com/user/repos"; - List repos = this.webClient - .get() - .uri(endpointUri) - .attributes(oauth2AuthorizedClient(authorizedClient)) - .retrieve() - .bodyToMono(List.class) - .block(); - model.addAttribute("repos", repos); - - return "github-repos"; + @GetMapping("/implicit") + String implicit(Model model) { + String body = this.webClient + .get() + .uri(this.uri) + .retrieve() + .bodyToMono(String.class) + .block(); + model.addAttribute("body", body); + return "response"; } } diff --git a/samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java b/samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java new file mode 100644 index 0000000000..62d4cb82c2 --- /dev/null +++ b/samples/boot/oauth2webclient/src/main/java/sample/web/RegisteredOAuth2AuthorizedClientController.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2018 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 + * + * 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 sample.web; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient; + +/** + * @author Joe Grandja + * @author Rob Winch + */ +@Controller +@RequestMapping(path = {"/annotation", "/public/annotation"}) +public class RegisteredOAuth2AuthorizedClientController { + private final WebClient webClient; + + private final String uri; + + public RegisteredOAuth2AuthorizedClientController(WebClient webClient, @Value("${resource-uri}") String uri) { + this.webClient = webClient; + this.uri = uri; + } + + @GetMapping("/explicit") + String explicit(Model model, @RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) { + String body = this.webClient + .get() + .uri(this.uri) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .retrieve() + .bodyToMono(String.class) + .block(); + model.addAttribute("body", body); + return "response"; + } + + @GetMapping("/implicit") + String implicit(Model model, @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) { + String body = this.webClient + .get() + .uri(this.uri) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .retrieve() + .bodyToMono(String.class) + .block(); + model.addAttribute("body", body); + return "response"; + } +} diff --git a/samples/boot/oauth2webclient/src/main/resources/application.yml b/samples/boot/oauth2webclient/src/main/resources/application.yml new file mode 100644 index 0000000000..7144c09a0f --- /dev/null +++ b/samples/boot/oauth2webclient/src/main/resources/application.yml @@ -0,0 +1,21 @@ +logging: + level: + root: INFO + org.springframework.web: INFO + org.springframework.security: INFO +# org.springframework.boot.autoconfigure: DEBUG + +spring: + thymeleaf: + cache: false + security: + oauth2: + client: + registration: + client-id: + client-id: replace-with-client-id + client-secret: replace-with-client-secret + provider: github + scopes: read:user,public_repo + +resource-uri: https://api.github.com/user/repos diff --git a/samples/boot/oauth2webclient/src/main/resources/templates/index.html b/samples/boot/oauth2webclient/src/main/resources/templates/index.html new file mode 100644 index 0000000000..49eea0a6bc --- /dev/null +++ b/samples/boot/oauth2webclient/src/main/resources/templates/index.html @@ -0,0 +1,51 @@ + + + + OAuth2 WebClient Showcase + + + +Log Out +

Examples

+ +

@RegisteredOAuth2AuthorizedClient

+

+Examples on RegisteredOAuth2AuthorizedClientController +

Authenticated

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - Use the currently logged in user's OAuth Token. This will + only work if the user authenticates with oauth2Login and the token provided is the correct token provided at + log in is authorized.
  • +
+

Public

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - This will fail if the user is not authenticated. + Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then + authenticates with oauth2Login()
  • +
+ +

ServletOAuth2AuthorizedClientExchangeFilterFunction

+

+ Examples on OAuth2WebClientController that demonstrate how to use ServletOAuth2AuthorizedClientExchangeFilterFunction +

Authenticated

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - Use the currently logged in user's OAuth Token. This will + only work if the user authenticates with oauth2Login and the token provided is the correct token provided at + log in is authorized.
  • +
+

Public

+
    +
  • Explicit - Explicitly provide a Client Registration Id
  • +
  • + Implicit - This will fail if the user is not authenticated. + Since it is mapped to permitAll, it is going to fail unless the user already took an action to log in and then + authenticates with oauth2Login()
  • +
+ + diff --git a/samples/boot/oauth2webclient/src/main/resources/templates/response.html b/samples/boot/oauth2webclient/src/main/resources/templates/response.html new file mode 100644 index 0000000000..ef5a21c74a --- /dev/null +++ b/samples/boot/oauth2webclient/src/main/resources/templates/response.html @@ -0,0 +1,31 @@ + + + + + + OAuth2 WebClient Showcase + + + +Back +

Response

+
+ + + diff --git a/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java b/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java new file mode 100644 index 0000000000..acefd54c26 --- /dev/null +++ b/samples/boot/oauth2webclient/src/test/java/sample/OAuth2WebClientApplicationTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2018 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 + * + * 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 sample; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Rob Winch + */ +@SpringBootTest +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +public class OAuth2WebClientApplicationTests { + + @Autowired + private MockMvc mockMvc; + + @Test + public void annotationExplicitWhenNotAuthenticatedThenLoginRequested() throws Exception { + this.mockMvc.perform(get("/annotation/explicit")) + .andExpect(status().is3xxRedirection()); + } +}