shadows
shadows

Reputation: 141

Spring Authorization Server with AuthorizationGrantType.PASSWORD

I want to build an authorization server with Spring Authorization Server project. For now I want to use AuthorizationGrantType.PASSWORD.

I developed a demo project from the samples of Spring Authorization Server project. However, when I try to get token with the http://localhost:9000/oauth2/token?grant_type=password&username=user&password=pass (I'm using client_id and client_secret as basic auth in Postman) request, I'm getting 403.

What I'm missing here?

Dependencies: spring-boot-starter-web, spring-boot-starter-security, spring-security-oauth2-authorization-server (version: 0.2.2)

AuthorizationServerConfig class:

import java.security.*;
import java.security.interfaces.*;
import java.util.UUID;
import org.springframework.context.annotation.*;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.server.authorization.client.*;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {
    
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("client1")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.PASSWORD)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }
    
    @Bean
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder()
                .issuer("http://auth-server:9000")
                .build();
    }
    
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }
    
    private static RSAKey generateRsa() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
    }
    
    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;
    }    
}

DefaultSecurityConfig class:

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.*;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class DefaultSecurityConfig {
    
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .cors().disable()
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests
                                .anyRequest()
                                .authenticated()
                );
        return http.build();
    }
    
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("user")
                .password("pass")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(userDetails);
    }    
}

There is this message in the log that I found It may be related:

o.s.s.w.a.i.FilterSecurityInterceptor    : Failed to authorize filter invocation [POST /oauth2/token?grant_type=password&username=user&password=pass] with attributes [authenticated]

Upvotes: 4

Views: 9964

Answers (2)

akuma8
akuma8

Reputation: 4691

I need that grant in my applications even if it was deprecated. I think that when we manage our own clients, it makes sens to use that grant. I managed to add it by following this guide in the official documentation. It took me 2 hours to implement it and test it.

EDIT: Take a look at my gist to see the implementation.

Upvotes: 6

shadows
shadows

Reputation: 141

Looks like password grant_type is not supported. Probably that's why I was getting error.

source: https://github.com/spring-projects/spring-authorization-server/issues/126

Upvotes: 7

Related Questions