Skip to main content

OAuth2

note

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 example https://example.com), an identifier for the provider (for example github) you configured for the client and the path login/oauth2/<client-id>/code (for example https://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.

warning

Please make sure that the email is in scope for your OAuth2 provider. Check out their official documentation.

PropertyTypeDescriptionDefault value
singularity.auth.oauth2.enableBooleanAllow authentication using OAuth2 identity providers. Disabled by default.false
singularity.auth.oauth2.error-redirect-uriStringThe 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:

info

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

ParameterDescriptionRequired
redirect_uriThe URI the user will be redirected to after the authentication was successful.false
step_upShould 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

info

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.

info

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

  1. Make sure the user is authenticated and a valid AccessToken is set as cookie.
  2. Authorize a step-up by calling POST /api/auth/step-up. This will set a StepUpToken as 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.

info

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>.

info

A security alert will be sent to the user's email if this setting is enabled and email is enabled and configured correctly.

warning

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.

CodeDescription
authentication_failedAuthentication 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_missingNo state parameter found in callback.
state_expiredThe state token is expired. It is valid for 15 min by default.
invalid_stateThe state token cannot be decoded.
session_token_missingNo session token provided as query parameter or cookie.
session_token_expiredThe provided session token is expired.
invalid_session_tokenThe provided session token cannot be decoded.
sub_claim_missingNo sub claim. provided from OAuth2 provider.
email_claim_missingNo email provided from OAuth2 provider.
server_errorAn 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.

CodeDescription
provider_already_connectedThe user already connected the provider.
connection_token_expiredThe provided OAuth2ProviderConnectionToken is expired.
invalid_connection_tokenThe provided OAuth2ProviderConnectionToken cannot be decoded.
connection_token_provider_mismatchThe provided OAuth2ProviderConnectionToken does not match the requested provider.
step_up_missingConnecting a new provider requires s StepUpToken.
step_up_token_expiredThe provided StepUpToken is expired.
invalid_step_up_tokenThe provided StepUpToken is invalid.
access_token_missingConnecting a new provider requires an AccessToken.
access_token_expiredThe provided AccessToken is expired.
invalid_access_tokenThe provided AccessToken is invalid.
connected_to_another_principalThe 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.

CodeDescription
user_already_authenticatedRegistration failed. The user is already authenticated.
email_already_registeredRegistration 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.

CodeDescription
user_already_authenticatedLogin 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.

CodeDescription
wrong_account_authenticatedThe account you logged in via OAuth2 does not match the AccessToken or the token is missing or invalid.

Converting Guests to Users

CodeDescription
user_already_authenticatedConversion of GUEST to user account failed. The user already authenticated.
email_already_registeredConversion of GUEST to user account failed. The email attribute of the OAuth2 provider matches an email of an already registered user.
provider_already_connectedThe user already connected the provider.
connection_token_expiredThe provided OAuth2ProviderConnectionToken is expired.
invalid_connection_tokenThe provided OAuth2ProviderConnectionToken cannot be decoded.
connection_token_provider_mismatchThe provided OAuth2ProviderConnectionToken does not match the requested provider.
step_up_missingConnecting a new provider requires step-up.
step_up_token_expiredThe provided StepUpToken is expired.
invalid_step_up_tokenThe provided StepUpToken is invalid.
connected_to_another_principalThe account that the principal wants to connect is already connected to another principal.