Integrating Laravel Personal Access Tokens with Spring Boot Resource Server

Mahfuzul Alam
4 min readSep 1, 2024

--

If you’re using Laravel as your OAuth2 Authorization Server and want to secure a Spring Boot application as a Resource Server using Laravel-issued JWT tokens, you are in the right place! This guide will help you configure your Spring Boot application to verify JWT tokens signed by Laravel and secure your REST APIs.

Target Audience: This article is aimed at developers using Laravel for authentication and who are looking to integrate a Spring Boot-based module as a secure resource server.

Resource Server Authorization (Laravel OAuth, Spring Boot)

Overview

When Laravel is used as an OAuth2 authorization server, it issues JWT tokens for authentication. To secure a Spring Boot application as a resource server, we need to configure it to accept these JWT tokens and validate them using the public key of the Laravel authorization server.

If your Laravel public key doesn’t change often, you can hard-code it directly into your Spring Boot application. Otherwise, you may want to expose a public API endpoint from Laravel to fetch the current public key dynamically.

Setting Up Spring Boot as a Resource Server

1. Add Required Dependencies

Add the following dependencies to your pom.xml:

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

These dependencies are needed to set up the Spring Boot application as an OAuth2 Resource Server that can decode and verify JWT tokens.

2. Configure the Security Class

In your Spring Boot application, create a SecurityConfig class to define the security configuration. This class will configure Spring Security to use JWT tokens and directly utilize the Laravel public key for verification.

Below is the complete SecurityConfig class:

package example.oauthdemo;

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.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;

@Configuration
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 ->
oauth2.jwt(jwt -> jwt.decoder(jwtDecoder()))
);
return http.build();
}

@Bean
public JwtDecoder jwtDecoder() {
// Public key in PEM format (masked for security)
String publicKey = "-----BEGIN PUBLIC KEY-----\n" +
"MIICIjANBgkqhki...<rest of your public key>...CAwEAAQ==\n" +
"-----END PUBLIC KEY-----";
return NimbusJwtDecoder.withPublicKey(RSAPublicKeyConverter(publicKey)).build();
}

private RSAPublicKey RSAPublicKeyConverter(String publicKey) {
try {
String publicKeyPEM = publicKey.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");
byte[] encoded = java.util.Base64.getDecoder().decode(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to convert public key", e);
}
}

}

3. Secure the REST Controller

Create a simple REST controller to test the configuration:

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

@RestController
public class HelloController {

@GetMapping("/hello")
public String hello(@AuthenticationPrincipal Jwt jwt) {
System.out.println(jwt.getSubject());
return "Hello World!";
}

}

Here, the @AuthenticationPrincipal annotation allows you to access the Jwt object that contains all the token details, including the subject.

How This Works

  • Public Key: The publicKey string is the PEM-encoded RSA public key from Laravel. It’s used to verify JWT signatures.
  • RSAPublicKeyConverter Method: Converts the PEM format key into a Java RSAPublicKey object using KeyFactory.
  • JwtDecoder Bean: Configures a JwtDecoder that uses the RSAPublicKey to verify JWT tokens.
  • Security Configuration: The securityFilterChain method sets up Spring Security to authenticate all requests using JWT tokens.

Should You Use a Static Public Key?

If your public key is not rotated or changed frequently, hard-coding the key directly into your Spring Boot application is a quick and straightforward solution. However, if the public key is rotated frequently (e.g., for security purposes or key management policies), this approach could become cumbersome and error-prone.

Handling Frequent Key Rotations

For environments where the public key might change regularly, it is advisable to expose an API endpoint in Laravel that provides the current public key. This way, your Spring Boot application can dynamically fetch and use the correct key. Here’s how you could do it:

  1. Create an Endpoint in Laravel: Create an endpoint in Laravel that returns the public key in JWK format.
  2. Fetch JWK in Spring Boot: Modify your Spring Boot configuration to fetch the JWK dynamically using
NimbusJwtDecoder.withJwkSetUri("https://your-laravel-app.com/oauth/jwks").build();

Explanation:

1. Mobile App Requests JWT Token: The mobile application sends login credentials to the Laravel Authorization Server to request a JWT token.

2. Laravel Issues JWT Token: The Laravel Authorization Server validates the credentials and issues a JWT token to the mobile app.

3. Mobile App Makes API Request: The mobile app sends an API request to the Spring Boot Resource Server, attaching the JWT token in the request header.

4. Spring Boot Validates JWT Token: The Spring Boot Resource Server needs to validate the JWT token’s signature using the public key from the Laravel Authorization Server.

5. Using Hard-Coded Public Key or JWK Endpoint: Spring Boot can either use a hard-coded public key or dynamically fetch it from a JWK endpoint exposed by Laravel.

6. Public Key for JWT Verification: If the key is fetched from an endpoint, Laravel responds with the public key. If the key is hard-coded, this step is skipped.

7. Spring Boot Processes the Request: Upon successful JWT validation, the Spring Boot Resource Server processes the request and sends the response back to the mobile app.

Conclusion

Integrating Laravel OAuth with a Spring Boot Resource Server can be seamless when leveraging JWT tokens and public key cryptography. Depending on your key rotation policy, you can either hard-code the public key in your Spring Boot application or expose a dynamic endpoint in Laravel to fetch it as needed.

By following this guide, you can securely extend your authentication architecture from Laravel to Spring Boot, ensuring consistent security across different modules and services.

Happy coding!

--

--

Mahfuzul Alam
Mahfuzul Alam

Written by Mahfuzul Alam

Software Engineer| AWS Certified Solutions Architect

No responses yet