# Part 2: The client side

## OAuth2 / OpenID Connect Client

Now we will implement the corresponding client for the product server to show the product list in a web UI.

**Tip**:\
You may look into the [Spring Boot Reference Documentation](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security-oauth2-client) and the [Spring Security Reference Documentation](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#oauth2client) on how to implement a client.

To start with this tutorial part, navigate to the project **initial/ui** in your IDE.

First, just run this unfinished client. Please make sure that you also have started the product server from the previous part.

Just run class *com.example.UiApplication*. Then navigate your web browser to <http://localhost:9095/client>. You should see the following screen.

Now try to click the link for *Products*. This should lead to the following whitelabel error screen:

This is because our initial client still only sends a basic authentication header to authenticate the request for getting the product list. But the product server now requires a JWT token instead. This is why we now get a 401 HTTP status error (unauthorized).

So let's start with fixing this issue by implementing an OAuth2/OIDC client.

### **Step 1: Change Maven dependencies for the client**

Add the following dependency to the existing maven *pom.xml* file:

*pom.xml*:

```markup
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
```

This adds the spring boot starter dependency for building an OAuth2/OIDC client. This includes all required classes to manage the authorization code flow of OAuth2 and handle all JWT token-related tasks.

### **Step 2: Add the required properties for the client**

The client requires several configuration parameters from the identity server to be used. Thanks to the OpenID Connect discovery specification most identity servers publish all required parameters at a well-known server endpoint */.well-known/openid-configuration*. In the case of *Auth0* the URL is <https://access-me.eu.auth0.com/.well-known/openid-configuration>.

This is why one parameter of spring (see below) is requiring the *issuer-uri*. This points to the base URL address of the identity server (i.e. without the */.well-known/openid-configuration* part).

Spring security provides predefined properties to configure the application as an OAuth2/OIDC client:

* The property `spring.security.oauth2.client.provider.auth0.issuer-uri` specifies the URI for loading the required configuration to set up an OAuth2/OIDC client for the *Auth0* identity provider.
* The property `spring.security.oauth2.client.provider.user-name-attribute` specifies the attribute claim to use for mapping user data retrieved from the user info endpoint in OAuth2/OIDC client for *Auth0*.
* The property `spring.security.oauth2.client.registration.auth0.client-id` specifies the *client id* as it is has been registered at the *Auth0* identity provider.
* The property `spring.security.oauth2.client.registration.auth0.client-secret` specifies the *client secret* to authorize the application to act as a registered at the *Auth0* identity provider.
* The property `spring.security.oauth2.client.registration.auth0.authorizationGrantType` specifies which OAuth2/OIDC grant flow should be used for the client.
* The property `spring.security.oauth2.client.registration.auth0.clientAuthenticationMethod` specifies the authentication method to use when calling the token endpoint at the *Auth0* identity provider. The value of *NONE* specifies that no *client\_secret* is specified. Instead, the dynamic *Proof Key for Key Exchange (PKCE)* is used instead.
* The property `spring.security.oauth2.client.registration.auth0.redirect-uri` specifies the redirect URI to call our client application with the authorization code from the *Auth0* identity provider. Spring also provides predefined placeholders for the base URL and the registration id.
* The property `spring.security.oauth2.client.registration.auth0.scope` specifies the scopes to be used for the OAuth2 login. The value of *openid* enables the OpenID Connect mode and *profile*/*email* specifies which attribute claims to include in the token.

After adding the required new properties the updated *application.yml* should look like this:

*application.yml*:

```yaml
spring:
  security:
    oauth2:
      client:
        provider:
          auth0:
            issuer-uri: https://access-me.eu.auth0.com/
            user-name-attribute: sub
        registration:
          auth0:
            client-id: 'v13BSQLEZnw4N96V36dDdsGRd022isKe'
            authorizationGrantType: authorization_code
            clientAuthenticationMethod: NONE
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            scope:
              - openid
              - profile
              - email
```

**Important:**\
Please check that all indents are correct. Otherwise, you may get strange runtime errors when starting the application. The client secret is noted here just for the purpose of this tutorial. In your real productive applications, you should **NEVER** publish sensitive data like this client secret or any other sensitive data!!

### **Step 3: Add OAuth2/OIDC client security configuration**

To enable the client application to act as an OAuth2/OIDC client for *Auth0* identity provider it is required to add a new security configuration.

To achieve this create a new class named *WebSecurityConfiguration* in package *com.example*.

*com/example/WebSecurityConfiguration.java*:

```java
package com.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@EnableWebSecurity
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Bean
  public SecurityFilterChain api(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorizeRequests ->
                        authorizeRequests
                                .anyRequest().authenticated()
                )
                .oauth2Client().and()
                .oauth2Login(withDefaults());
        return http.build();
  }
}
```

### **Step 4: Update the call to the resource server**

We already extended the product server requiring a bearer token in the *Authorization* header with each request. To be able to call the server from the client we need to add the access token.

To achieve this we have to change the class *ProductService* to add the required header with the token.

*com/example/ProductService.java*:

```java
package com.example;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

@Service
public class ProductService {

  public Collection<Product> getAllProducts(OAuth2AccessToken oAuth2AccessToken) {

    RestTemplate template = new RestTemplate();

    ResponseEntity<Product[]> response =
            template.exchange(
                    "http://localhost:9090/server/products",
                    HttpMethod.GET,
                    new HttpEntity<Product[]>(createAuthorizationHeader(oAuth2AccessToken)),
                    Product[].class);

    if (response.getBody() != null) {
      return Arrays.asList(response.getBody());
    } else {
      return Collections.emptyList();
    }
  }

  private HttpHeaders createAuthorizationHeader(OAuth2AccessToken oAuth2AccessToken) {
    return new HttpHeaders() {
      {
        String authHeader = "Bearer " + oAuth2AccessToken.getTokenValue();
        set("Authorization", authHeader);
      }
    };
  }
}
```

In the *ProductController* class, we need to add a reference to an instance of the class *OAuth2AuthorizedClientService*. By using this instance we can retrieve the required access token.

In addition to this we also show the currently authenticated user by adding a new parameter of type *org.springframework.security.oauth2.core.oidc.user.OidcUser* annotated by *@AuthenticationPrincipal*.

*com/example/ProductController*:

```java
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/** UI controller for products frontend. */
@Controller
public class ProductController {

  private final ProductService productService;
  private final OAuth2AuthorizedClientService authorizedClientService;

  @Autowired
  public ProductController(ProductService productService, OAuth2AuthorizedClientService authorizedClientService) {
    this.productService = productService;
    this.authorizedClientService = authorizedClientService;
  }

  @GetMapping(path = "/")
  public String index(@AuthenticationPrincipal OidcUser oidcUserInfo, Model model) {
    String fullName = oidcUserInfo.getUserInfo().getNickName();
    model.addAttribute("username", fullName);
    return "index";
  }

  @GetMapping(path = "/products")
  public String getAllProducts(Authentication authentication, Model model) {
    OAuth2AuthorizedClient authorizedClient =
            this.authorizedClientService.loadAuthorizedClient("auth0", authentication.getName());
    Iterable<Product> products = productService.getAllProducts(authorizedClient.getAccessToken());
    model.addAttribute("products", products);
    return "products";
  }
}
```

Please note that *"auth0"* refers to the corresponding id of the client configuration in *application.yml*.

### **Step 5: Run the client application**

Now we can run the finished client as well. Please ensure that you have also started the product server from the last part.

Just run class *com.example.UiApplication*. Then navigate your web browser to <http://localhost:9095/client>.

If you have successfully followed and completed all steps you should be redirected to the login dialog of the identity server of *Auth0*.

![](https://1217954492-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtxH74bfp_X3TwUlR-g%2F-LtxHDMRDhbptfH8_G-v%2F-LtxJy47KRPsgikzJDBE%2Fauth0_login.png?alt=media\&token=d3498f31-c11e-47f8-a8f5-c85ffdb959c1)

To log in please use the following user credentials:

* user: <user@example.com>
* password: user\_4demo!

**Important:**\
The user credentials are noted here just for the purpose of this tutorial. In your real productive applications, you should **NEVER** publish user credentials or any other sensitive data!!

After successful login, you should again be redirected back to the client application and you should see the main screen.

![](https://1217954492-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtxH74bfp_X3TwUlR-g%2F-LtxHDMRDhbptfH8_G-v%2F-LtxK1qsYV8aNNgXSBxS%2Fclient_main.png?alt=media\&token=3c3bf513-7f2d-41b8-a9a1-4be0e4fd1d88)

After clicking the *Products* link you should see the list of products.

![](https://1217954492-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtxH74bfp_X3TwUlR-g%2F-LtxHDMRDhbptfH8_G-v%2F-LtxK5-wiAfLFP1l1Cma%2Fclient_products.png?alt=media\&token=4aa1716c-65f0-4102-827c-90fc5955eeb0)

This ends the whole tutorial.
