OAUTH2/OIDC
Configure ODD Platform to authenticate users via OAuth2/OIDC against AWS Cognito, GitHub, Google, Azure AD, Okta, Keycloak, or any custom OIDC-compliant identity provider.
ODD Platform supports different OIDC/OAuth2 providers. Currently there are:
It is possible to have multiple providers at the same time (e.g. you want to allow to authenticate users from Github and Google, or from multiple Cognito user pools). Configuration properties name for each provider must fit the pattern auth.oauth2.client.{client_id}.{client_parameter}, where client_id is provider identifier.
There are some common parameters which are used across all providers:
auth.type. Must be set to OAUTH2auth.oauth2.client.{client-id}.provider. Provider code, which helps application to understand which provider is used.auth.oauth2.client.{client-id}.client-id. Client ID obtained from providerauth.oauth2.client.{client-id}.client-secret. Client secret obtained from providerauth.oauth2.client.{client-id}.client-name. Custom name, which will be shown on UI in case of multiple providers enabled. (optional)auth.oauth2.client.{client-id}.redirect-uri. Redirect URL. Must be defined as{domain}/login/oauth2/code/{client-id}auth.oauth2.client.{client-id}.scope. Authorization scopes which are allowed for application
auth.oauth2.client.{client-id}.issuer-uri. URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0 Authorization Server Metadata endpoint defined by RFC 8414.
auth.oauth2.client.{client-id}.username-attribute. Defines which token claim should be picked as username in ODD Platformauth.oauth2.client.{client-id}.admin-attribute. Defines which token claim is responsible for admin principalauth.oauth2.client.{client-id}.admin-principals. List of users, who will have ADMIN role on login (for detailed explanation please check the Roles section).auth.oauth2.client.{client-id}.pkce. Optional Boolean (default unset, i.e. disabled). Enables Proof Key for Code Exchange (RFC 7636) for the authorization code flow. Whenpkce: trueandclient-secretis empty, the platform registers the client as a public OAuth2 client (client_authentication_method=none) and PKCE protects the code exchange. Whenclient-secretis set, the platform always uses confidential-client authentication and thepkceflag has no effect on the registration. Most commonly required by Keycloak — see the Keycloak with PKCE example below.
Admin-detection per-provider matrix
The configuration POJO that backs auth.oauth2.client.{id} is uniform across every provider, but each provider's runtime handler reads a different subset of the configured fields. The matrix below summarises how admin-principals and admin-groups actually behave for every supported provider — the silent-no-op rows in particular catch operators by surprise because the configuration loads without error. For the same comparison across all auth modes (not just OAuth2), see Admin promotion across providers.
AWS Cognito
admin-principals (email / configured username-attribute) and admin-groups (against the cognito:groups claim)
exact match on both
—
GitHub
admin-principals (login) before the organization-name check; admin-groups after, against team names returned by /user/teams
exact (principals); case-insensitive full match (groups)
admin-principals bypasses organization-name; GitHub Enterprise Server is not supported — api.github.com is hard-coded in the handler
admin-principals (email by default; overridable via admin-attribute)
exact match
admin-groups is a silent no-op — the field binds without error but the handler never reads it; use admin-principals or admin-attribute
Azure AD
admin-principals (against admin-attribute) and admin-groups (against the roles claim by default; switch to groups via groups-claim)
exact match
—
Okta
admin-principals always (against the admin-attribute / username-attribute claim); admin-groups only when groups-claim is set
case-insensitive full match
admin-groups is inert unless groups-claim is configured — the generic OIDC handler has no default groups claim. admin-principals works with no extra config
Keycloak
same as Okta — admin-principals always; admin-groups only when groups-claim is set
case-insensitive full match
same as Okta — admin-groups needs groups-claim
Custom OIDC
admin-principals always; admin-groups only when groups-claim is set
case-insensitive full match
no default groups claim — set groups-claim to use admin-groups; admin-principals needs no extra config
Reading the matrix: if your provider row says "silent caveats", the configured admin-groups / admin-principals are not enforced the way the property names suggest. The provider-specific sections below repeat each caveat at the point of configuration; this matrix is the single comparison view.
AWS Cognito
AWS Cognito provider can be configured using common oauth properties and couple of provider specific properties:
auth.oauth2.client.{client-id}.admin-groups. List of admin groups. Groups are retrieved fromcognito:groupstoken claim.auth.oauth2.client.{client-id}.logout-uri. Application will be redirected to this URI after user logout for removing session on cognito side. Please check AWS Docs for more details.
Github
You can use Github as your OAUTH provider. ODD platform can retrieve info about user organizations and teams and use it for granting ADMIN permissions (for detailed explanation please check the Roles section). There are some github specific properties, which can be set:
auth.oauth2.client.{client-id}.organization-name. Restricts login only for users from this particular organizationauth.oauth2.client.{client-id}.admin-groups. Grants admin privilegies for users who are members of these teams, which are inside above organization
ODD Platform allows to authenticate users via Google. You can restrict users to login under your organization domain. This is controlled by auth.oauth2.client.{client-id}.allowed-domain property.
Azure AD
ODD Platform supports integration with Azure Active Directory (Azure AD) using OAuth2/OpenID Connect (OIDC). Azure AD applications can be registered in one of two modes:
Single-tenant — only users from one specific Azure AD tenant can sign in. Use this for organisation-internal deployments.
Multi-tenant — users from any Azure AD tenant can sign in. Use this when the platform is hosted as a service for several organisations.
The two modes share the same properties; they differ only in how the issuer-uri, authorization-uri, token-uri, and logout-uri are constructed. Examples for both forms are given below.
Prerequisites: Azure AD app registration
Before configuring ODD Platform, register an application in Azure AD:
Go to Azure Active Directory → App registrations → New registration.
Choose the supported account types (single-tenant vs. multi-tenant) that match the deployment.
Add a Web redirect URI:
{host}/login/oauth2/code/azure.Under Certificates & secrets, generate a client secret and store its value — it cannot be viewed again later.
Under API permissions, add the following Microsoft Graph delegated permissions and grant admin consent:
openid,offline_access,User.Read. Addemailandprofileas well if theprofile/emailscopes are requested below.(Optional) To use
admin-groups, choose one of the following:Azure AD App Roles (default, recommended) — under App roles, create the roles you want to grant ADMIN (for example
Admins,Managers), then assign users or groups to those roles. Azure will emit the role values in therolesclaim of the ID token, which is what ODD Platform reads by default.Azure AD security groups — under Token configuration → Add groups claim, include the
groupsclaim in the ID token. You must also setgroups-claim: groupsin the ODD configuration (see the note underadmin-groupsbelow), because the Azure handler reads fromrolesunless told otherwise.
Note the Application (client) ID and Directory (tenant) ID — both are needed by the configuration below.
Single-tenant configuration
Use the tenant-specific issuer-uri; {azure_tenant_id} is your Directory (tenant) ID. Spring Security will discover authorization-uri, token-uri, jwk-set-uri, and user-info-uri from the issuer's OpenID Connect discovery document, so they do not need to be set explicitly. jwk-set-uri is still shown in the example because it can be required when the discovery endpoint is unreachable (for example, from air-gapped networks).
Multi-tenant configuration
For a multi-tenant application, the OpenID Connect discovery document is not served under a tenant-specific URL, so issuer-uri cannot be used. Instead, override authorization-uri, token-uri, jwk-set-uri, and logout-uri to point at the organizations endpoint (use common if the app should also accept personal Microsoft accounts). azure-tenant-id must still be set to the tenant that owns the app registration.
Notes:
Ensure the
openidscope is always included, as it is mandatory for OIDC.The
azure-tenant-idshould correspond to the Azure AD tenant that owns the app registration.The
jwk-set-uriis mandatory for Azure to function correctly with ODD Platform when the discovery endpoint is unreachable.logout-uriis the Azure AD OpenID Connect logout endpoint —https://login.microsoftonline.com/{azure_tenant_id}/oauth2/v2.0/logoutfor single-tenant andhttps://login.microsoftonline.com/organizations/oauth2/v2.0/logout(or thecommonvariant if the app also accepts personal Microsoft accounts) for multi-tenant. On logout, ODD Platform redirects the browser here so Azure AD can end its own session and then return the user to the Platform.
admin-principalsis the list of user identifiers (matched against theadmin-attributeclaim,emailin the examples above) that will be granted the ADMIN role on login.admin-groupsgrants the ADMIN role to every user whose token contains one of the listed values. By default, ODD Platform's Azure handler reads these values from therolesclaim, which Azure AD populates from App roles assigned to the user. The values inadmin-groupsmust match the app rolevaluefields, not Azure AD display names.To grant ADMIN based on Azure AD security group membership instead, set
groups-claim: groupsin the Azure configuration and add thegroupsclaim to the ID token in Azure (Token configuration → Add groups claim). Theadmin-groupslist then matches against values emitted in thegroupsclaim (group object IDs by default — switch the Azure claim output toGroup Namein the Token configuration dialog if you want to match on group display names).
If an external user's login doesn't provide the email attribute by default, ensure that the user exists as an external guest in Azure AD associated with an email.
Troubleshooting Tips:
If you encounter errors regarding the missing
emailattribute, ensure the user exists in Azure AD as a properly configured external guest user with an email attribute.For single-tenant deployments, always verify that
issuer-uri,jwk-set-uri,user-info-uri, andlogout-uricorrespond to your tenant ID.If logout returns a 500 error or never completes, verify that
logout-uriis set and matches your single-tenant / multi-tenant choice. An unsetlogout-uritriggers aNullPointerExceptionin the Azure logout handler; the browser sees a 500 while the user remains signed in on both sides.If
admin-groupshas no effect, check which claim is being read:App Roles (default) — decode the ID token at
jwt.msor equivalent; confirm therolesclaim is present and contains the app-rolevaluefields that youradmin-groupslist references. If therolesclaim is absent, verify that you have assigned users to app roles under Enterprise applications → your app → Users and groups.Security groups — confirm that
groups-claim: groupsis set in the ODD configuration (without this,admin-groupsis matched againstroles, notgroups), and that thegroupsclaim is emitted by the Azure token configuration.
Other OIDC providers
ODD Platform doesn't have any specific parameters for other providers, so they can be easily configured using default parameters. You can check examples below for OKTA and Keycloak OIDC providers.
Keycloak with PKCE
Keycloak realms can be configured to require Proof Key for Code Exchange (PKCE, RFC 7636) on the authorization code flow — most commonly for public clients (browser-based or SPA-style flows that cannot keep a client secret).
To enable PKCE, set pkce: true on the Keycloak provider configuration and leave client-secret empty. The platform then registers the client as public (client_authentication_method=none) and Spring Security performs the code exchange with PKCE.
Logout token-revocation matrix
When an operator clicks Sign out in the ODD UI, the platform invalidates the local server-side session and — depending on the provider — optionally asks the identity provider (IdP) to revoke the OAuth2 access token. Whether the IdP-issued token is actually revoked differs per provider, and the residual-token-validity window can be substantial. Operators on shared or public workstations need to know which providers leave a usable token behind after a "successful" logout.
AWS Cognito
yes
no — the handler redirects to Cognito's /logout for session removal but does not call /oauth2/revoke
access token ~1 hour; refresh token up to 90 days, depending on the User Pool configuration
GitHub
yes
yes — the handler calls DELETE /applications/{client_id}/grant to revoke the OAuth grant on the user's behalf
—
yes
yes — the handler POSTs to oauth2.googleapis.com/revoke for the access token
—
Azure AD
yes
no — Azure AD v2.0 does not expose RFC 7009 token revocation (protocol-level limitation); the handler can only redirect to the OIDC end-session endpoint
access token ~1 hour; refresh token per tenant policy
ODD_IAM
yes
no — session invalidation only by design (the IAM provider does not implement revocation)
—
Okta / Keycloak / Custom OIDC
yes
no — there is no dedicated logout handler; the platform invalidates only the local session
per IdP policy on the access token; refresh tokens persist until they expire
Operator caution. For any row in the IdP access token revoked: no column, a token captured by an attacker before logout — for example, from browser-side credential exfiltration on a shared terminal — remains usable against IdP-protected resources for the remainder of the token's natural validity. The ODD platform's session invalidation does not affect this; the only mitigation in those modes is to wait for the token to expire or to sign out at the IdP directly (via the Cognito hosted UI, the Azure portal session controls, etc.).
A platform-side fix for the Cognito gap — calling /oauth2/revoke from the Cognito logout handler — is tracked upstream.
Post-logout redirect derivation
All five OAuth2 logout handlers (Cognito, Github, Google, Azure, ODD_IAM) build the post_logout_redirect_uri (or, for the providers that use a different name, the equivalent return-URL parameter) from the inbound request's URI. The platform reads scheme, host, port, path, and query from the inbound HTTP request and reconstructs the base URL with / as the path — there is no allowlist of trusted hosts, no platform.base-url-style configuration property, and no scheme enforcement.
In a typical deployment where the inbound Host header is the browser-presented value and the platform's reverse proxy passes it through unchanged, the post-logout redirect points back to the same origin and there is nothing to worry about. The concern surfaces when the reverse proxy trusts user-controlled Host or X-Forwarded-Host headers, or when the IdP's post-logout-redirect allowlist is wider than the platform's host.
UI feedback on logout outcome
The SPA's Sign out action sends the user to /logout via a hard navigation; the platform handles the rest. The SPA does not distinguish between "logged out + IdP token revoked" (Google / GitHub) and "logged out + IdP token still valid" (Cognito / Azure / ODD_IAM) in any UI-visible signal. Operators on shared or public terminals who depend on revocation should not infer it from the absence of an error; sign out from the IdP directly (Cognito hosted UI, Azure portal, etc.) in addition to ODD logout when the workstation is not yours. Per-provider feedback messages in the SPA are tracked upstream.
Last updated