OpenID Connect 1.0 SSO deprecated
Introduction
With version 7.10.0 App Suite introduces the support for Single Sign On (SSO) with OpenID which is also compatible with version 7.8.4. OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It enables Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner. The implementation supports handling of multiple OpenID providers (OP) for different hosts from one single instance. Logout with termination of the OPs session is available alongside an autologin mechanism which checks for a valid OP session before login. So far only the code flow for login and a third party initiated login are supported. There is also no possibility to gather additional user information from an OP after authorization so far. The session status mechanism with two communicating IFrames like suggested by the standard is also not supported.
The full OpenID specification can be found here.
An operator guide to setup Keycloak as an Authorization Server can be found here.
Feature overview
- Web SSO using Authorization Code Grant
- HTTP API login with username/password using Resource Owner Password Credentials Grant
- Autologin with valid session confirmation on OP side
- Direct autologin via session storage in own OIDC Cookie
- Logout with additional redirect to OP for session termination
- Direct logout from App Suite
- OAuth token refresh
- Third Party initiated login
- Multiple registration of OpenID backends in one instance
- Multiple tenants working with one backend
- Back- and frontchannel logout
Supported Message Flows
Login as well as optionally front- and backchannel logout flows are supported. App Suite logout is either with termination of the OP session or without. There are also two autologin flows supported, one with redirect to the OP for a valid session check and one without.
Web SSO login flow
The following diagram describes the whole code flow login process. The current implementation does not gather additional user informations like described in step 6.
API login flow
OX Mail, Exchange-ActiveSync, CalDAV/CardDAV or other clients/protocols might only support direct username/password authentication. It is possible to still use OpenID Connect for according authentication requests, by using the Resource Owner Password Credentials Grant, which is part of the OAuth 2.0 Core Framework. This is an optional feature that needs to be enabled explicitly via the com.openexchange.oidc.enablePasswordGrant
configuration property.
Sessions that got spawned through the resource owner password grant flow are not considered as SSO sessions shared by multiple clients, therefore the removal of such an OX session also leads to an explicit logout request being issued against the OP, independently of property com.openexchange.oidc.ssoLogout
.
OAuth Tokens
An OAuth bearer access token is always required to be issued along with the ID token!
Note that OAuth tokens contained in token responses are always set as session parameters and internally validated on each request. If the Authorization Server issues an access token with expiry date, this date will determine how long the App Suite session can be used. OIDC sessions will be terminated on the first request that happens after expires_in - (com.openexchange.oidc.oauthRefreshTime / 1000)
seconds.
If a refresh token is contained in the token response, access tokens will be refreshed during the first request that happens after expires_in - (com.openexchange.oidc.oauthRefreshTime / 1000)
seconds. A failed refreshed due an invalid/revoked refresh token or an invalid_grant
response will lead to the session being terminated.
In the default implementation, sessions created through OpenID Connect have the "stay signed in" marker set to false
, which means that unused sessions will expiry expire after the timespan configured at com.openexchange.sessiond.sessionDefaultLifeTime. Depending on the desired behavior, it might be useful to align this default session time to the lifetime of the refresh tokens.
If an ID Token is returned as a part of a token refresh request, the following requirements apply (see openid-connect-core-1.0, section 12.2):
The iss claim value of the new ID Token MUST be the same as the iss claim value in the original ID Token issued during the initial authentication.
The sub claim value of the new ID Token MUST be the same as the sub claim value in the original ID Token issued during the initial authentication.
The iat Claim of the new ID Token MUST represent the time when the new ID Token is issued.
The aud claim value of the new ID Token MUST be the same as the aud claim value in the original ID Token issued during the initial authentication.
If the original ID Token contains an auth_time Claim, the value of the auth_time Claim in the new ID Token MUST represent the time of the original authentication, not the time when the new ID Token is issued.
The azp claim value of the new ID Token MUST be the same as the "azp" claim value in the original ID Token issued during the initial authentication. If the original ID Token did not contain an azp Claim, the new ID Token MUST NOT include one.
A failed refresh due an invalid ID token will lead to the session being terminated.
If expires_in
is not contained in the token response, the session will live as long as the configured short-term session lifetime, no matter if a refresh token is contained or not. The access token will never be refreshed in that case.
Autologin with check for a valid OP session
If no valid session is present on side of the OP, the user is asked to login first. The handling on side of the Relying party is untouched by this scenario.
Autologin directly in App Suite
If no OIDC cookie exists, the standard login procedure is triggered.
Logout with redirect to OP and termination of session
To enable the logout with previous termination of the OP session, the com.openexchange.oidc.ssoLogout
property has to be set to true
. Additionally the com.openexchange.oidc.rpRedirectURIPostSSOLogout
property has to be configured to redirect the user to the logout mechanism that handles the response on App Suite site. This location has to be registered and known for the client, otherwise the logout request will not be handled. Finally, property com.openexchange.oidc.rpRedirectURILogout
holds the location, where the user should be redirected after a successful logout, a custom goodbye page, for example.
If an error occurs during the logout process, like an invalid response from the OP, the RP session is terminated anyways. In case a logout flow cannot be processed because the OX session no longer exists, a redirect to the location configured in com.openexchange.oidc.rpRedirectURILogout
is performed as fallback.
For the optional confirmation page, there is an example dialog in the examples/backend-samples
repository which should work with a connect2ID OpenID server. The according project is com.openexchange.sample.c2id-logout-page-jsp
. The example is called with the following parameters:
id_token_hint:eyJraWQiOiJDWHVwIiwiYWxn...
post_logout_redirect_uri:https://192.168.33.109//appsuite/api/oidc/logout
state:di26WOr8iZyVFReDvgsNwueDolfgwuB1rpjbo3t99Wo
id_token_hint
: The users id token, to acquire the correct session.post_logout_redirect
: Where should the user be redirected after the confirmation.state
: A generated state property, to verify the response later.
Direct Logout from App Suite
As alternative to the logout flow with termination of the SSO session at the OP, also an immediate logout from App Suite only is possible. This direct logout will be performed implicitly if property com.openexchange.oidc.ssoLogout
is set to false
.
Third-Party Initiated Login
For the third-party initiated login, there are 3 parameters which are processed by the middleware, informations about these parameters can be found here. Please note that in the current implementation, target_link_uri is only checked for equality with com.openexchange.oidc.uiWebPath
.
Developers Guide
There are three relevant bundles, the com.openexchange.oidc
bundle, which contains all relevant interfaces. The default implementation, contained in the com.openexchange.oidc.impl
bundle, which uses the Nimbus SDK to provide the needed OpenID features, located in the com.nimbus
bundle. Further details can be found here. The default implementation is designed to work with the connect2id OpenID server, per default all features can be used, except the logout via OP flow. Therefore the connect2ID server has to be extended by a logout confirmation page like described before.
The OIDC Backend
The core implementation of the OIDC Backend can be used as reference to all further implementations. To keep the needed effort as small as possible, every new implementation should extend the core com.openexchange.oidc.spi.AbstractOIDCBackend
and override only those functions, that should behave differently. The configuration of every backend is loaded from an implementation of the com.openexchange.oidc.OIDCBackendConfig
interface. The developer should also extend the core implementation of this interface, which is com.openexchange.oidc.spi.AbstractOIDCBackendConfig
and replace only those property calls, that are different from the core configuration. This way multiple backends can share the same properties, which reduces redundancy and keeps maintenance efforts small. The com.openexchange.oidc.tools.OIDCTools
provide a set of useful functions and constants, remember to have a look at those, before implementing the same for every backend.
The used Cookies
The used cookies are the open-xchange-public-session-
, open-xchange-secret-
and open-xchange-session-
cookie.
Tokens
The OAuth access and refresh tokens are both stored in the user session, alongside with the ID token of the user. The refresh token is automatically used to get a new Access token, when needed. The IDToken usually stores the users unique identification in the sub
field, but can use other claims to identify App Suite users, too. How contexts and users are resolved from an ID token is configurable and can also be overridden with custom OIDC Backends.
Operators Guide
If you want to enable this feature without starting the implemented core backend, you have to disable it by setting com.openexchange.oidc.startDefaultBackend=false
. For an overview of all possible properties and their description, take a look at all OpenID properties
Configuration
You can find a description of every property on the property documentation site by searching for oidc
. Alternatively, take a look at the com.openexchange.oidc.OIDCConfig
and com.openexchange.oidc.OIDCBackendConfig
classes.
If you don't specify a distinct UI web path for your backend, via com.openexchange.oidc.uiWebPath
, you have to configure the default path of the web UI, which is used in several places, via /opt/open-xchange/etc/server.properties
. If you haven't already (because of other requirements), set com.openexchange.UIWebPath to /appsuite/
.
User resolution
For OX session creation, an according OX user entity needs to be resolved based on the issued ID token. Per default, it is expected that the sub
claim contains a value in the form of <user-name>@<context-name>
. <context-name>
must be a valid login mapping of the context or the numeric context identifier. <user-name>
must match the provisioned user name.
The resolution behavior is configurable to use different claims and different patterns to match claim values to lookup values. For details, see configuration documentations for the following properties:
- com.openexchange.oidc.contextLookupClaim
- com.openexchange.oidc.contextLookupNamePart
- com.openexchange.oidc.userLookupClaim
- com.openexchange.oidc.userLookupNamePart
With a customized OIDC backend, the resolution behavior can be overridden based on customer requirements.
Autologin configuration
If you want to use the OpenId login feature, you have to make sure that the regular autologin mechanism is disabled by setting the associated Sessiond property to false com.openexchange.sessiond.autologin=false
. This is a crucial precondition for any of the provided autologin mechanisms.
Session Lifetime Configuration
If refresh tokens are used by the Authorization Server, they are stored alongside in the App Suite session to trigger the token refresh flow as needed. If the default OIDC backend implementation is used, sessions created through OpenID Connect have the "stay signed in" marker set to false
by default. This can be changed through configuration parameter com.openexchange.oidc.staySignedIn, and has an impact on the cookie- and OX session lifetime. If true
, cookies will be decorated with the max. age as configured via com.openexchange.cookie.ttl. Also, the maximum idle time of OX sessions will be aligned to com.openexchange.sessiond.sessionLongLifeTime. If set to false
, cookies will use session lifetime, and the maximum OX session idle time follows com.openexchange.sessiond.sessionDefaultLifeTime.
Frontend Configuration
In order to enable the special OpenID login handling, enable the oidcLogin flag via the /opt/open-xchange/etc/as-config.yml
. E.g.:
default:
host: my-domain.com
oidcLogin: true
Front- / Backchannel Logout
Optionally, to support "Single Sign-out" scenarios, the backend can be configured for accepting front- and backchannel logouts initiated by the OP. If one of those is enabled, a separate, Redis-backed mapping for OIDC session to OX session identifiers is managed internally.
Frontchannel Logout
Support for frontchannel logout by setting com.openexchange.oidc.frontchannelLogoutEnabled
to true
. If enabled, a separate servlet is registered under /
, which will accept frontchannel logout requests from the OP via the user agent (the "sid" parameter is required in the request to associate the OP session with the corresponding OX session properly).
Typically, the resulting URL for the logout servlet needs to be configured for the registered client at the OP as "Front-Channel Logout URL".
Backchannel Logout
Similar as above, support for backchannel logout by setting com.openexchange.oidc.backchannelLogoutEnabled
to true
.
If enabled, a separate servlet is registered under /
, which will accept backchannel logout requests from the OP (the "sid" claim is required in the logout token to associate the OP session with the corresponding OX session properly).
Also, the resulting URL for the logout servlet needs to be configured for the registered client at the OP as "Back-Channel Logout URL". Also, one needs to configure that the OIDC session identifier (as in the sid
claim) is included in the logout request data ("Backchannel Logout Session Required").
Termination of OP Sessions
OP sessions are closed during the regular, user initiated logout flow from App Suite if com.openexchange.oidc.ssoLogout
is true
. Also, sessions spawned through the Resource Owner Password Credentials Grant are closed on the OP side as they're not shared between multiple clients, see above for further details.
Additionally, it is also possible to terminate these sessions upon further session removal events by invoking the configured com.openexchange.oidc.opLogoutEndpoint
(yet without further redirects). The session events can be configured via property com.openexchange.oidc.opLogoutOnSessionRemoval
, where the following removal events can be defined as a comma-separated list: - expired
- The session is removed after being idle/unused for a certain duration - user_closed
- Another session is removed explicitly by the user (usually via session management API) - admin_closed
- A session is removed explicitly via an administrative interface (e.g. close sessions commandline utility or REST API)
Example Configuration
Example configuration based on the Keycloak setup described here:
/opt/open-xchange/etc/oidc.properties
com.openexchange.oidc.enabled=true
com.openexchange.oidc.startDefaultBackend=true
com.openexchange.oidc.hosts=appsuite.example.com
com.openexchange.oidc.ssoLogout=true
com.openexchange.oidc.opLogoutOnSessionRemoval=user_closed,admin_closed
com.openexchange.oidc.clientId=appsuite.example.com
com.openexchange.oidc.clientSecret=vVl8eEWUZOEJ9QZHfOEXAMPLESECRET
com.openexchange.oidc.rpRedirectURILogout=true
com.openexchange.oidc.rpRedirectURIInit=https://keycloak.example.com/appsuite/api/oidc/init?flow=thirdParty
com.openexchange.oidc.rpRedirectURIAuth=https://keycloak.example.com/appsuite/api/oidc/auth
com.openexchange.oidc.rpRedirectURIPostSSOLogout=https://keycloak.example.com/appsuite/api/oidc/logout
com.openexchange.oidc.opIssuer=http://keycloak.example.com:8085/auth/realms/demo
com.openexchange.oidc.opAuthorizationEndpoint=http://keycloak.example.com:8085/auth/realms/demo/protocol/openid-connect/auth
com.openexchange.oidc.opTokenEndpoint=http://keycloak.example.com:8085/auth/realms/demo/protocol/openid-connect/token
com.openexchange.oidc.opJwkSetEndpoint=http://keycloak.example.com:8085/auth/realms/demo/protocol/openid-connect/certs
com.openexchange.oidc.opLogoutEndpoint=http://keycloak.example.com:8085/auth/realms/demo/protocol/openid-connect/logout
com.openexchange.oidc.uiWebPath=/
com.openexchange.oidc.failureRedirect=https://www.open-xchange.com/
com.openexchange.oidc.enablePasswordGrant=true
com.openexchange.oidc.passwordGrantUserNamePart=local-part
com.openexchange.oidc.contextLookupClaim=email
com.openexchange.oidc.userLookupClaim=email
In addition to the above configuration, Keycloak users should have the email attribute, which needs to be included in ID tokens. To enable the email attribute in ID tokens, please check Add to ID token
in the the client's email attribute mapper.