Search code examples
spring-bootspring-securityspring-authorization-server

Authorization and Resource Server in same application using Spring Security and jwt


I am new to Spring Security, I want to implement Authorization server and Resource Server in same application using JWT. I am using Java 17, Spring Boot 3. I have found references there are either using separate application for each server and client and most of them are deprecated. I am also not sure about the complete flow. Can anyone please share any reference or details code and config.

I have tried following https://stackoverflow.com/questions/70949390/spring-authorization-and-resource-on-same-server but don't know whether following configurations are correct and how to use in Login and SignUp API.Am I doing it correctly, whatelse I will need to configure because earlier there were annotations like @EnableAuthorizationServer and @EnableResourceServer which are not available. Can someone share any working example

Following is my SecurityConfig class

@Configuration
@EnableWebSecurity
public class SecurityConfig{
    
    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, CorsConfigurationSource corsConfigurationSource) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());

http.exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(
                                new LoginUrlAuthenticationEntryPoint("/login"))
                )
                .oauth2ResourceServer((resourceServer)-> resourceServer.jwt(Customizer.withDefaults()));
        
http.cors(customizer -> customizer.configurationSource(corsConfigurationSource));
        return http.build();

    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                //.oauth2ResourceServer(OAuth2AuthorizationServerConfigurer::jwt)  //jwt method is not resolved
                .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()))//https://github.com/spring-projects/spring-security/issues/13446
                // Form login handles the redirect to the login page from the
                // authorization server filter chain
                .formLogin(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("oidc-client")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
                .postLogoutRedirectUri("http://127.0.0.1:8080/")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

        return new InMemoryRegisteredClientRepository(oidcClient);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }
}

I am using following dependencies

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

Solution

  • The code that you've shared looks like it was adapted from the getting started example in the reference documentation. Because you have added .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults())), your existing config is all that is required.

    You can use any access token obtained by the OAuth2 client (which was created by this auth server) to make a protected resources request to the authorization server. Simply define custom endpoints in your authorization server application for your use case and you're good to go.