How to Add a Confirmation Step to Spring Boot 2 OAuth2 Authorization Flow

This guide explains why Spring Boot 2’s OAuth2 authorization code flow skips the user‑approval page, walks through the relevant source code, and shows how to customize the confirmation and login pages, configure controllers and security settings, and verify the changes with tests.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Add a Confirmation Step to Spring Boot 2 OAuth2 Authorization Flow

Problem Overview

In the OAuth2 authorization code flow, after entering correct credentials the user is redirected directly to the redirect_uri without seeing a confirmation page, even when the auto‑approval setting is set to false.

Investigation of Spring Security Code

The AuthorizationEndpoint class is examined. First, the extractScopes method retrieves all scopes for the client and stores them in an AuthorizationRequest object. Then the checkForPreApproval method compares the scopes from the request with the client’s stored scopes. Because they match, the framework automatically marks the request as approved, resetting the auto‑approval flag to true and bypassing the confirmation step.

Customizing the Confirmation Page

To force a user‑approval step, the source is modified to keep the auto‑approval flag false. A custom HTML page is created (Thymeleaf template) to display the client ID and an "Agree/Authorize" button.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>授权确认</title>
    <link rel="stylesheet" th:href="@{/resources/bootstrap/css/bootstrap.min.css}" />
    <style type="text/css">
        /* simplified CSS omitted for brevity */
    </style>
</head>
<body>
    <div class="loginContainer">
        <div class="pageTitle"><h3>授权确认</h3></div>
        <div class="loginPanel">
            <div class="loginTitle">
                <p th:text="'客户端:' + ${clientId} + ' 请求授权'"></p>
            </div>
            <div class="loginContent">
                <form method="post" action="/oauth/authorize">
                    <input type="hidden" name="user_oauth_approval" value="true">
                    <button class="btn btn-sm btn-primary" type="submit">同意/授权</button>
                </form>
            </div>
        </div>
    </div>
    <script th:src="@{/resources/jquery/jquery.min.js}"></script>
    <script th:src="@{/resources/bootstrap/bootstrap.min.js}"></script>
</body>
</html>

A corresponding controller returns this view:

@Controller
@SessionAttributes("authorizationRequest")
public class GrantController {
    @RequestMapping("/custom/confirm_access")
    public ModelAndView getAccessConfirmation(Map<String, Object> model) {
        AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
        ModelAndView view = new ModelAndView();
        view.setViewName("grant-page");
        view.addObject("clientId", authorizationRequest.getClientId());
        return view;
    }
}

Mapping the Custom Page

The core security configuration is updated to map the custom confirmation endpoint:

Custom Login Page

A simple login page is also created to replace the default Spring Security login form, along with a controller that returns the view name:

@Controller
public class LoginController {
    @RequestMapping("/sign/login")
    public String login() {
        return "login";
    }
}

The login HTML (Thymeleaf) contains fields for username and password and displays error messages when authentication fails.

Security Configuration

The WebSecurityConfig class extends WebSecurityConfigurerAdapter and:

Disables CSRF.

Permits access to the custom login URLs.

Requires authentication for all other requests.

Configures form‑login to use the custom login page.

Sets session management with a maximum of one concurrent session.

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/sign/login", "/login").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/sign/login")
                .permitAll()
            .and()
            .logout()
                .logoutUrl("/sign/logout")
                .invalidateHttpSession(true)
            .and()
            .sessionManagement()
                .maximumSessions(1);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }
}

Testing the Changes

After redeploying, logging in redirects to the custom confirmation page, allowing the user to explicitly approve the client. The same flow works for the custom login page. Screenshots in the original article confirm successful navigation to the confirmation screen and final completion of the OAuth2 authorization code flow.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootOAuth2AuthorizationCustom Login
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.