CODE WITH SIBIN

Solving Real Problems with Real Code


Spring Boot Thymeleaf Okta: Implementing Secure User Management with Okta

Table of Contents

  1. Introduction
  2. Setting Up the Spring Boot Project
  3. Configuring Okta for Authentication
  4. Implementing User Registration with Thymeleaf
  5. Implementing Login with Okta OAuth 2.0
  6. Custom Login and Logout with Thymeleaf

1. Introduction

Overview of Spring Boot, Thymeleaf, and Okta

Spring boot is a framework in Java that helps developers to quickly create stand-alone, production-grade applications. This simplifies the setup and configuration of Java applications, making it easier to start a project and create a web application without dealing with a complex setup.

Thymeleaf is a template engine for Java that allows developers to dynamically generate an HTML page. It works well with spring boot and is often used to build a view part of the web application. You can use it to create a dynamic web page that change based on the data provided by your backnd.

Okta is a cloud-based identification management service. It provides certification and user management, which means that it handles things such as logging users, keeping them safe and managing their profiles. Okta takes care of complex identity and safety tasks so that developers do not need to make those systems from scratching.

Why use Okta for authentication and user management?

Okta is used for authentication and user management because it is reliable, safe and saves time. Instead of writing your own login systems and worrying about security issues like password storage and multi-factor authentication, Okta offers ready-to-use solutions to the system of writing and password storage and multi-factor authentication. This helps to make applications more secure and reduces the charge for developers.

Here are a few reasons to use Okta:

  • Security: OKTA follows the best practices to secure user data and prevent unauthorized access.
  • Ease of integration: It is easy to integrate okta with existing applications such as spring boot, allowing developers to quickly add user management features.
  • Scalability: Okta can handle many users, making it ideal for both small and large applications.
  • Time-Saving: With Okta, you donโ€™t need to spend time creating complex authentication systems; you can focus on building your application.

Guide Objectives and Prerequisites

Objective:

  • To show how to integrate okta certification with spring boot application.
  • To display the dynamic content in your spring boot application to display to use the thymeleaf.
  • To teach how to secure your application using authentication and authority facilities of OKTA.

Prerequisites

  • Basic understanding of Java and Spring boot.
  • Some knowledge of HTML and Thymeleaf. Familiar with Okta (though the guide will cover the basics).
  • A Java development environment (such as intelligent idea or eclipse) is installed on your computer.
  • To work with a basic spring boot project.

2. Setting Up the Spring Boot Project

Creating a New Spring Boot Project

To start, you need to create a new Spring Boot project. You can do this in a couple of ways, but the easiest method is to use Spring Initializr, which will generate a basic Spring Boot application for you.

Follow these steps:

  1. Go to Spring Initializr.
  2. Select the following options:
    • Project: Maven or Gradle (Maven is easier for beginners).
    • Language: Java.
    • Spring Boot Version: The latest stable version.
    • Project Metadata: Fill in the Group and Artifact fields with your project details (e.g., com.example for Group and okta-springboot for Artifact).
    • Dependencies: Select the dependencies you need (Spring Web, Spring Security, Thymeleaf, and Okta Spring Boot Starter).
  3. Click Generate, and a ZIP file with the project structure will be downloaded.
  4. Extract the ZIP file and open it in your favorite IDE (like IntelliJ IDEA or Eclipse).

Adding Required Dependencies

If you didnโ€™t add all the required dependencies when generating the project from Spring Initializr, you can manually add them to your pom.xml file.

1. Spring Boot Web
This dependency is required to build web applications with Spring Boot.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. Spring Security
Spring Security is used to secure your application. It handles authentication, authorization, and other security-related tasks.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

3. Thymeleaf
Thymeleaf is the template engine used for rendering HTML views.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

4. Okta Spring Boot Starter
This is the Okta integration starter. It helps integrate Okta with your Spring Boot application for authentication and user management.

<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-boot-starter</artifactId>
</dependency>

Once youโ€™ve added these dependencies, your project will be ready to use Spring Boot, Thymeleaf, Spring Security, and Okta.

3. Configuring Okta for Authentication

To enable authentication in your Spring Boot application, you need to set up Okta, retrieve authentication credentials, and configure your application to use them. Follow these steps to integrate Okta authentication.

Creating an Okta Developer Account

If you donโ€™t already have an Okta account, you need to create a free developer account:

  1. Open your web browser and go to Okta Developer.
  2. Sign up with your email address.
  3. Once registered, you will be redirected to the Okta Developer Dashboard.
  4. Note down your Okta domain (e.g., https://dev-XXXXXX.okta.com)โ€”you will need this later.

Setting Up an Okta Web Application

After creating your Okta account, you need to set up a web application:

  1. Log in to your Okta Developer Dashboard.
  2. Click on Applications from the left menu.
  3. Click Create App Integration.
  4. Select OAuth 2.0 as the Sign-in method.
  5. Under Application Type, choose Web Application.
  6. Click Next.
  7. Configure the following settings:
    • Sign-in redirect URIs:
      • http://localhost:8080/login/oauth2/code/okta
    • Sign-out redirect URIs:
      • http://localhost:8080
    • Leave the other settings as default.
  8. Click Save.

Once your application is created, Okta will generate Client ID and Client Secret, which you will use in your Spring Boot application.

Retrieving Client ID, Client Secret, and Issuer URL

To integrate Okta authentication, retrieve the required credentials:

  1. In the Okta Developer Dashboard, navigate to Applications.
  2. Select the application you just created.
  3. Go to the General tab.
  4. Copy the Client ID and Client Secret.
  5. Under Issuer, copy the URL (e.g., https://dev-XXXXXX.okta.com/oauth2/default).

Adding Okta Configuration to application.yml

Now, configure your Spring Boot application by adding the Okta credentials to ๐Ÿ“„src/main/resources/application.yml:

server:
  port: 8080

okta:
  oauth2:
    client-id: YOUR_OKTA_CLIENT_ID
    client-secret: YOUR_OKTA_CLIENT_SECRET
    issuer: https://dev-XXXXXX.okta.com/oauth2/default

Replace YOUR_OKTA_CLIENT_ID, YOUR_OKTA_CLIENT_SECRET, and https://dev-XXXXXX.okta.com/oauth2/default with your actual values from Okta.

4. Implementing User Registration with Thymeleaf

Now that we have set up Okta authentication, the next step is to implement user registration. We will create a registration form using Thymeleaf, handle user input with a Spring MVC controller, send user data to Okta for account creation, and implement form validation.

Creating a Thymeleaf Registration Form

We will create an HTML registration form using Thymeleaf. This form will collect user details such as first name, last name, email, and password.

Create a new file:
๐Ÿ“„ src/main/resources/templates/register.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Register</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <h2>Register</h2>
    <form th:action="@{/register}" method="post" th:object="${user}">
        <label>First Name:</label>
        <input type="text" th:field="*{firstName}" required>
        <br>

        <label>Last Name:</label>
        <input type="text" th:field="*{lastName}" required>
        <br>

        <label>Email:</label>
        <input type="email" th:field="*{email}" required>
        <br>

        <label>Password:</label>
        <input type="password" th:field="*{password}" required>
        <br>

        <button type="submit">Register</button>
    </form>
</body>
</html>

Handling User Input with a Spring MVC Controller

We need a Spring MVC controller to handle the form submission and process user registration.

๐Ÿ“„ src/main/java/com/example/controller/RegistrationController.java

package com.example.controller;

import com.example.model.User;
import com.example.service.OktaUserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class RegistrationController {

    private final OktaUserService oktaUserService;

    public RegistrationController(OktaUserService oktaUserService) {
        this.oktaUserService = oktaUserService;
    }

    @GetMapping("/register")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new User());
        return "register";
    }

    @PostMapping("/register")
    public String registerUser(@ModelAttribute User user, Model model) {
        boolean isRegistered = oktaUserService.registerUser(user);
        if (isRegistered) {
            return "redirect:/login";
        } else {
            model.addAttribute("error", "Registration failed. Try again.");
            return "register";
        }
    }
}

Sending User Data to Okta for Account Creation

We will now create a service class to communicate with Oktaโ€™s API and register new users.

๐Ÿ“„ src/main/java/com/example/service/OktaUserService.java

package com.example.service;

import com.example.model.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.Map;

@Service
public class OktaUserService {

    @Value("${okta.oauth2.issuer}")
    private String oktaIssuer;

    @Value("${okta.oauth2.client-id}")
    private String clientId;

    @Value("${okta.oauth2.client-secret}")
    private String clientSecret;

    private final RestTemplate restTemplate = new RestTemplate();

    public boolean registerUser(User user) {
        String url = oktaIssuer + "/v1/users";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBasicAuth(clientId, clientSecret);

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("profile", Map.of(
                "firstName", user.getFirstName(),
                "lastName", user.getLastName(),
                "email", user.getEmail(),
                "login", user.getEmail()
        ));
        requestBody.put("credentials", Map.of(
                "password", Map.of("value", user.getPassword())
        ));

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);

        try {
            ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
            return response.getStatusCode().is2xxSuccessful();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

Implementing Form Validation

To validate user input, we will create a User model with validation annotations.

๐Ÿ“„ src/main/java/com/example/model/User.java

package com.example.model;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class User {

    @NotBlank(message = "First name is required")
    private String firstName;

    @NotBlank(message = "Last name is required")
    private String lastName;

    @NotBlank(message = "Email is required")
    @Email(message = "Invalid email format")
    private String email;

    @NotBlank(message = "Password is required")
    @Size(min = 6, message = "Password must be at least 6 characters long")
    private String password;

    // Getters and Setters
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

To enable validation in the controller, modify the registerUser method:

๐Ÿ“„ RegistrationController.java

@PostMapping("/register")
public String registerUser(@Valid @ModelAttribute User user, BindingResult result, Model model) {
    if (result.hasErrors()) {
        return "register";
    }

    boolean isRegistered = oktaUserService.registerUser(user);
    if (isRegistered) {
        return "redirect:/login";
    } else {
        model.addAttribute("error", "Registration failed. Try again.");
        return "register";
    }
}

5. Implementing Login with Okta OAuth 2.0

Now, we will implement user login using Oktaโ€™s OAuth 2.0 authentication. We will configure our Spring Boot application to redirect users to the Okta login page, handle login responses in Spring Security, and store user session information.

Using Oktaโ€™s OAuth 2.0 for Authentication

Since we already set up Okta OAuth 2.0 configuration in the application.yml file, Spring Security will automatically handle authentication.

When a user tries to access a secured page, they will be redirected to Oktaโ€™s login page. After successful authentication, Okta will send the user back to our application.

Redirecting Users to the Okta Login Page

Spring Security provides built-in support for OAuth 2.0 authentication, and Okta integrates seamlessly with it.

To enable this functionality, we need to configure security settings.

Create a Security Configuration Class

๐Ÿ“„ src/main/java/com/example/security/SecurityConfig.java

package com.example.security;

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.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/", "/register").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2Login(); // Enable OAuth2 login with Okta

        return http.build();
    }
}

Explanation:

  • The / (home) and /register pages are publicly accessible.
  • Any other request requires authentication.
  • oauth2Login() ensures that users are redirected to Oktaโ€™s login page when they try to access a protected resource.

Handling Login Responses in Spring Security

Once the user logs in successfully via Okta, Spring Security will handle the authentication response and retrieve user details.

To display the logged-in user's information, modify your Thymeleaf template.

Update the Navigation Bar

๐Ÿ“„ src/main/resources/templates/fragments/navbar.html

<nav>
    <a href="/">Home</a>
    <a href="/profile">Profile</a>

    <!-- Check if user is authenticated -->
    <div sec:authorize="isAuthenticated()">
        <span>Welcome, <span sec:authentication="name"></span>!</span>
        <a th:href="@{/logout}">Logout</a>
    </div>

    <div sec:authorize="!isAuthenticated()">
        <a th:href="@{/oauth2/authorization/okta}">Login</a>
    </div>
</nav>

Explanation:

  • If the user is authenticated, their username is displayed.
  • The Logout button appears when a user is logged in.
  • If the user is not logged in, a Login button appears, which redirects to Oktaโ€™s login page (/oauth2/authorization/okta).

Storing User Session Information

Spring Security automatically stores the authenticated user details in the Security Context.

We can retrieve this user information in a controller and display it on the profile page.

Create a Profile Controller

๐Ÿ“„ src/main/java/com/example/controller/ProfileController.java

package com.example.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
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;

@Controller
public class ProfileController {

    @GetMapping("/profile")
    public String userProfile(@AuthenticationPrincipal OidcUser user, Model model) {
        model.addAttribute("user", user);
        return "profile";
    }
}

Create a Profile Page

๐Ÿ“„ src/main/resources/templates/profile.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Profile</title>
</head>
<body>
    <h2>User Profile</h2>
    
    <p><strong>Name:</strong> <span th:text="${user.fullName}"></span></p>
    <p><strong>Email:</strong> <span th:text="${user.email}"></span></p>
    
    <a href="/">Go Home</a>
</body>
</html>

Explanation:

  • We use @AuthenticationPrincipal OidcUser user to get the logged-in userโ€™s details.
  • The OidcUser object contains user attributes such as name and email.
  • This information is passed to the profile.html page and displayed.

6. Custom Login and Logout with Thymeleaf

Now, we will implement a custom login page using Thymeleaf instead of redirecting users to Oktaโ€™s default login page. We will also configure logout functionality and handle user redirection after login and logout.

Creating a Thymeleaf-Based Custom Login Page

By default, Spring Security provides a login page when using OAuth 2.0 authentication, but we can create a custom login page in Thymeleaf to provide a better user experience.

Create a Login Page

๐Ÿ“„ src/main/resources/templates/login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>

    <p>Click below to log in with Okta:</p>

    <a th:href="@{/oauth2/authorization/okta}">
        <button>Login with Okta</button>
    </a>

</body>
</html>

Modify Security Configuration to Use Custom Login Page

๐Ÿ“„ src/main/java/com/example/security/SecurityConfig.java

package com.example.security;

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.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/", "/register", "/login").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .loginPage("/login") // Custom login page
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/login?logout") // Redirect after logout
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
            );

        return http.build();
    }
}

Explanation:

  • The /login page is made publicly accessible.
  • oauth2Login().loginPage("/login") tells Spring Security to use our custom login page.
  • Users will be redirected to the login.html page instead of Oktaโ€™s login page.
  • If they click the "Login with Okta" button, they will be redirected to Okta for authentication.

Implementing Logout Functionality

Spring Security provides built-in logout handling, which we can integrate into our Thymeleaf templates.

Update the Navigation Bar with a Logout Button

๐Ÿ“„ src/main/resources/templates/fragments/navbar.html

<nav>
    <a href="/">Home</a>
    <a href="/profile">Profile</a>

    <!-- Check if user is authenticated -->
    <div sec:authorize="isAuthenticated()">
        <span>Welcome, <span sec:authentication="name"></span>!</span>
        <a th:href="@{/logout}">Logout</a>
    </div>

    <div sec:authorize="!isAuthenticated()">
        <a th:href="@{/login}">Login</a>
    </div>
</nav>

Explanation:

  • If the user is logged in, a "Welcome" message and the "Logout" button are displayed.
  • If the user is not authenticated, a "Login" button appears.
  • The /logout URL will log out the user and clear their session.

Redirecting Users After Login and Logout

After login, users can be redirected to a specific page instead of the default home page. Similarly, after logout, they can be redirected to a confirmation page.

Configure Redirection in Security Configuration

๐Ÿ“„ src/main/java/com/example/security/SecurityConfig.java

.oauth2Login(oauth2 -> oauth2
    .loginPage("/login")
    .defaultSuccessUrl("/profile", true) // Redirect to profile page after login
)
.logout(logout -> logout
    .logoutSuccessUrl("/login?logout") // Redirect to login page after logout
);

Explanation:

  • defaultSuccessUrl("/profile", true): After successful login, users will be redirected to the /profile page.
  • logoutSuccessUrl("/login?logout"): After logout, users will be redirected to the login page with a logout confirmation message.

๐ŸŒฟ Spring Boot Thymeleaf Okta: Implementing Secure User Management with Okta

Get started with a fully secure Spring Boot web project integrated with Okta.

๐Ÿš€ Clone on GitHub

Leave a Reply

Your email address will not be published. Required fields are marked *