Skip to content

Instantly share code, notes, and snippets.

@monospacesoftware
Created November 7, 2020 15:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monospacesoftware/07cfd9603442830d370024414a53f374 to your computer and use it in GitHub Desktop.
Save monospacesoftware/07cfd9603442830d370024414a53f374 to your computer and use it in GitHub Desktop.
How to use Spring Boot WebClient to access an OAuth2 password protected REST API
spring:
security:
oauth2:
client:
registration:
salesforce:
authorization-grant-type: password
client-authentication-method: post
client-id: <clientId>
client-secret: <clientSecret>
provider: salesforce
provider:
salesforce:
token-uri: https://foo.my.salesforce.com/services/oauth2/token
foo:
salesforce:
api-url: https://foo.my.salesforce.com/services/data/v49.0/sobjects/
api-user-name: <username>
api-password: <password>
package com.foo;
@Slf4j
@Service
public class SalesForceClient {
private final WebClient webClient;
public SalesForceService(@Qualifier("salesForceWebClient") WebClient webClient) {
this.webClient = webClient;
}
public String createCase(SalesForceCase salesForceCase) {
SalesForceResponse response = webClient.post()
.uri("/Case")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(salesForceCase))
.retrieve()
.bodyToMono(SalesForceResponse.class)
.onErrorMap(e -> new SalesForceException("Error communicating with SalesForce API: " + e.getMessage(), e))
.block();
if (response == null)
throw new SalesForceException("No response from SalesForce API");
if (!response.isSuccess())
throw new SalesForceException("SalesForce API returned an error: " + Optional.ofNullable(response.getErrors()).map(List::toString).orElse("[]"));
return response.getId();
}
package com.foo.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.*;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import java.util.HashMap;
import java.util.Map;
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "foo.salesforce")
public class SalesForceConfig {
private String apiUrl;
private String apiUsername;
private String apiPassword;
@Bean
public OAuth2AuthorizedClientManager salesForceAuthorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.password()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
Map<String, Object> passwordAttributes = new HashMap<>();
passwordAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, getApiUsername());
passwordAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, getApiPassword());
authorizedClientManager.setContextAttributesMapper(request -> passwordAttributes);
return authorizedClientManager;
}
@Bean
public WebClient salesForceWebClient(@Qualifier("salesForceAuthorizedClientManager") OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oAuth2Filer = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oAuth2Filer.setDefaultClientRegistrationId("salesforce");
return WebClient.builder()
.filter(oAuth2Filer)
.baseUrl(getApiUrl())
.build();
}
}
@pabbott-ANet
Copy link

Hmm tried this with same credentials that work in postman but I get
org.springframework.security.oauth2.core.OAuth2AuthorizationException: [invalid_client_id] client identifier invalid

I've read that this is often because you need to send the credentials in the body and not auth header. But, I can't for the life of me figure out how to specify this in Webclient. You can easily do so in the deprecated RestTemplate

@fallenangel321
Copy link

dude, saved me hours and hours -- just plugged everything in and i'm good to go. it took way too long to find your original reddit post, hopefully more people find it

@shone1991
Copy link

when I run the app it keeps saying
Parameter 0 of method salesForceAuthorizedClientManager in dev.egov.oauth2client.config.WebClientConfig required a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found.

@codewithbharath
Copy link

Hello,
It's saving my time but still i couldn't understand some of the class integrations .. can you please help me on this
where exactly store the token and how we are using that token in next requests.

if i run my spring application...
Always i got Login with OAuth 2.0 this message

@budanar
Copy link

budanar commented Mar 21, 2023

Hello, It's saving my time but still i couldn't understand some of the class integrations .. can you please help me on this where exactly store the token and how we are using that token in next requests.

if i run my spring application... Always i got Login with OAuth 2.0 this message

The "Login to OAuth" is coming because the service you created is also getting protected by OAUTH.
Refer to post: https://fullstackdeveloper.guru/2022/03/17/how-to-invoke-oauth2-protected-microservice-using-webclient-in-spring-boot/

Above states to use WebSecurityConfigurerAdapter to address this issue. However this is now deprecated. So can use SecurityFilterChain instead
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

@maksymgendin
Copy link

maksymgendin commented Jul 20, 2023

Just in case someone is looking for a reactive way 🙂

@Bean
@Qualifier(CLIENT_NAME)
ReactiveOAuth2AuthorizedClientManager authorizedClientManager(ReactiveClientRegistrationRepository clientRegistrationRepository,
                                                              ReactiveOAuth2AuthorizedClientService authorizedClientService) {
    final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
            .password()
            .build();

    final AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
            new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    authorizedClientManager.setContextAttributesMapper(request -> Mono.just(Map.of(
            OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, getUsername(),
            OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, getPassword()
    )));

    return authorizedClientManager;
}

@Bean
@Qualifier(CLIENT_NAME)
WebClient webClient(@Qualifier(CLIENT_NAME) ReactiveOAuth2AuthorizedClientManager clientManager) {
    final ServerOAuth2AuthorizedClientExchangeFilterFunction filter =
            new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientManager);
    filter.setDefaultClientRegistrationId(CLIENT_NAME);
    return WebClient.builder()
            .filter(filter)
            .baseUrl(getBaseUrl())
            .build();
}

@shone1991 This is probably the solution for your case 😬

@larsf96
Copy link

larsf96 commented Jul 20, 2023

I'm still getting org.springframework.security.oauth2.core.OAuth2AuthorizationException: [invalid_client_id] client identifier invalid
even if I use the same credentials as in Postman.
@pabbott-ANet Did you figure out how to solve that?

@maksymgendin
Copy link

I'm still getting org.springframework.security.oauth2.core.OAuth2AuthorizationException: [invalid_client_id] client identifier invalid even if I use the same credentials as in Postman. @pabbott-ANet Did you figure out how to solve that?

Check authorization-grant-typeand client-authentication-methodin your provider configuration, if you're not using a predefined one.

@rafonsecad
Copy link

rafonsecad commented Aug 5, 2023

@monospacesoftware thanks for sharing, one thing to notice though, since spring security 5.5 the values for client-authentication-method changed, so instead of post we need to replace it by client_secret_post. I just ran into this issue updating spring boot, here we can see the commit and post value was removed here for version 6.0.0

@larsf96 @maksymgendin check this out.

@maksymgendin
Copy link

@monospacesoftware thanks for sharing, one thing to notice though, since spring security 5.5 the values for client-authentication-method changed, so instead of post we need to replace it by client_secret_post. I just ran into this issue updating spring boot, here we can see the commit and post value was removed here for version 6.0.0

@larsf96 @maksymgendin check this out.

Yeah, you're right, the values have changed. Thanks for notifying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment