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 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.
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. |
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
OAuth Client Registrations are managed from the Security / OAuth Client Registrations (/client-registrations
) page of the Broadleaf admin.
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
.
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. |
In the previous example:
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
A registration entry defines how the Auth Service interacts with the Provider. Data includes client id, client secret, redirect uri, and scopes to request.
Tip
|
See OAuth2ClientProperties.Registration. |
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".
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.
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
The following sections cover important components when adding a new IDP.
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.
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. |
The Auth service overrides certain beans from the library to facilitate the integration.
A AuthenticationDetailsSource that creates a BroadleafOAuth2ClientAuthenticationDetails
enabling the blc client id to be included in the OAuth2Authentication
object.
Saves and loads OAuth2AuthorizationRequest from a signed JWT cookie.
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
.
With that done, customers will see a "continue withe Facebook" button on the 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"> </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"> </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>
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. |