Skip to content
Security > Authentication

OAuth

OAuth authentication is a separate plugin, not bundled with 3forge Enterprise: request and install it, then point 3forge Enterprise's SSO extension point at it along with your provider's OAuth client details. This is a browser-redirect login, not a username-and-password check, so it works for the Web only; see Authentication -> Two plugin mechanisms, not one if you need REST or WebSocket clients authenticated too.

Configuring the connection

Register a web application with your OAuth provider first, so you have a client ID, a client secret, and a redirect URI to give it. Then set sso.plugin.class, the URL that triggers the redirect, and the provider's endpoints, and restart:

# Load the OAuth plugin as the SSO plugin
sso.plugin.class=com.f1.ami.web.AmiWebOAuthPluginImpl

# The provider, and the client registered with it
oauth.server.domain=https://your-provider.example.com
oauth.client.id=your-client-id
oauth.client.secret=your-client-secret

# Where the provider sends the browser back. The path after the host is where 3forge Enterprise expects the response
oauth.redirect.uri=https://your-ami-host:33333/oauthRedirectUrl

# Provider endpoints, appended to oauth.server.domain
oauth.authorization.endpoint=/oauth2/authorize
oauth.token.endpoint=/oauth2/token
oauth.refresh.token.endpoint=/oauth2/token

# Scopes requested, and the claim that becomes the user name
oauth.scope=openid profile email
oauth.username.field=email

oauth.redirect.uri must exactly match the redirect URI registered with the provider: most providers reject the login attempt outright if the two differ by so much as a trailing slash.

After restarting, open the Web's address in a browser, and the login redirects to the provider. A successful login redirects back into the Web already signed in; a rejected one is turned back by the provider before 3forge Enterprise sees anything to accept or reject.

Provider requirements

  • Set every endpoint for a provider other than Okta: The defaults are Okta-style paths (/oauth2/default/v1/authorize, /oauth2/default/v1/token, and /oauth2/default/v1/logout), and oauth.refresh.token.endpoint defaults to the token path.
  • Allow offline tokens, or drop the scope: The default scope is openid profile email offline_access, and offline_access asks for a refresh token. A provider can reject the token request with an error such as Offline tokens not allowed for the user or client, so allow offline tokens at the provider or set oauth.scope without offline_access.
  • ID token claims become user attributes: Each claim is added to the user's attributes as amiscript.variable.<claim>, for example amiscript.variable.email.

Mapping OAuth claims to roles

Set a role property to the OAuth/OIDC claim name that carries it, then a values property to say which values mean "yes":

1
2
3
4
oauth.ami.isadmin.field=groups
oauth.ami.isadmin.values=ami-admins
oauth.ami.isdev.field=groups
oauth.ami.isdev.values=ami-devs

Set the field and values properties together. A field without its values property fails the sign-in with an error.

To grant attributes by role, name the roles 3forge Enterprise recognizes, where the token carries them, and what each role grants:

# Claim holding the user's list of roles
oauth.ami.roles.field=groups

# Claim holding the user's default role
oauth.ami.default.role.field=default_role

# Roles 3forge Enterprise recognizes
oauth.ami.roles.enabled=analyst,trader

# Attributes granted by each role
oauth.ami.role.analyst.LAYOUTS=analyst_layout
oauth.ami.role.trader.LAYOUTS=trader_layout

The default role grants all of its attributes. Every other enabled role the user holds adds only its LAYOUTS. If the default role is not enabled, the first enabled role in the user's list becomes the default. A user with no enabled role is rejected at login. Claim names can be nested with a dot.

Refreshing an expired session

  • oauth.access.token.expires.in: the name of the field in the provider's token response that carries the token lifetime. It defaults to expires_in. If the response has no such field, the token is treated as never expiring, and the session does not poll for a new one.
  • oauth.refresh.token.endpoint, oauth.refresh.grant.type, oauth.refresh.scope: the endpoint and parameters used to refresh a token without asking the user to sign in again.
  • oauth.single.logout.enabled plus oauth.logout.endpoint, oauth.logout.redirect.uri: end the session at the provider, not just in 3forge Enterprise, when a user logs out. Single logout takes effect only when oauth.logout.redirect.uri is set.

An open session polls every oauth.session.check.period.seconds and renews its access token at the refresh endpoint. The session ends if the renewal fails, so set oauth.refresh.token.endpoint to the provider's token endpoint.

Other properties

Property Default Purpose
oauth.debug false Logs the authorization exchange in detail. With it on, the log records the token request, including its Authorization header, which encodes the client secret. Turn it off after setup
oauth.validate.certs true Validates the provider's TLS certificate on token and refresh requests
oauth.dynamic.redirect false Builds the redirect URI from the address the browser used to reach the Web, keeping the path from oauth.redirect.uri
oauth.session.check.period.seconds 60 How often an open session renews its access token
oauth.refresh.client.id oauth.client.id Client ID sent on the refresh request
oauth.refresh.client.secret oauth.client.secret Client secret sent on the refresh request
oauth.refresh.redirect.uri none Sent as redirect_uri on the refresh request when set
oauth.code.challenge.method S256 (when unset) PKCE code challenge method. Leave it unset: the plugin also reads this key as the digest algorithm name, so setting it, even to S256, breaks the code challenge

What this page does not cover

Okta has its own, separate authentication plugin: a different class, which reads a subset of the same oauth.* properties. It is not a variant of this plugin, and installing it doesn't require this plugin's jar. See Okta for configuring it.

What this does not support

  • No REST or WebSocket login: OAuth here is a browser-redirect flow, which a REST or WebSocket client cannot follow, so this plugin authenticates the Web only.
  • No single sign-on discovery: there is no property for a .well-known OpenID configuration URL, so every endpoint (oauth.authorization.endpoint, oauth.token.endpoint, and the rest) is set individually.