CODE WITH SIBIN

Solving Real Problems with Real Code


Securing Spring Boot REST API with Okta: Authentication & Authorization Guide

Security is an important part of any web application. This ensures that only the correct users can access preserved resources. In this guide, we will see how to protect the spring boot rest API using okta for authentication and authorization.

We’ll go through:

  • Setting up a Spring Boot project
  • Configuring Okta for authentication
  • Implementing Spring Security
  • Creating secured REST endpoints
  • Testing with Postman
  • Writing unit tests with JUnit & Spring Security

Let’s get started!

1. Project Setup (pom.xml)

Ensure your pom.xml includes the required dependencies:

<dependencies>
    <!-- Spring Boot Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Okta Spring Boot Starter -->
    <dependency>
        <groupId>com.okta.spring</groupId>
        <artifactId>okta-spring-boot-starter</artifactId>
        <version>3.0.7</version>
    </dependency>

    <!-- Testing Dependencies -->
    <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>
</dependencies>

2. Okta Configuration

Step 1: Create an Okta Developer Account

  1. Sign up at https://developer.okta.com
  2. Create an Authorization Server under Security → API
  3. Register a new application:
    • Select OIDC - OpenID Connect
    • Choose Web as the platform
    • Set the redirect URI to http://localhost:8080/login/oauth2/code/okta
  4. Note down the following details from Okta Dashboard:
    • Issuer URL: https://your-okta-domain/oauth2/default
    • Client ID: your-client-id
    • Client Secret: your-client-secret

Step 2: Configure application.yml

Create an application.yml file:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: your-client-id
            client-secret: your-client-secret
            scope: openid, profile, email
        provider:
          okta:
            issuer-uri: https://your-okta-domain/oauth2/default

3. Spring Security Configuration

Create a security configuration class to define role-based access:

package com.example.demo.config;

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.http.SessionCreationPolicy;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public").permitAll()
                .requestMatchers("/admin").hasAuthority("SCOPE_admin")
                .requestMatchers("/user").hasAuthority("SCOPE_user")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            )
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        return http.build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        return new JwtAuthenticationConverter();
    }
}

This configuration ensures that:

  • Public endpoints (/public) are accessible to everyone.
  • User endpoints (/user) require the SCOPE_user role.
  • Admin endpoints (/admin) require the SCOPE_admin role.

4. Creating Secure REST Endpoints

Now, create a controller with endpoints secured by Spring Security:

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/public")
    public String publicEndpoint() {
        return "This is a public endpoint.";
    }

    @GetMapping("/user")
    public String userEndpoint(@AuthenticationPrincipal Jwt jwt) {
        return "Hello User! Your email: " + jwt.getClaim("email");
    }

    @GetMapping("/admin")
    public String adminEndpoint(@AuthenticationPrincipal Jwt jwt) {
        return "Hello Admin! Your email: " + jwt.getClaim("email");
    }
}

5. Testing with Postman

Step 1: Get Access Token

  1. Open Postman and create a POST request.
  2. Use the following Okta OAuth 2.0 Token Endpoint: https://your-okta-domain/oauth2/default/v1/token
  3. Set the request Body to x-www-form-urlencoded with:
    • grant_type: client_credentials
    • client_id: your-client-id
    • client_secret: your-client-secret
    • scope: openid profile email user admin
  4. Click Send and copy the access_token.

Step 2: Access API Endpoints

Public Endpoint (No Token Required)

GET http://localhost:8080/api/public

User Endpoint (Requires Token)

GET http://localhost:8080/api/user

Add Authorization Header: Bearer <your-access-token>

Admin Endpoint (Requires Token with SCOPE_admin)

GET http://localhost:8080/api/admin

Add Authorization Header: Bearer <your-access-token>

6. Unit Testing with JUnit & Spring Security

Write tests to verify security constraints:

package com.example.demo;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.jupiter.api.Test;
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.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

@SpringBootTest
@AutoConfigureMockMvc
class ApiControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testPublicEndpoint() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/api/public"))
                .andExpect(status().isOk());
    }

    @Test
    void testUserEndpoint() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/api/user")
                .with(jwt().jwt(jwt -> jwt.claim("scope", "user"))))
                .andExpect(status().isOk());
    }

    @Test
    void testAdminEndpoint() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/api/admin")
                .with(jwt().jwt(jwt -> jwt.claim("scope", "admin"))))
                .andExpect(status().isOk());
    }

    @Test
    void testUnauthorizedAdminAccess() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/api/admin")
                .with(jwt().jwt(jwt -> jwt.claim("scope", "user"))))
                .andExpect(status().isForbidden());
    }
}

🌿 Secure Your Spring Boot App with Okta

Get started with a fully secure Spring Boot project integrated with Okta OAuth 2.0 authentication.

🚀 Clone on GitHub

Leave a Reply

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