History of a bug

SpringBoot 2, OAUTH 2 and client_credentials in POST parameters

Rédigé par gorki Aucun commentaire

Problem :

Edited : due to a bug, see also (https://www.hoab.fr/springboot-2-oauth-2-and-tokenstore)

I try to implements client credentials OAUTH flow for a server which is resource and authentication server. (Goody summary here : https://www.slideshare.net/halyph/oauth2-and-spring-security)

There is plenty of posts on how to make an oauth 2 authentication server, (like authentication code flow  less with client_credentials mode...

And in these examples :

But if I easily found how to perform this kind of request : POST + BasicAuth(client_id/client_secret), I wasn't able to do :

POST + optional BasicAuth + client_id/client_secret in post parameters

Solution :

I activate DEBUG on spring security and see that filter chains do not have UsernamePasswordAuthenticationFilter, I plan to add the filter in websecurity as in some examples but it was not working.

After a lot of unsuccessful tries, I finally understand that I have to add a kind of UsernamePasswordAuthenticationFilter on OAUTH2 filter chain and not on the others (yes, there is multiple security filter chain....)

Going back to origins, I check AuthorizationServerConfigurerAdapter override method and ... eurêka !

As simple as add a




method... So simple, so hard to find.

But my AuthorizationServer is now :

Due to a bug, the code is now in (https://www.hoab.fr/springboot-2-oauth-2-and-tokenstore)

And a simple (for now) client detail service :

package com.example;

import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;

import java.util.Arrays;

public class MyClientDetailsService implements ClientDetailsService {

    private static final String CLIENT_CREDENTIALS = "client_credentials";
    private static final String REFRESH_TOKEN = "refresh_token";
    private static final String SCOPE_READ = "read";
    private static final String SCOPE_WRITE = "write";
    private static final String TRUST = "trust";
    private static final int VALID_FOREVER = -1;

    private static final String CLIENT_ID = "my-client";
    // encoding method prefix is required for DelegatingPasswordEncoder which is default since Spring Security 5.0.0.RC1
    // you can use one of bcrypt/noop/pbkdf2/scrypt/sha256
    // you can change default behaviour by providing a bean with the encoder you want
    // more: https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-encoding
    static final String CLIENT_SECRET = "{noop}my-secret";

    public ClientDetails loadClientByClientId(String s) throws ClientRegistrationException {
        if (s.equals(CLIENT_ID)) {
            BaseClientDetails client = new BaseClientDetails();
            client.setAuthorizedGrantTypes(Arrays.asList(CLIENT_CREDENTIALS, REFRESH_TOKEN));
            client.setScope(Arrays.asList(SCOPE_READ, SCOPE_WRITE, TRUST));
            return client;
        return null;


And my resource server :

Due to a bug, the code is now in (https://www.hoab.fr/springboot-2-oauth-2-and-tokenstore)

The CORS filter from (https://www.hoab.fr/springboot-2-oauth-2-options-and-cors) :

package com.example;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SimpleCorsFilter implements Filter {

    public SimpleCorsFilter() {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
        } else {
            chain.doFilter(req, res);

    public void init(FilterConfig filterConfig) {

    public void destroy() {

And a protected resource :

package com.example;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

public class Metrics {

    String getToken() {
        return "OK";





Écrire un commentaire

Quelle est le sixième caractère du mot iz3de71 ?

Fil RSS des commentaires de cet article