Implementing Salesforce OAuth Client with Spring Security!

Posted on: 17th Nov 2021

web server flow 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.

  1. Authorize call using the authorization endpoint
  2. Exchange the code with OAuth token after redirection using Token endpoint
  3. 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

GitHub - gouravsood/spring-security-oauth2-salesforce-servlet: Spring Boot Security OAuth Authorisation Code Flow (Web Server) with Salesforce using Servlet Stack

and here for Reactive Stack in case you are working with Spring WebFlux

GitHub - gouravsood/spring-security-oauth2-salesforce-reactive: Spring Boot Security OAuth Authorisation Code Flow (Web Server) with Salesforce using Reactive Stack

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

General OAuth 2.0 Flows

OAuth Community Site

Spring Security Architecture

Spring Boot

Help And Training Community

Salesforce OAuth Web Server Flow

Help And Training Community

Salesforce UserInfo Endpoint

gouravsood

© 2024

LinkedIn