Skip to content

Latest commit

 

History

History
271 lines (244 loc) · 11.3 KB

File metadata and controls

271 lines (244 loc) · 11.3 KB

This is aimed at implementing the capability for authenticating an end-user against a standard OAuth 2.0 Provider or an OpenID Connect 1.0 Provider. The feature essentially implements the use-case for, “Login with Google” or “Login with Facebook” or any other similar auth service.

First, we go to the OAuth2 Authentication service provider’s developer website (e.g. Google or Twitter or Facebook etc. Dev APIs console), register and obtain a new OAuth2 client credential, which we will use in our app.

Next, we create a Spring Web MVC project with Spring Boot 2.0 or higher (e.g. using Spring Initialzr or Spring Boot CLI or an IDE with Spring support etc.) In the pom.xml file, include the following:

4.0.0

com.obinna.spring.demos springsecurity-demo 0.0.1-SNAPSHOT jar

springsecurity-demo Demo project for Spring Boot

org.springframework.boot spring-boot-starter-parent 2.0.3.RELEASE UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-security org.springframework.security spring-security-oauth2-client org.springframework.security spring-security-oauth2-jose org.thymeleaf.extras thymeleaf-extras-springsecurity4 org.springframework spring-webflux io.projectreactor.ipc reactor-netty
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
 </dependency>
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
 </dependency>
 <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
 </dependency>
org.springframework.boot spring-boot-maven-plugin

In the src\main\resources\application.yml or properties file, enter the following minimal settings: spring: thymeleaf: cache: false security: oauth2: client: registration: google: client-id: [enter-your-client-id-here] client-secret: [enter-your-client-secret-here]

client-authentication-method: basic

authorization-grant-type: authorization_code

redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"

       scope: profile, email, address, phone
       client-name: Google Account

provider:

google:

user-name-attribute: sub

In the src\main\java\app-package-name package, create a new package named, controller; and add a new Controller class (named e.g. MainController.java) and in it code the following: @Controller public class MainController { @Autowired private OAuth2AuthorizedClientService authorizedClientService;

@GetMapping("/") public String index(Model model, OAuth2AuthenticationToken authentication) { OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient( authentication.getAuthorizedClientRegistrationId(), authentication.getName()); model.addAttribute("userName", authentication.getName()); model.addAttribute("clientName", authorizedClient.getClientRegistration().getClientName()); return "index"; }

@GetMapping("/userinfo") public String userinfo(Model model, OAuth2AuthenticationToken authentication) { OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient( authentication.getAuthorizedClientRegistrationId(), authentication.getName()); Map userAttributes = Collections.emptyMap(); String userInfoEndpointUri = authorizedClient.getClientRegistration() .getProviderDetails().getUserInfoEndpoint().getUri(); if (!StringUtils.isEmpty(userInfoEndpointUri)) { // userInfoEndpointUri is optional for OIDC Clients userAttributes = WebClient.builder() .filter(oauth2Credentials(authorizedClient)) .build() .get() .uri(userInfoEndpointUri) .retrieve() .bodyToMono(Map.class) .block(); } model.addAttribute("userAttributes", userAttributes); return "userinfo"; }

private ExchangeFilterFunction oauth2Credentials(OAuth2AuthorizedClient authorizedClient) { return ExchangeFilterFunction.ofRequestProcessor( clientRequest -> { ClientRequest authorizedRequest = ClientRequest.from(clientRequest) .header(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue()) .build(); return Mono.just(authorizedRequest); }); } }

In the src\main\resources\templates folder, add the following thymeleaf pages:

index.html:

<title>Spring Security - OAuth 2.0 Login</title>
User:
 

OAuth 2.0 Login with Spring Security

You are successfully logged in via the OAuth 2.0 Client
 

userinfo.html:

<title>Spring Security - OAuth 2.0 User Info</title>

OAuth 2.0 User Info

User Attributes:
  • :

Start the application and go to http://localhost:8080/. Click on the link titled Google Account and it will redirect to the Google Account login page. After authentication, the app will be redirected to the http://localhost:8080/index page. Also, on clicking the User Info link on the index page, the app will display the Google user account details for the authenticated principal. Et viola! OAuth2 in action!!! Note: Further steps that can be done are: Setting the app to use a custom landing-page, a custom redirect page etc, by simply overriding the defaults. For this and much more, see the Spring Security Reference Guide, online. Implementing the above OAuth2 configuration but in a programmatic way (say, for a classic Springframework app that is not using the autoconfig-steroids from Spring Boot). To accomplish this, we do the following - see https://docs.spring.io/spring-security/site/docs/current/reference/html5/#jc-oauth2login: In the src\main\java\app-package-name package, add the following custom config code in this src file named, OAuth2LoginConfig (note: this class is annotated with the @Configuration annotation, so this will override the Spring Boot defaults).

OAuth2LoginConfig.java

@Configuration public class OAuth2LoginConfig {

@EnableWebSecurity public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
           .authorizeRequests()
           .anyRequest().authenticated()
           .and()
           .oauth2Login();
   }

}

@Bean public ClientRegistrationRepository clientRegistrationRepository() { return new InMemoryClientRegistrationRepository(this.googleClientRegistration()); }

private ClientRegistration googleClientRegistration() { return ClientRegistration.withRegistrationId("google") .clientId("enter-client-id-here") .clientSecret("enter-client-secret-here") .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}") .scope("profile", "email", "address", "phone") .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth") .tokenUri("https://www.googleapis.com/oauth2/v4/token") .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo") .userNameAttributeName(IdTokenClaimNames.SUB) .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs") .clientName("Google") .build(); } }