Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Browser testing is essential if you have a website or web applications that users interact with. Manual testing can be very helpful to an extent, but given the multiple browsers available, not to mention versions and operating system, testing everything manually becomes time-consuming and repetitive.

To help automate this process, Selenium is a popular choice for developers, as an open-source tool with a large and active community. What's more, we can further scale our automation testing by running on theLambdaTest cloud-based testing platform.

Read more through our step-by-step tutorial on how to set up Selenium tests with Java and run them on LambdaTest:

>> Automated Browser Testing With Selenium

Partner – Orkes – NPI EA (cat=Java)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LSS – NPI (cat=Spring Security)
announcement - icon

If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:

>> LEARN SPRING SECURITY

1. Introduction

In this tutorial, we’ll discuss the use of Proof Key for Code Exchange (PKCE) for OAuth 2.0 public clients.

2. Background

OAuth 2.0 public clients such as Single Page Applications (SPA) or mobile applications utilizing Authorization Code Grant are prone to authorization code interception attacks. A malicious attacker may intercept the authorization code from the authorization endpoint if the client-server communication happens over an insecure network.

If an attacker can access the authorization code, it can then use it to obtain an access token. Once the attacker owns the access token, it can access the protected application resources similar to a legitimate application user, thus, severely compromising the application. For instance, if the access token is associated with a financial application, the attacker may gain access to sensitive application information.

2.1. OAuth Code Interception Attack

In this section, let us discuss how an Oauth authorization code interception attack can occur: Authorization Code Grant Attack   The above diagram demonstrates the flow of how a malicious attacker can misuse the authorization grant code to obtain the access token:

  1. A legitimate OAuth application initiates the OAuth authorization request flow using its web browser with all required details
  2. The web browser sends the request to the Authorization server
  3. The authorization server returns the authorization code to the web browser
  4. At this stage, the malicious user may access the authorization code if the communication happens over an insecure channel
  5. The malicious user exchanges the authorization code grant to obtain an access token from the authorization server
  6. Since the authorization grant is valid, the authorization server issues an access token to the malicious application. The malicious application can misuse the access token to act on behalf of the legitimate application and access protected resources

The Proof Key for Code Exchange is an extension of the OAuth framework intended to mitigate this attack.

3. PKCE with OAuth

The PKCE extension includes the following additional steps with the OAuth Authorization Code Grant flow:

  • The client application sends two additional parameters code_challenge and code_challenge_method with the initial authorization request
  • The client also sends a code_verifier in the next step while it exchanges the authorization code to obtain an access token

First, a PKCE-enabled client application selects a dynamically created cryptographically random key called code_verifier. This code_verifier is unique for every authorization request. According to the PKCE specification, the length of the code_verifier value must be between 43 and 128 octets.

Besides, the code_verifier can contain only alphanumeric ASCII characters and a few allowed symbols. Second, the code_verifier is transformed into a code_challenge using a supported code_challenge_method. Currently, the supported transformation methods are plain and S256.  The plain is a no-operation transformation and keeps the code_challange value the same as of code_verifier. The S256 method first generates a SHA-256 hash of the code_verifier and then performs a Base64 encoding of the hash value.

3.1. Preventing OAuth Code Interception Attack

The following diagram demonstrates how PKCE extension prevents the access token theft: Authorization Code Grant Attack

  1. A legitimate OAuth application initiates the OAuth authorization request flow using its web browser with all required details, and additionally the code_challenge and the code_challenge_method parameters.
  2. The web browser sends the request to the authorization server and stores the code_challenge and the code_challenge_method for the client application
  3. The authorization server returns the authorization code to the web browser
  4. At this stage, the malicious user may access the authorization code if the communication happens over an insecure channel
  5. The malicious user attempts to exchange the authorization code grant to obtain an access token from the authorization server. However, the malicious user is unaware of the code_verifier that needs to be sent along with the request. The authorization server denies the access token request to the malicious application
  6. The legitimate application supplies the code_verifier along with the authorization grant to obtain an access token. The authorization server calculates the code_challenge from the supplied code_verifier and the code_challenge_method it stored earlier from the authorization code grant request. It matches the calculated code_challange with the previously stored code_challenge. These values always match and the client is issued an access token
  7. The client can use this access token to access application resources

4. PKCE With Spring Security

As of version 6.3, Spring Security supports PKCE for both servlet and reactive web applications. However, it is not enabled by default as not all identity providers support the PKCE extension yet. PKCE is automatically used for public clients when the client is running in an untrusted environment such as a native application or web browser-based application and the client_secret is empty or not provided and the client-authentication-method is set to none.

4.1. Maven Configuration

Spring Authorization server supports the PKCE extension. Thus, the simple way to include PKCE support for a Spring authorization server application is to include the spring-boot-starter-oauth2-authorization-server dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
    <version>3.3.0</version>
</dependency>

4.2. Register Public Client

Next, let us register a public Single Page Application client by configuring the following properties in the application.yml file:

spring:
  security:
    oauth2:
      authorizationserver:
        client:
          public-client:
            registration:
              client-id: "public-client"
              client-authentication-methods:
                - "none"
              authorization-grant-types:
                - "authorization_code"
              redirect-uris:
                - "http://127.0.0.1:3000/callback"
              scopes:
                - "openid"
                - "profile"
                - "email"
            require-authorization-consent: true
            require-proof-key: true

In the above code snippet, we register a client with client_id as public-client and client-authentication-methods as none. The require-authorization-consent requires the end-user to provide additional consent to access the profile and email scopes after the successful authentication. The require-proof-key configuration prevents the PKCE Downgrade Attack.

With require-proof-key configuration enabled, the authorization server does not allow any malicious attempt to bypass the PKCE flow without the code_challenge. The remaining configurations are standard configurations to register the client with the authorization server.

4.3. Spring Security Configuration

Next, let us define the SecurityFileChain configuration for the authorization server:

@Bean
@Order(1)
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
      .oidc(Customizer.withDefaults());
    http.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"), new MediaTypeRequestMatcher(MediaType.TEXT_HTML)))
      .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
    return http.cors(Customizer.withDefaults())
      .build();
}

In the above configuration, we first apply the authorization server’s default security settings. We then apply the Spring security default settings for OIDC, CORS, and Oauth2 resource servers. Let us now define another SecurityFilterChain configuration that will be applied to other HTTP requests, such as the login page:

@Bean
@Order(2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authorize) -> authorize.anyRequest()
      .authenticated())
      .formLogin(Customizer.withDefaults());
    return http.cors(Customizer.withDefaults())
      .build();
}

In this example, we use a very simple React application as our public client. This application runs on http://127.0.0.1:3000. The authorization server runs on a different port, 9000. Since these two applications are running on different domains, we will need to supply additional CORS settings so that the authorization server allows the React application to access it:

@Bean
CorsConfigurationSource corsConfigurationSource() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    config.addAllowedOrigin("http://127.0.0.1:3000");
    config.setAllowCredentials(true);
    source.registerCorsConfiguration("/**", config);
    return source;
}

We are defining a CorsConfigurationSource instance with the allowed origin, headers, methods, and other configurations. Note that in the above configuration, we are using the IP address 127.0.0.1 instead of localhost as the latter is not allowed. Lastly, let us define a UserDetailsService instance to define a user in the authorization server.

@Bean
UserDetailsService userDetailsService() {
    PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    UserDetails userDetails = User.builder()
      .username("john")
      .password("password")
      .passwordEncoder(passwordEncoder::encode)
      .roles("USER")
      .build();

    return new InMemoryUserDetailsManager(userDetails);
}

With the above configurations, we will be able to use the username john and password as the password to authenticate to the authorization server.

4.4. Public Client Application

Let us now talk about the public client. For demonstration purposes, we are using a simple React application as the Single Page Application. This application uses the oidc-client-ts library for client-side OIDC and  OAuth2 support. The SPA application is configured with the following configurations:

const pkceAuthConfig = {
  authority: 'http://127.0.0.1:9000/',
  client_id: 'public-client',
  redirect_uri: 'http://127.0.0.1:3000/callback',
  response_type: 'code',
  scope: 'openid profile email',
  post_logout_redirect_uri: 'http://127.0.0.1:3000/',
  userinfo_endpoint: 'http://127.0.0.1:9000/userinfo',
  response_mode: 'query',
  code_challenge_method: 'S256',
};

export default pkceAuthConfig;

The authority is configured with the address of the Spring Authorization server, which is http://127.0.0.1:9000. The code challenge method parameter is configured as S256. These configurations are used to prepare the UserManager instance, which we use later to invoke the authorization server. This application has two endpoints – the “/” to access the landing page of the application and the callbackendpoint that handles the callback request from the Authorization server:

import React, { useState, useEffect } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Login from './components/LoginHandler';
import CallbackHandler from './components/CallbackHandler';
import pkceAuthConfig from './pkceAuthConfig';
import { UserManager, WebStorageStateStore } from 'oidc-client-ts';

function App() {
    const [authenticated, setAuthenticated] = useState(null);
    const [userInfo, setUserInfo] = useState(null);

    const userManager = new UserManager({
        userStore: new WebStorageStateStore({ store: window.localStorage }),
        ...pkceAuthConfig,
    });

    function doAuthorize() {
        userManager.signinRedirect({state: '6c2a55953db34a86b876e9e40ac2a202',});
    }

    useEffect(() => {
        userManager.getUser().then((user) => {
            if (user) {
                setAuthenticated(true);
            } 
            else {
                setAuthenticated(false);
            }
      });
    }, [userManager]);

    return (
      <BrowserRouter>
          <Routes>
              <Route path="/" element={<Login authentication={authenticated} handleLoginRequest={doAuthorize}/>}/>
              <Route path="/callback"
                  element={<CallbackHandler
                      authenticated={authenticated}
                      setAuth={setAuthenticated}
                      userManager={userManager}
                      userInfo={userInfo}
                      setUserInfo={setUserInfo}/>}/>
          </Routes>
      </BrowserRouter>
    );
}

export default App;

5. Testing

We’ll use a React application with OIDC client support enabled to test the flow. To install the required dependencies, we need to run the npm install command from the application’s root directory. Then, we will start the application using the npm start command.

5.1. Accessing Application for Authorization Code Grant

This client application performs the following two activities: First, accessing the home page at http://127.0.0.1:3000 renders a sign-in page. This is the login page of our SPA application: Next, once we proceed with sign in, the SPA application invokes the Spring Authorization server with the code_challenge and the code_challenge_method: PKCE Request We can notice the request made to the Spring Authorization server at http://127.0.0.1:9000 with the following parameters:

http://127.0.0.1:9000/oauth2/authorize?
client_id=public-client&
redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Fcallback&
response_type=code&
scope=openid+profile+email&
state=301b4ce8bdaf439990efd840bce1449b&
code_challenge=kjOAp0NLycB6pMChdB7nbL0oGG0IQ4664OwQYUegzF0&
code_challenge_method=S256&
response_mode=query

The authorization server redirects the request to the Spring Security login page: Spring Security Login Page Once we provide the login credentials, the authorization requests consent for the additional Oauth scope profile and email. This is due to the configuration require-authorization-consent to true in the authorization server: Consent Required

5.2. Exchange Authorization Code for Access Token

If we complete the login, the authorization server returns the authorization code. Subsequently, the SPA requests another HTTP to the authorization server to obtain the access token. The SPA supplies the authorization code obtained in the previous request along with the code_challenge to obtain the access_token: Access Token Request For the above request, the Spring authorization server responds with the access token: Access Token Response Next, we access the userinfo endpoint in the authorization server to access the user details. We supply the access_token with the Authorization HTTP header as the Bearer token to access this endpoint. This user information is printed from the userinfo details: Welcome user

6. Conclusion

In this article, we’ve demonstrated how to use the OAuth 2.0 PKCE extension in a single-page application with a Spring Authorization Server. We started the discussion with the need for the PKCE for public clients and explored the configurations in the Spring Authorization server to use PKCE flow. Lastly, we leveraged a React application to demonstrate the flow.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LSS – NPI (cat=Security/Spring Security)
announcement - icon

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)