Broadleaf Microservices
  • v1.0.0-latest-prod

Authentication with a Third-Party Identification Provider

Overview

AuthenticationServices includes the spring-security-oauth2-client library to facilitate using a third-party Identity Provider (IDP), such as Google, Okta, or Facebook for login and registration. It also leverages the oauth2-client library’s out-of-box OAuth2LoginConfigurer to make implementation easy. By using the out-of-box configurer, implementors can rely on existing experience and the body of online documentation for Spring Security OAuth2 library.

For clarity, the following details the flow when using a third-party identity provider.

OAuth2 Flow using a Third Party IDP

Configuring Client Registrations and Providers

OAuth2 Clients have Provider and Registration data. These can be configured in two ways:

These methods are not mutually exclusive. Any clients configured with a properties file will be loaded into the database and made available for management in the Admin UI. The persistent version also includes a tenantId allowing different tenants to have different clients configured.

Using Properties File

Spring provides properties with which to configure your clients. These are defined by OAuth2ClientProperties.

Important
After properties are loaded for the first time, they are saved to the database. Any subsequent changes will need to be performed in the Broadleaf Admin.
Note
Some clients like Okta will only provide an "issuer uri" and not all the provider details. The rest of the details and some registration fields will be hydrated automatically by the system using the issuer URI.
Example properties for configuring client registrations and providers
spring:
  security:
    oauth2:
      client:
        provider:
          facebook:
            authorization-uri: https://www.facebook.com/v3.0/dialog/oauth
            token-uri: https://graph.facebook.com/v3.0/oauth/access_token
            user-info-uri: https://graph.facebook.com/v3.0/me?fields=id,name,email,verified,is_verified
          okta:
            issuer-uri: https://my-app.okta.com/oauth2/default
        registration:
          facebook:
            client-id: example-clientId
            client-secret: example_client_secret
            redirect-uri: https://heatclinic.localhost:8456/auth/login/oauth2/code/facebook
            scope:
              - email
              - public_profile
          google:
            client-id: example-clientId.apps.googleusercontent.com
            client-secret: example_client_secret
            redirect-uri: https://heatclinic.localhost.broadleafcommerce.com:8456/auth/login/oauth2/code/google
            scope:
              - email
              - profile
          okta:
            client-id: example-clientId
            client-secret: example-clientSecret
            client-name: Okta

Using the Admin UI

OAuth Client Registrations are managed from the Security / OAuth Client Registrations (/client-registrations) page of the Broadleaf admin.

Client Registration in the Admin Nav

This section allows you to add all the same information as above in a single form. Similar to using the properties file, an issuerUri can be provided and the system will automatically fetch the client details from it. Thus, you need not enter all the information in the form manually, except for the few required fields like clientId and clientSecret.

Client Provider

A provider entry defines the public set of URL endpoints for an IDP. These are standardized endpoints such as authorization and token URIs.

Tip
For more information, see OAuth2ClientProperties.Provider.
  • Facebook is added as a provider and its public URIs are listed as properties.

  • There is not an entry for Google because its provider data is included in the OAuth2 Client library.

  • Okta only includes an issuer-uri from which the rest of the details can be fetched by the system and filled out

Client Registration

A registration entry defines how the Auth Service interacts with the Provider. Data includes client id, client secret, redirect uri, and scopes to request.

In this example, there are entries for facebook and google. These entries determine which IDPs are available for login. The client id and secret come from the service where your app is registered (eg Facebook or Google). The redirect uri is determined by the OAuth configuration—the default pattern is /auth/login/oauth2/code/{code}, where code is the name of the registration, like "google" or "facebook".

Additional Configuration Properties

Broadleaf also adds a few additional configuration properties (defined below) specific to whether authentication is done for an admin or for a commerce-facing app.

These properties all fall under both broadleaf.auth.user.web.authorization.identity-provider.commerce and broadleaf.auth.user.web.authorization.identity-provider.admin.

  • hideFirstPartyForm: Whether to hide the 1st-party sign-in form provided by Broadleaf and instead redirect the user to an external 3rd-party identity provider for authentication.

    • Ensure that if this is false, that there is at least one provider specified in providers. If there is more than one, the user will be prompted to select one before being directed there to.

  • providers: Map of the registered 3rd-party identity providers that should be used by the respective type of app (admin or commerce). In addition to specifying the provider (using the registration ID), you can also specify the following for that provider for display purposes:

    • icon: Path to an asset to show with the provider’s name on the frontend. We provide icons for GitHub, Facebook, Google, Auth0, and Okta out of box with the path pattern of /img/{registrationId}-icon.svg, e.g., /img/okta-icon.svg.

Currently, only the commerce flows support the following property. For the admin, all users must be created in the Broadleaf system before they can log in. Note that they do not need to provide all details, just the username.

  • autoRegister: Enables registering new users automatically after successful authentication. False by default.

Example Configuration
broadleaf:
  auth:
    user:
      web:
        authorization:
          identity-provider:
            admin:
              # indicates we should redirect straight to okta
              hideFirstPartyForm: true
              providers:
                okta:
                  # no icon needed since we aren't showing the 1st party form
                  icon:
            commerce:
              # indicates customers should be created in Broadleaf on successful login
              auto-register: true
              providers:
                github:
                  # this icon will appear a "sign in with GitHub" button
                  icon: /img/github-icon.svg
                google:
                  icon: /img/google-icon.svg
                facebook:
                  icon: /img/facebook-icon.svg

Key Components for Adding a new IDP

The following sections cover important components when adding a new IDP.

Provider and Registration

For IDPs following OAuth2 / OIDC standards, a new IDP would need a provider entry and a registration entry at minimum. This basic information will allow users to login and register with the Auth Services. See the [Configuration] section above for more details.

AuthenticationStrategyDelegate

This interface defines how to convert an Authentication into an OAuth2UserDetails for a particular IDP. We’ve provided a DefaultOIDCAuthenticationStrategyDelegate that should handle any IDPs that follow the OpenID Connect protocol (OIDC) out-of-box. For those that follow the OAuth 2.0 protocol but not OIDC, AbstractOAuthClientAuthenticationStrategyDelegate provides a good base to start from.

Tip
We also already have delegates for GitHub and Google OAuth.

Included Overrides

The Auth service overrides certain beans from the library to facilitate the integration.

DefaultOAuth2ClientAuthenticationDetailsSource

A AuthenticationDetailsSource that creates a BroadleafOAuth2ClientAuthenticationDetails enabling the blc client id to be included in the OAuth2Authentication object.

BroadleafAuthorizationRequestRepository

Saves and loads OAuth2AuthorizationRequest from a signed JWT cookie.

Tutorial: Enabling Facebook Login

Let’s look at what it takes to set up the ability for customers to log in using their Facebook accounts. This will be similar for any OIDC compliant providers as well as GitHub and Google, which are supported out of box. All you need to do is set the following properties:

spring:
  security:
    oauth2:
      client:
        provider:
          facebook:
            authorization-uri: https://www.facebook.com/v3.0/dialog/oauth
            token-uri: https://graph.facebook.com/v3.0/oauth/access_token
            user-info-uri: https://graph.facebook.com/v3.0/me?fields=id,name,email,verified,is_verified
        registration:
          facebook:
            # The ID of your app, found on the Facebook App Dashboard.
            # See https://developers.facebook.com/apps
            client-id: example-clientId
            # Your unique app secret, shown on the Facebook App Dashboard.
            # Never include this app secret in client-side code or in binaries that could be
            # decompiled
            client-secret: example_client_secret
            # The URL that you want to redirect the customer logging in back to.
            redirect-uri: {baseUrl}/{action}/oauth2/code/{registrationId}
            # A comma or space separated list of Permissions to request from the person using your app.
            # See https://developers.facebook.com/docs/permissions/reference
            scope:
              - email
              - public_profile
broadleaf:
  auth:
    user:
      web:
        authorization:
          identity-provider:
            commerce:
              # indicates customers should be created in Broadleaf on successful login
              auto-register: true
              providers:
                facebook:
                  # We provide a facebook icon with the Auth service OOB
                  # This will display next to the name of the provider as a button on the log in form
                  icon: /img/facebook-icon.svg

Alternatively, you can also register a provider in the admin by going to "Security > OAuth Client Registrations" (/client-registrations) in your admin UI and creating a new registration. The form takes the same fields as what Spring defines for a spring.security.oauth.client.provider and *.registration.

Admin form with values

With that done, customers will see a "continue withe Facebook" button on the log in form.

Commerce log in form
Tip
If you want to modify how the 3rd party IDP buttons show up on the login form, add an override for the fragments/additional-idps.html template in your copy of the AuthenticationServicesImage in src/main/resources/templates. The following is an example of what that looks like using Thymeleaf:
<th:block
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:th="http://www.thymeleaf.org"
>
    <th:block th:fragment="additional-idps" th:unless="${#arrays.isEmpty(providers)}">
        <!-- Divider -->
        <div th:unless="${hideFirstPartyForm}"
             class="tw-flex tw-items-center tw-w-full tw-my-6 md:tw-my-8 lg:tw-my-10">
            <div class="tw-flex-grow tw-h-0 tw-border-b-2">&nbsp;</div>
            <div class="tw-mx-4 tw-font-bold tw-uppercase"
                 th:utext="#{login.form.other.providers.or}">Or
            </div>
            <div class="tw-flex-grow tw-h-0 tw-border-b-2">&nbsp;</div>
        </div>
        <!-- Additional IDPs -->
        <ul class="tw-w-full">
            <li th:each="idp : ${providers}"
                class="tw-w-full tw-mb-5 last:tw-mb-0">
                <a class="tw-flex tw-items-center tw-w-full tw-py-3 tw-px-4 tw-text-sm tw-font-semibold tw-text-gray-800 leading-tight tw-bg-white tw-rounded tw-shadow hover:tw-bg-gray-100 focus:tw-outline-none focus:tw-shadow-outline"
                   th:href="@{${idp.url}(client_id=${#auth.getClientId()})}">
                    <img th:src="@{${idp.icon}}" class="tw-h-6 tw-w-6 tw-mr-3" alt=""/>
                    <span>[[#{login.form.other.providers.continue(${idp.name})}]]</span>
                </a>
            </li>
        </ul>
    </th:block>
</th:block>

Encrypting the Client Secret

Because we are storing the registration details in the database to allow facilitate admins managing the properties, we need to set up an encryption key to encrypt the data during persistence and decrypt during retrieval. The key should be encoded and set as the value of broadleaf.auth.client.provider.encryption.encoded-key. We use an AttributeConverter to handle the encryption and decryption during persistence and retrieval.

When entering production it is critical to generate a new key and not use the same one as in development. The AuthenticationServicesImage included with starter projects contains a KeyGeneratorUtil that can be used to generate RSA and AES keys. The generated keys will be printed to the console and will be base-64 encoded. We generate a 256-bit AES key for the client secret encryption.

Important
The AES key should be stored in a secure place and not as part of the source of the application. Wherever database credentials are stored should be adequate.