Search code examples
javaspring-bootmavenoauth-2.0graphql

How do I configure a client Java application which must request a token from an authorization server using a 'password' grant type?


I need to implement a client Java application with Maven which makes requests from a resource server. In order for a request to be authorized a bearer token has to be provided in the header of the request. To obtain the token, another request has to be done before. The server provides me all parameters needed to make the token request: client_id, client_secret, username, password, etc. The grant_type of the token request is 'password'.

The server is based on GraphQL so I'm using the following Maven plugin in order to generate the Java classes which perform the GraphQL queries: https://github.com/graphql-java-generator/graphql-maven-plugin-project

They provide a tutorial on how to "Access to an OAuth2 GraphQL server": https://graphql-maven-plugin-project.graphql-java-generator.com/client_oauth2.html

As they mention in the tutorial, in order to connect to the OAuth server, I must use Spring. The tutorial explains how to access a server which uses 'client_credentials' grant type. I am not familiar with spring and spring-security so I didn't manage to configure my Spring client for 'password' grant type (all I did was to change in the application.properties the grant_type from 'client_credentials' to 'password'). Where the username and password need to be configured? What additional configurations do I need to make? Is there any way to do this without Spring? What I'm trying to obtain is way which requests the token from the authorization server and (under the hood) adds the token to the resource requests.

Following is the piece of code which I have following the tutorial:

@SpringBootApplication(scanBasePackageClasses = { MinimalSpringApp.class, GraphQLConfiguration.class})
public class MinimalSpringApp implements CommandLineRunner
{
   /**
    * The executor, that allows to execute GraphQL queries. The class name is the one defined in the GraphQL schema.
    */
   @Autowired
   QueryExecutor queryExecutor;


   public static void main( String[] args )
   {
      SpringApplication.run( MinimalSpringApp.class, args );
   }


   /**
    * This method is started by Spring, once the Spring context has been loaded. This is run, as this class implements
    * {@link CommandLineRunner}
    */
   @Override
   public void run( String... args ) throws Exception
   {
// Create query
      GraphQLRequest softwareTechnologyRequest = queryExecutor.getSoftwareTechnologyGraphQLRequest( "{ id name }" );
      SoftwareTechnologyFilter filter = new SoftwareTechnologyFilter();

// Execute query
      List<SoftwareTechnology> technologies =
            queryExecutor.softwareTechnology( softwareTechnologyRequest, filter, 0, "", 1000, "", 0,
                  Collections.emptyList() );

      System.out.println( technologies );
   }

   @Bean
   ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction(
         ReactiveClientRegistrationRepository clientRegistrations) {
      ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
            clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
      oauth.setDefaultClientRegistrationId("provider_test");
      return oauth;
   }

When I run the application it throws an IllegalStateException caused by '401 Unauthorized from POST'.


Solution

  • SOLVED

    I needed to create my own client manager bean, where the username and password are set. There is an example in the Spring official documentation at the "Resource Owner Password Credentials" section: https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle/#oauth2client

       @Bean
       public ReactiveOAuth2AuthorizedClientManager authorizedClientManager( ReactiveClientRegistrationRepository clientRegistrationRepository,
             ReactiveOAuth2AuthorizedClientService authorizedClientService)
       {
          ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
                ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password().refreshToken().build();
          AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService );
          authorizedClientManager.setAuthorizedClientProvider( authorizedClientProvider );
          authorizedClientManager.setContextAttributesMapper( contextAttributesMapper() );
          return authorizedClientManager;
       }
    
       private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper()
       {
          return authorizeRequest -> {
             Map<String, Object> contextAttributes = new HashMap<>();
             contextAttributes.put( OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "username" );
             contextAttributes.put( OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password" );
             return Mono.just(contextAttributes);
          };
       }
    

    Then the bean from the graphql java generator plugin tutorial needed to be adapted in order to use the provided client manager:

       @Bean
       ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction(
             ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
          ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
                authorizedClientManager);
          oauth.setDefaultClientRegistrationId("provider_test");
          return oauth;
       }