Above diagram is from https://cloudsundial.com/salesforce-oauth-flows Courtesy Lawrence Newcombe
Recently I was looking to create an application which can connect to Salesforce using OAuth Authorisation Code flow (in Salesforce language “Web Server” flow) and Spring Boot & Spring Security.
When I was trying to find a relevant example where anyone has written a web application using Spring Security, to my surprise I couldn’t find any sample repo or blog post where latest Spring Security based implementation can be found. Of course there are examples of Spring Security with Google, Facebook, LinkedIn and even for GitHub, but I couldn’t find anything in relations to Salesforce OAuth integration.
This led to creation of a small project which can connect to Salesforce using OAuth and Spring Boot & Spring Security. After spending hours figuring out how Spring Security works with Authorisation Servers, Resource Owners and which particular configurations will be required to make sure end to end OAuth flow works involving three calls via the browser.
- Authorize call using the authorization endpoint
- Exchange the code with OAuth token after redirection using Token endpoint
- Finally using UserInfo API to retrieve the Principal (Salesforce user).
Source Code
Here I’m sharing the sample code for both Servlet and Reactive implementations.
You can review the source of this sample app here for Servlet Stack
and here for Reactive Stack in case you are working with Spring WebFlux
Where magic happens
src/main/resources/application.yml
spring:
security:
oauth2:
client:
registration:
salesforce:
token-name: access_token
client-id: replace-me
client-secret: replace-me
scope: api, refresh_token, id, profile
redirect-uri: http://localhost:8080/login/oauth2/code/salesforce
authorization-grant-type: authorization_code
provider:
salesforce:
token-uri: https://login.salesforce.com/services/oauth2/token
authorization-uri: https://login.salesforce.com/services/oauth2/authorize
user-info-uri: https://login.salesforce.com/services/oauth2/userinfo
userNameAttribute: sub
src/main/java/com/sf/oauth/SecurityConfiguration.java
Pretty standard implementation which is protecting the resources of this application and making sure all endpoints are secured with OAuth token.
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Client()
.and()
.oauth2Login();
}
}
src/main/java/com/sf/oauth/WebClientConfig.java
Again standard implementation for the initialisation of WebClient instance which will be used for all the API Calls after the OAuth flow has completed.
public class WebClientConfig {
@Bean
public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(oauth)
.build();
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(new AuthorizationCodeOAuth2AuthorizedClientProvider());
return authorizedClientManager;
}
}
src/main/java/com/sf/oauth/AccountController.java
Sample implementation of AccountController which shows how you can call Salesforce APIs without worrying about the OAuth Token because management of OAuth token is completely offloaded to Spring Security with above configurations.
private String fetchAccounts(OAuth2AuthorizedClient authorizedClient, String userProfileURL) {
String sfHost = userProfileURL.substring(0, userProfileURL.lastIndexOf("/"));
return this.webClient
.get()
.uri(sfHost, uriBuilder ->
uriBuilder
.path("/services/data/v51.0/query/")
.queryParam("q","SELECT Id, Name FROM Account LIMIT 5")
.build()
)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono(String.class)
.block();
}
These samples are complied with Maven and dependencies are list here.
References
Salesforce OAuth Web Server Flow
Salesforce UserInfo Endpoint