OAuth2
This guide assumes familiarity with the Spring Framework.
If you are new to Spring, we recommend starting with their official guides to get up to speed.
To simplify the authentication process of your users, you can allow authentication using OAuth2 clients. This implementation is based on Spring OAuth 2.0 Client. Check out this guide if you need more information.
Set Up an OAuth2 Provider
Create an application for your OAuth2 provider with the following parameters:
Redirect URI: Use the base URI of your application (for examplehttps://example.com), an identifier for the provider (for examplegithub) you configured for the client and the pathlogin/oauth2/<client-id>/code(for examplehttps://example.com/login/oauth2/github/code)
Copy the client-id and client-secret for the next step.
Configuration
Spring OAuth 2.0 provides an easy method to configure your OAuth2 providers.
Please make sure that the email is in scope for your OAuth2 provider. Check out their official documentation.
| Property | Type | Description | Default value |
|---|---|---|---|
| singularity.auth.oauth2.enable | Boolean | Allow authentication using OAuth2 identity providers. Disabled by default. | false |
| singularity.auth.oauth2.error-redirect-uri | String | The path the user will be redirected to if there was an error in the OAuth2 flow. | http://localhost:8000/auth/oauth2/error |
Make sure to set singularity.auth.oauth2.enable to true.
Clients
You can configure your clients with the following properties.
spring:
security:
oauth2:
client:
registration:
okta: # registrationId
client-id: <your-okta-client-id>
client-secret: <your-okta-client-secret>
authorization-grant-type: authorization_code
scope: read, write
provider:
okta: # registrationId
authorization-uri: https://<your-subdomain>.oktapreview.com/oauth2/v1/authorize
token-uri: https://<your-subdomain>.oktapreview.com/oauth2/v1/token
For known providers like GitHub you don't need to specify the provider part:
security:
oauth2:
client:
registration:
github: # registrationId
client-id: <your-github-client-id>
client-secret: <your-github-client-id>
scope:
- user:email
- read:user
For more information, check out the Spring Docs.
Registration and Login
If a user wants to register to your application or authenticate to an existing account using an OAuth2 provider, you have to perform the following steps:
This section strongly relies on cookies.
After successful authentication you will not be able to retrieve the AccessToken and RefreshToken though.
These tokens will be set as HTTP-only cookies only.
You only have the possibility to override certain tokens in the request header.
1. Retrieving a Session Token
Authentication in Singularity strongly relies on sessions. Access tokens and refresh tokens are bound to a specific session for example. You can learn more about that here.
Therefore, before authenticating via an OAuth2 client,
you need to retrieve a SessionToken using POST /api/auth/sessions/token.
This sets the SessionToken as an HTTP-only cookie and returns the value in the response body
if header authentication is enabled.
2. Calling the Spring OAuth2 Authorization Endpoint
Spring automatically creates OAuth2 authorization endpoints for all of your providers
on the path /oauth2/authorization/{registrationId}.
Parameters
| Parameter | Description | Required |
|---|---|---|
redirect_uri | The URI the user will be redirected to after the authentication was successful. | false |
step_up | Should step-up authentication be requested? Boolean. Learn more here. | false |
3. Redirect
If the authorization was successful, you will be redirected to /login/oauth2/code/{registrationId}.
Singularity will check the response and redirect the user to the redirect_uri if specified in the previous step.
The user is now authenticated.
Step-Up Authentication
This section strongly relies on cookies.
Connecting a new provider to an existing user needs an AccessToken set a cookie.
Placing them in the header will not lead to a successful connection since they will be lost after the callback.
1. Authenticate the User
Authenticate the user by calling POST /api/auth/login.
This will set the AccessToken as an HTTP-only cookie.
2. Request the Step-Up
You can request a StepUpToken by adding the parameter step_up=true to the initial authorization request
(for example https://example.com/oauth2/authorization/{registration_id}?step_up=true).
This will set the StepUpToken as an HTTP-only cookie.
You can learn more about step-up authentication here.
Managing Providers
Getting Connected Providers
You can request a list of connected providers using
GET /api/users/me/providers
with a valid AccessToken.
Connecting an OAuth2 Provider to an Existing Account
It is possible to connect multiple OAuth2 clients to an account.
This section strongly relies on cookies.
Connecting a new provider to an existing user needs an AccessToken and a StepUpToken set as cookies.
Placing them in the header will not lead to a successful connection since they will be lost after the callback.
1. Authenticate the User
- Make sure the user is authenticated and a valid
AccessTokenis set as cookie. - Authorize a step-up by calling
POST /api/auth/step-up. This will set aStepUpTokenas an HTTP-only cookie. You can learn more about step-up authentication here.
2. Create an OAuth2 Provider Connection Token
Call POST /api/users/me/providers/oauth2/token authenticated as the user to create an OAuth2ProviderConnectionToken.
This token will be set as an HTTP-only cookie and returned in the response if header-authentication is enabled.
3. Follow the Steps For Registration
With the OAuth2ProviderConnectionToken set and the user authenticated, you can follow the same steps.
Because of the AccessToken,
StepUpToken and OAuth2ProviderConnectionToken,
the server automatically tries to connect the new provider to the current user.
If successful, the user will be connected to the new provider.
A security alert will be sent to the user's email if this setting is enabled and email is enabled and configured correctly.
Adding Password Authentication
If a user registered using an OAuth2 provider, it is possible to add the option to authenticate using a password.
Call POST /api/users/me/providers/password
with a valid AccessToken and StepUpToken.
If successful, the user can now log in using his new password.
Disconnecting an OAuth2 Provider
If a user connected multiple provider,
it is possible to disconnect providers through the endpoint
DELETE /api/users/me/providers/<provider-name>.
A security alert will be sent to the user's email if this setting is enabled and email is enabled and configured correctly.
You are not allowed to disconnect the password identity. Furthermore, if the only identity is an OAuth2 identity, you are not allowed to disconnect this identity.
Error Handling
If authentication failed,
the user will be redirected to the URI you specify in singularity.auth.oauth2.error-redirect-uri.
This allows you to specifically handle these scenarios in your frontend.
The full URI will contain a query parameter code (for example https://example.com/oauth2/error?code=state_parameter_missing) that specifies the type of error that occurred.
The following error types exist:
All Flows
These error codes can occur on any type of flow.
| Code | Description |
|---|---|
authentication_failed | Authentication at OAuth2 provider failed. In this case another parameter details is included that specifies the error. This parameter correspond to error codes thrown by Spring OAuth 2.0. Check their documentation for more information. |
state_parameter_missing | No state parameter found in callback. |
state_expired | The state token is expired. It is valid for 15 min by default. |
invalid_state | The state token cannot be decoded. |
session_token_missing | No session token provided as query parameter or cookie. |
session_token_expired | The provided session token is expired. |
invalid_session_token | The provided session token cannot be decoded. |
sub_claim_missing | No sub claim. provided from OAuth2 provider. |
email_claim_missing | No email provided from OAuth2 provider. |
server_error | An unspecified error occurred. |
Connection to Existing Account
Besides the error codes that can occur on all flows, these codes can occur when trying to connect an OAuth2 provider to an existing account.
| Code | Description |
|---|---|
provider_already_connected | The user already connected the provider. |
connection_token_expired | The provided OAuth2ProviderConnectionToken is expired. |
invalid_connection_token | The provided OAuth2ProviderConnectionToken cannot be decoded. |
connection_token_provider_mismatch | The provided OAuth2ProviderConnectionToken does not match the requested provider. |
step_up_missing | Connecting a new provider requires s StepUpToken. |
step_up_token_expired | The provided StepUpToken is expired. |
invalid_step_up_token | The provided StepUpToken is invalid. |
access_token_missing | Connecting a new provider requires an AccessToken. |
access_token_expired | The provided AccessToken is expired. |
invalid_access_token | The provided AccessToken is invalid. |
connected_to_another_principal | The account that the principal wants to connect is already connected to another principal. |
Registration
Besides the error codes that can occur on all flows, these codes can occur when trying to register a user via OAuth2.
| Code | Description |
|---|---|
user_already_authenticated | Registration failed. The user is already authenticated. |
email_already_registered | Registration failed. The email attribute of the OAuth2 provider matches an email of an already registered user. In this case an Identity Provider Information will be sent to the associated account. |
Login
Besides the error codes that can occur on all flows, these codes can occur when trying to log in a user account via OAuth2.
| Code | Description |
|---|---|
user_already_authenticated | Login failed. The user is already authenticated. |
Step-Up
Besides the error codes that can occur on all flows, these codes can occur when trying to perform step-up authentication via OAuth2.
| Code | Description |
|---|---|
wrong_account_authenticated | The account you logged in via OAuth2 does not match the AccessToken or the token is missing or invalid. |
Converting Guests to Users
| Code | Description |
|---|---|
user_already_authenticated | Conversion of GUEST to user account failed. The user already authenticated. |
email_already_registered | Conversion of GUEST to user account failed. The email attribute of the OAuth2 provider matches an email of an already registered user. |
provider_already_connected | The user already connected the provider. |
connection_token_expired | The provided OAuth2ProviderConnectionToken is expired. |
invalid_connection_token | The provided OAuth2ProviderConnectionToken cannot be decoded. |
connection_token_provider_mismatch | The provided OAuth2ProviderConnectionToken does not match the requested provider. |
step_up_missing | Connecting a new provider requires step-up. |
step_up_token_expired | The provided StepUpToken is expired. |
invalid_step_up_token | The provided StepUpToken is invalid. |
connected_to_another_principal | The account that the principal wants to connect is already connected to another principal. |