Implement OAuth Authorization Server using Spring Authorization Server

The Authorization Server in OAuth has the task of issuing an access token that allows the Client Application to use this access token to request the resource it needs to use. The Resource Server will validate this access token with the Authorization Server every time the Client Application requests to resource to decide whether to allow the Client Application access to this resource. You can use many other open sources such as Keycloak, Spring Security OAuth (deprecated), or a new Spring project called Spring Authorization Server to implement this Authorization Server. In this tutorial, I will show you how to use Spring Authorization Server to implement OAuth Authorization Server!

First, I will create a new Spring Boot project with Spring Web Starter, Spring Security Starter and OAuth2 Authorization Server Starter:

to make an example.

Result:

With the auto-configuration mechanism, Spring Boot currently supports the Spring Authorization Server, so you only need to configure some required information such as Client Application information and user information to be able to up and run an Authorization Server with Spring Authorization Server.

In this tutorial, I will not use this auto-configuration mechanism of Spring Boot!

Authorization Server configuration

First, I will create a new AuthorizationServerConfiguration class to configure the Authorization Server.

By default, the Spring Authorization Server supports the OAuth2AuthorizationServerConfiguration class with default configurations for an Authorization Server. If you take a look at the code of this class, you will see that it defines an applyDefaultSecurity() method that initializes the OAuth2AuthorizationServerConfigurer object, with the purpose of applying the default configurations that this OAuth2AuthorizationServerConfigurer class defines:

As you can see, the applyDefaultSecurity() method defines security for the default endpoints of an Authorization Server.

Class OAuth2AuthorizationServerConfiguration also defines a bean for the SecurityFilterChain class that calls the applyDefaultSecurity() method to register these default configurations with Spring Security of Authorization Server.

You can import this OAuth2AuthorizationServerConfiguration class using Spring’s @Import annotation to use these default configurations:

or if you want to add something custom code, then declare a bean for the SecurityFilterChain class and call the applyDefaultSecurity() method as follows:

Here, I added more code so that if the user does not have permission to request the default endpoints of an Authorization Server, the Authorization Server will redirect to the login page.

With an Authorization Server, an important thing that we need to do is define the JSON Web Key to verify the information in the access token that the user requested to the Resource Server, issued by the Authorization Server. A JwtDecoder bean with an object of the JWKSource class is required to complete the configuration of this Authorization Server. We can define beans for these objects as follows:

You also need to declare an additional bean of the AuthorizationServerSettings class to complete the configuration information for the Authorization Server.

The AuthorizationServerSettings class allows us to customize the default Spring Authorization Server configurations related to the Issuer Identifier, the JWK Set endpoint, and some other settings. You can configure the bean for the AuthorizationServerSettings class as follows:

Spring Security configuration

When the Authorization Server redirects to the login page because the user is not authenticated, we need to define another SecurityFilterChain to handle this request and all other requests of the Authorization Server. Because the OAuth2AuthorizationServerConfiguration class only defines security for the default endpoints of the Authorization Server.

We can define this SecurityFilterChain as follows:

At this point, the login page will display if the user is not logged in.

Register client with Authorization Server

Spring Authorization Server uses the RegisteredClient class to declare the information of a client registered with the Authorization Server and uses the implementation of the RegisteredClientRepository interface to store the information of all these clients.

We can declare client information using memory or a certain database:

Implement OAuth Authorization Server using Spring Authorization Server

For simplicity, I will use memory as follows:

There are several important properties that a client must have: client ID and authorization grant type enabled for this client ID.

Client ID, I don’t need to explain, right? For authorization grant type, Spring Authorization Server supports all grant types of OAuth 2.

Client secret depends on the client type we want to define, if our client is confidential, see also Client types in OAuth 2.0, Client secret is mandatory. Here, you need to declare how to encrypt the client secret with PasswordEncoder, if you don’t want to encrypt it for testing purposes, we can use NoOpPasswordEncoder by declaring “{noop}” at the beginning of the client secret as I did above. Remember this is for testing purposes only!

The Client Authentication method is also required if our client is confidential, declared to define how we authenticate with some endpoints using client secret.

Depending on the grant type of the client you are defining, some other required information that we need to declare. For example, in my case, I am defining a client with grant type authorization_code, so I have to define a redirect_uri. Here, I will use the tool https://oidcdebugger.com/ to get the authorization code, so I define the redirect_uri with the value https://oidcdebugger.com/debug as you can see.

Depending on your needs, let’s define client information accordingly.

Register user with Authorization Server

User information logged into the Authorization Server, I use memory with the following declaration:

OK, at this point, we have completed the basic configuration for the Authorization Server.

To check the results, I will use the tool https://oidcdebugger.com/ as I mentioned above, with the following declaration:

Click Send Request on this page, you will see the Authorization Server login page displayed as follows:

Implement OAuth Authorization Server using Spring Authorization Server

Log in with the information we have declared above, you will see the following results:

Implement OAuth Authorization Server using Spring Authorization Server

Using this authorization code along with the client secret that we have declared, you can get the access token for this client as follows:

Implement OAuth Authorization Server using Spring Authorization Server

4.3/5 - (6 votes)

138 thoughts on “Implement OAuth Authorization Server using Spring Authorization Server

  1. Hi, This article is great and solve many puzzles. I still have a questions that hope you can answer

    How the client ID huongdanjava1 correlating to the user admin/password with role ADMIN. In case the auth server need to support more than one client ID

  2. Can we catch 401 unauthenticated error message in springboot code? As it is required in our usecase because we can to take some action if request is found to be unauthenticated (error code 401).

      1. I had followed https://thomasandolf.medium.com/spring-security-jwts-getting-started-ebdb4e4f1dd1 article to setup springboot security. The authorization server I am using is AWS cognito.

        Everything is working fine w.r.t authentication and authorization working. But as I said I have additional requirement to fetch 401 error message for unauthenticated users and do some action. Bottleneck is that I am unable to catch 401 error at springboot level using above stated article implementation.

  3. Hi,
    I would like to ask if you have got any experience with an angular app as the client. I am trying the “angular-oidc.js” library to implement Authorization Code Flow +PKCE on the client side to make things simpler but somehow my frontend is blocked by the authorization server due to the CORS issue. In your configuration classes, I see no “CORS policy” code. How do you think one could configure the CORS policy in the configuration classes?. Thank you very much for your help in advance.

  4. Ricardo Legorreta

    Reply

    How can I add an extra endpoint to the Authorization Server. I tried to use the securityFilterChain with no success. It sends me an exception like: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [myendpoint], template might not exist or might not be accessible by any of the configured Template Resolvers.

    What I am doing wrong?

  5. This example doesn’t work anymore with authorization server 1.0.
    Also, even using the authorization server version, the postman test never works. It keeps bringing up a HTML printout of a sign-in page, which the example stated here never addresses.

  6. I am trying spring auth server 1.0.0 and really facing issues. I tried it before with 0.2.0 and the examples worked out of the bax.

    Now I am trying federated spring auth server 1.0.0 samples with messages-client from https://github.com/spring-projects/spring-authorization-server and gives following error

    {“exception”:”[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse] and content type [text/html;charset=utf-8]”,”timestamp”:1674107981336}

    This I am getting with local server as well as with google as a federated oauth server. HAs anybody got it working for 1.0.0?

  7. Good guide Thank you,
    My question is : if you are already Register user with Authorization Server so Is it important to Register client with Authorization Server again ? and what is the difference ?

  8. Hi..

    Looking for suggestions on implementing security on spring-boot microservices integrated with angular UI. I have an external identity provider(Ping Federate) to support SSO and all user roles/authorities are maintained in the application database.

    What is the best approach to secure APIs? If Oauth is recommended way, how to implement it(Stateless).

    Should the Authorization Server be customized to connect to the
    identity provider Authorization Server and generate tokens from the custom Authorization Server?

    or

    Oauth2 client should generate tokens by loading user details from the database after successful authentication with the identity provider?

    Any code samples along with suggestions will be appreciated.

  9. Hi Khanh,

    Thanks for your tutorial. I cant seem to get the code to work 🙁
    When I am prompted to login I use “admin” & “password” as the credentials but I keep getting Bad Credentials… In my code I have this bean as you described:

    @Bean
    UserDetailsService users() {
    UserDetails user = User.withDefaultPasswordEncoder()
    .username(“user1”)
    .password(“password”)
    .roles(“USER”)
    .build();
    return new InMemoryUserDetailsManager(user);
    }

    But In the debug log I get:

    2022-05-23 11:49:19.561 DEBUG 44060 — [nio-8000-exec-1] o.a.t.util.http.Rfc6265CookieProcessor : Cookies: Parsing b[]: JSESSIONID=61B94763D6232EC0BC149860811BFE45
    2022-05-23 11:49:19.561 DEBUG 44060 — [nio-8000-exec-1] o.a.catalina.connector.CoyoteAdapter : Requested cookie session id is 61B94763D6232EC0BC149860811BFE45
    2022-05-23 11:49:19.561 DEBUG 44060 — [nio-8000-exec-1] o.a.c.authenticator.AuthenticatorBase : Security checking request POST /login
    2022-05-23 11:49:19.561 DEBUG 44060 — [nio-8000-exec-1] org.apache.catalina.realm.RealmBase : No applicable constraints defined
    2022-05-23 11:49:19.561 DEBUG 44060 — [nio-8000-exec-1] o.a.c.authenticator.AuthenticatorBase : Not subject to any constraint
    2022-05-23 11:49:19.562 DEBUG 44060 — [nio-8000-exec-1] o.s.security.web.FilterChainProxy : Securing POST /login
    2022-05-23 11:49:19.562 DEBUG 44060 — [nio-8000-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
    2022-05-23 11:49:19.562 DEBUG 44060 — [nio-8000-exec-1] org.apache.tomcat.util.http.Parameters : Set encoding to UTF-8
    2022-05-23 11:49:19.562 DEBUG 44060 — [nio-8000-exec-1] org.apache.tomcat.util.http.Parameters : Start processing with input [username=user1&password=password&_csrf=77f2d76f-52d5-402d-9dd1-3d9493b5e794]
    2022-05-23 11:49:19.617 DEBUG 44060 — [nio-8000-exec-1] o.s.s.a.dao.DaoAuthenticationProvider : Failed to find user ‘user1’
    2022-05-23 11:49:19.618 DEBUG 44060 — [nio-8000-exec-1] o.s.s.web.DefaultRedirectStrategy : Redirecting to /login?error
    2022-05-23 11:49:19.618 DEBUG 44060 — [nio-8000-exec-1] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
    2022-05-23 11:49:19.618 DEBUG 44060 — [nio-8000-exec-1] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
    2022-05-23 11:49:19.618 DEBUG 44060 — [nio-8000-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request

    Do you know why it cant find user1?

    Thanks in advance!

    Peter

  10. Greetings. Could you answer why I have cors problems when I make a request from the frontend server to oauth2/token?

  11. can this authorization server also be an oauth2 client and a resource server? I am not sure how to plug in an javascript client app with multiple logins options like this server and google at the same time?

  12. Victor van den Hoven

    Reply

    Hello Khanh Nguyen,

    Since the “Spring Security OAuth” is deprecated now, I am trying to migrate my old AuthorizationServerConfigurerAdapter-based application to the new Spring Authorization Server.

    Your blog is helping me a lot, but the following is unclear to me.

    Would it be possible to get an access-token from the authorization-server without a login-form that pops-up for username password?

    In my old “Spring Security OAuth”-implementation the oauth2-client that wants to get an access-token, just posts a request with the following multiValueMap:
    MultiValueMap map = new LinkedMultiValueMap();
    map.add(“client_id”, ….);
    map.add(“client_secret”, ….);
    map.add(“scope”, “session-type:Analyst”);
    map.add(“grant_type”, “password”);
    map.add(“username”, restApiUserName);
    map.add(“password”, restApiUserPassword);

    and the authorization-server figured-out by means of a UserDetailsService-bean that the credentials were ok and returned the access-token, without asking for it.

    I can not get the Spring authorization-server to work like this.

      1. Victor van den Hoven

        Reply

        Hi, thanks very much for your answer!
        I understand that password grant type is deprecated.
        My client is a machine so it can not enter any password.
        Would it be possible to work with authorization code grant type without out a login form?

  13. Hi, Thanks for this article It helped me alot to understand the things.
    When I am hitting url for authorization code it’s showing me login page but after entering the user details it is redirecting me to 401.

  14. Hi, Can I get the code from /authorize endpoint automatically using api without the login page? I wanted to replicate the password grant flow by using the authorization code flow since it is not anymore supported. Thanks in advance.

  15. How can aggregate AuthenticationManagerBuilder with WebSecurityConfigurerAdapter to?

    I have:
    public class SecurityConfig extends WebSecurityConfigurerAdapter

    but extends WebSecurityConfigurerAdapter generate conflicts with:
    public SecurityFilterChain defaultSecurityFilterChain

    and i need AuthenticationManagerBuilder for save authentication session and add user details service for more information in token jwt.

  16. Hi, thanks for the article, it was very helpful. I have set up my authorization server successfully and am able to retrieve the token but when I use the bearer token to make a request I get this error {
    "timestamp": "2021-12-06T09:41:03.911+00:00",
    "status": 403,
    "error": "Forbidden",
    "path": "/auth/user"
    }

    1. Here is my security config

      @Bean
      SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
      http
      .authorizeRequests()
      .antMatchers("/actuator/**").permitAll()
      .antMatchers("/routes/**").permitAll()
      .antMatchers("/h2-console/**").permitAll()
      .antMatchers("/oauth2/**").permitAll()
      .antMatchers(HttpMethod.POST, "/auth/user").permitAll()
      // .antMatchers(HttpMethod.GET, "/auth/user").hasAuthority(AuthoritiesConstants.ADMIN)
      .antMatchers(HttpMethod.GET, "/auth/user").hasAuthority("SCOPE_read")
      // .access("hasAuthority('SCOPE_read')")
      // .access("hasAuthority('" + AuthoritiesConstants.ADMIN + "')")
      // .access("hasAuthority('SCOPE_read') and hasAuthority('" + AuthoritiesConstants.ADMIN + "')")
      .anyRequest().authenticated()
      .and()
      .csrf().disable()
      .headers().frameOptions().disable()
      .and()
      .formLogin(withDefaults());
      return http.build();
      }

      // client configs

      @Bean
      @Order(Ordered.HIGHEST_PRECEDENCE)
      public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
      OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
      return http.formLogin(Customizer.withDefaults()).build();
      }

      private RegisteredClient webClient() {
      return RegisteredClient.withId("98a9104c-a9c7-4d7c-ad03-ec61bcfeab36")
      .clientId(authProps.getClientId())
      .clientName(authProps.getClientName())
      .clientSecret(authProps.getClientSecret())
      .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
      .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
      .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
      .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
      .redirectUri("http://127.0.0.1:8080/authorized")
      .scope("create").scope("read").scope("write").scope("update").scope("delete")
      .tokenSettings(tokenSettings)
      .build();
      }

  17. Hmm… It does work well with “https://oidcdebugger.com/debug” but does not work if redirect_uri is “http://localhost:8080/authorize”. 🤔

    If I update the code to use .redirectUri(“http://localhost:8080/authorize”) then it the authorization server should redirect to http://localhost:8080/authorize with the authorization code attached. But it does not redirect… Instead, it displays the /error page with the following message.

    “There was an unexpected error (type=None, status=999).”

      1. hmm… The Debug mode is informative but it does not tell why it is redirecting to /error..


        Start processing with input [username=test&password=12345&_csrf=19bb6ea1-4b74-4daf-b640-2adecfe5bdd7]

        Authenticated user

        Changed session id from 84DEA2449BF8248B2CDCA1F001502B1A

        Replaced CSRF Token

        Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=test, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=84DEA2449BF8248B2CDCA1F001502B1A], Granted Authorities=[ROLE_ADMIN]]

        Redirecting to http://localhost:8081/error?client_id=client1&redirect_uri=http://localhost:8080/authorize&scope=openid&response_type=code

        Strange… if redirect_uri is set to https://oidcdebugger.com/debug then it works just fine…

        1. Exactly i too had the same problem… only for localhost , we have this problem… i face this problem when using swagger for the moment
          only if i login via 127.0.0.1:8080/swagger-ui.html .. it redirects with http://127.0.0.1:9500/swagger-ui/oauth2-redirect.html other wise it goes to localhost redirection and failing with error

          if you find any solution .. please let me know as well

  18. Great Article !! Thanks a lot
    Can we have our own custom authentication for validating user credentials as our organization has different method for password encryption

  19. I had implemented the custom user detail service injection point is working and retrieving the user from database but it does not authenticate here is my code. Can you guide me on what I am doing wrong here? you can see the stack trace also

    import com.huongdanjava.dto.UserPrincipal;
    import com.huongdanjava.model.User;
    import com.huongdanjava.repository.UserRepository;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.transaction.annotation.Transactional;

    public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // TODO Auto-generated method stub
    System.out.println(“UserName===>”+username);
    User user = userRepository.findByUsername(username)
    .orElseThrow(() ->
    new UsernameNotFoundException(“User not found with username or email : ” + username)
    );
    System.out.println(“user=====>”+user);
    return UserPrincipal.create(user);
    }

    }

    2021-11-18 12:54:25.865 TRACE 18228 — [nio-8080-exec-5] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=BB05EE690F46417798A83E5CE1C21406], Granted Authorities=[ROLE_ANONYMOUS]]
    2021-11-18 12:54:25.868 TRACE 18228 — [nio-8080-exec-5] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (10/12)
    2021-11-18 12:54:25.872 TRACE 18228 — [nio-8080-exec-5] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (11/12)
    2021-11-18 12:54:25.873 TRACE 18228 — [nio-8080-exec-5] o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (12/12)
    2021-11-18 12:54:25.876 TRACE 18228 — [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Did not re-authenticate AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=BB05EE690F46417798A83E5CE1C21406], Granted Authorities=[ROLE_ANONYMOUS]] before authorizing
    2021-11-18 12:54:25.878 TRACE 18228 — [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Authorizing filter invocation [GET /custom-login] with attributes [permitAll]
    2021-11-18 12:54:25.884 DEBUG 18228 — [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Authorized filter invocation [GET /custom-login] with attributes [permitAll]
    2021-11-18 12:54:25.891 TRACE 18228 — [nio-8080-exec-5] o.s.s.w.a.i.FilterSecurityInterceptor : Did not switch RunAs authentication since RunAsManager returned null
    2021-11-18 12:54:25.893 DEBUG 18228 — [nio-8080-exec-5] o.s.security.web.FilterChainProxy : Secured GET /custom-login
    2021-11-18 12:54:25.905 TRACE 18228 — [nio-8080-exec-5] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
    2021-11-18 12:54:25.915 DEBUG 18228 — [nio-8080-exec-5] w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
    2021-11-18 12:54:25.924 DEBUG 18228 — [nio-8080-exec-5] w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
    2021-11-18 12:54:25.927 DEBUG 18228 — [nio-8080-exec-5] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request

    1. You also need change the configuration for authorizationServerSecurityFilterChain in the class AuthorizationServerConfiguration as follows:

      1. I have successfully setup my own login page and client application. But after successful login it does not redirect to client application its again redirect to login page itself.

        Am I missing anything

      2. Tnx a lot Khanh, but I already fixed this.
        I have another question 🙂 Do you maybe have or know some tutorial for implementing authorization code flow with PKCE in the spring authorization server?
        Tnx in advance!

      3. Hey, i have a login form available at port 3000, but if i try to set the value of loginPage() as
        (form.loginPage(“http://localhost:3000/login”)), I get a CORS error. I’m calling the “https://localhost:8081/login” endpoint from the UI. Do you have any suggestion?

  20. Hi , thanks for the awesome example, i make it work in that way.

    I got a doubt , i got this postman request
    =================================================
    POST /oauth2/token HTTP/1.1
    Host: localhost:2005
    Authorization: Basic VERTQWRtaW46VERTQWRtaW4=
    Content-Type: application/x-www-form-urlencoded
    Cookie: JSESSIONID=0397FD3D6FAD1C2282DB26CC8151BD41
    Content-Length: 29

    grant_type=client_credentials
    ==================================================

    As you can see the request is similar to yours, the only difference is that in the headers i am sending a basic authorization coded, i want to make it work with your code, could you tell me what parts of the code need to be changed ??

    Regards.

      1. I’ve already understood but I would like to clarify.
        1 – SecurityFilterChain to choose auth server
        2 SecurityFilterChain to login the user credentials
        yes?

        1. The SecurityFilterChain in AuthorizationServerConfiguration class in my example is for defining security for the default endpoints of an Authorization Server. If you take a look at OAuth2AuthorizationServerConfigurer class, you will see the definition for endpointsMatcher which define all the default endpoints of an Authorization Server.
          The SecurityFilterChain in SpringSecurityConfiguration class for other endpoints of your application including the /login endpoint.
          I hope it is clear for you now.

  21. Hi!
    Maybe I am doing something wrong but the last action to get the token does not work. There is a redirect to the login page

  22. Hi Khanh

    It is really a good example.

    Could you tell me how we can retrieve user details from db (I am using cassandra db) to authenticate an user and client details instead of using inmemory.

      1. Thanks for the reply.
        I tried to implement both the approaches, for registeredClient in my scenario I am having few issues wrt cassandra db to implement this, which I will debug more.
        For retrieving user details from db, I injected UserDetailsService interface by autowiring in webconfiguration class and provided separate class implementation of the UserDetailsService loadUserByUsername(String username) method. This worked for me with noOpPasswordEncoder. But facing issue with BcryptPasswordEncoder. Bcrypted (12 folds) Password is stored in my db for the user and while validating it is throwing error similar to “it does not look like Bcrypt password”. The same password is getting validated with old auth server.

        So if possible in your next article kindly include these scenarios.

  23. Thanks Khanh for the good article! However, I tried to enable https for the auth server but getting 401 when accessing a protected api from a resource server (spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://127.0.0.1:9000/oauth2/jwks). Can you please show me how to config it correctly?

      1. Yes, I did. But in that tutorial the setting is spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080. I’d like to set it point to https like this:
        spring.security.oauth2.resourceserver.jwt.issuer-uri=https://localhost:8080

  24. Hi Khanh Nguyen,


    @Bean
    public UserDetailsService users() {
    UserDetails user = User.withDefaultPasswordEncoder()
    .username("admin")
    .password("password")
    .roles("ADMIN")
    .build();

    return new InMemoryUserDetailsManager(user);
    }

    Can you explain detail more about create UserDetailsService jdbc connection? Thanks

  25. Hello, how can I load customized page instead of default login page provided by Spring.
    Please guide me on this, here is the code snippet:

    —Client end configuration ——-
    @EnableWebSecurity
    public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
    .authorizeRequests(authorizeRequests ->
    authorizeRequests.anyRequest().authenticated()
    )
    .oauth2Login(oauth2Login ->
    oauth2Login.loginPage(“/oauth2/authorization/apicenter-client-oidc”))
    .oauth2Client(withDefaults());
    return http.build();
    }
    }

    @EnableWebSecurity
    public class DefaultSecurityConfig {

    —- Oauth Server end configuration ————————–

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    try {
    http.authorizeRequests(
    authorizeRequests -> authorizeRequests
    .anyRequest().authenticated())
    .formLogin(Customizer.withDefaults());
    return http.build();
    } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }

  26. Hello, how can I load customized page instead of default login page provided by Spring.
    Please guide me on this, here is the code snippet:
    @EnableWebSecurity
    public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
    .authorizeRequests(authorizeRequests ->
    authorizeRequests.anyRequest().authenticated()
    )
    .oauth2Login(oauth2Login ->
    oauth2Login.loginPage(“/oauth2/authorization/apicenter-client-oidc”))
    .oauth2Client(withDefaults());
    return http.build();
    }
    }

    1. I think you can declare a RegisteredClient with authorization_code grant type. Example:

  27. Hi, thanks for this very good article it helps me a lot
    how i can add more information to jwt access_token (jwt Claims)?

      1. i finally find official solution

        @Bean
        OAuth2TokenCustomizer jwtCustomizer() {
        return context -> {
        context.getClaims().claim("key", "value");

        };
        }

    1. @Bean
      OAuth2TokenCustomizer jwtCustomizer() {
      return context -> {
      Authentication principal = context.getPrincipal();
      if (context.getTokenType().getValue().equals(“access_token”) && principal instanceof UsernamePasswordAuthenticationToken) {
      Set authorities = principal.getAuthorities().stream()
      .map(GrantedAuthority::getAuthority)
      .collect(Collectors.toSet());
      User user = (User) principal.getPrincipal();
      context.getClaims().claim(“authorities”, authorities);
      context.getClaims().claim(“userId”, user.getUserId());
      }
      };
      }

      inside if condition statements can be modified as per your requirement.

  28. Great article. Thanks
    I’m looking to obtain access code without using the login form. Can you tell me the configuration I need for that? Also, is it only supporting POST to /oauth2/authorize – or can that be customised?
    Thanks

    1. You can declare a RegisteredClient with client_credentials as follows:

      1. Hey, thank you for the article. It is the best one available! On the subject of accessing a token without the login form, I have downloaded your code from git (linked further down in comments) but I cannot get my postman request to work. Would you be able to share the CURL for obtaining a token without the login? thanks again 🙂

        1. Here is it @Niall:

        1. Hi! I have a spring-boot-starter-oauth2-client working with Authorization Code grant with PKCE (keycloak auth server). To do so, I’ve configured the client with:
          authorization-grant-type: authorization_code
          client-authentication-method: none
          And it’s working fine (create the code_challenge, etc.)
          What I’m trying to now is make this client working with the Spring OAuth Server, but I cannot find how to add a RegisteredClient with Authorization Code grant with PKCE,
          The class AuthorizationGrantType (spring-security-oauth2-core-5.5.3) does not have a constant for PKCE

          1. You can declare a RegisteredClient like below:

  29. E có thử dùng password grant_type nhưng khi gửi request để lấy access token thì lại báo là ‘unsupported_grant_type’. Mình có cần config thêm gì không ạ?

      1. I added 2 line into RegisteredClient
        .authorizationGrantType(AuthorizationGrantType.PASSWORD)

        authorization_code grant type worked fine but the password grant type didn’t
        And this is my declaration
        RegisteredClient registeredClient1 =
        RegisteredClient.withId(UUID.randomUUID().toString())
        .clientId(“hb_client_id_1”)
        .clientSecret(passwordEncoder.encode(“hb_client_secret_1”))
        .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
        .authorizationGrantType(AuthorizationGrantType.PASSWORD)
        .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
        .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
        .redirectUris(redirectUris -> {
        redirectUris.add(“https://oauth.pstmn.io/v1/callback”);
        redirectUris.add(“http://localhost:8080/login/oauth2/code/hb-gateway”);
        redirectUris.add(“http://localhost:8080/authorized”);
        })
        .scope(OidcScopes.OPENID)
        .scope(“notification.read”)
        .tokenSettings(TokenSettings.builder()
        .refreshTokenTimeToLive(Duration.ofDays(7))
        .accessTokenTimeToLive(Duration.ofMinutes(5))
        .build())
        .build();

  30. Good and much needed one. What is the URL for validating the token /oauth/check_token is not working. i have custom flow to validate the token.

    1. You can declare a RegisteredClient as follows:

  31. Any pointers on how to use Spring-Authorization-Server project to connect to an identity provider (eg. Auth0) – and receive id-token and access-token and return own Spring-Authorization-Server access-token?

  32. Hi, thanks for this very good article it helps me a lot… a have trouble in the last step when i want to POST /oauth2/token with code im getting a 401 from authorization server, i see in other guides that you have to pass a header “Authorization: Basic xxxxx”, dependes on your clientAuthenticationMethod (i suposed) but i dont know how to complete it

Add Comment