OAuth 2.0/OpenID 1.0 for LogicNets Release 7.4
This document details authentication and authorization functionality that is available in LogicNets Release 7.4 and higher. The implementation details for this functionality may be different than that for older LogicNets releases. See OAUTH2.0/OpenID 1.0.
OpenID Connect / OAuth 2.0 / SMART-App-Launch Frameworks
The LogicNets system implements the OpenID Connect 1.0 specification (https://openid.net/specs/openid-connect-core-1_0.html). OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 [RFC6749] 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.
As background, the OAuth 2.0 Authorization Framework [RFC6749] and OAuth 2.0 Bearer Token Usage [RFC6750] specifications provide a general framework for third-party applications to obtain and use limited access to HTTP resources. They define mechanisms to obtain and use Access Tokens to access resources but do not define standard methods to provide identity information. Notably, without profiling OAuth 2.0, it is incapable of providing information about the authentication of an End-User. Readers are expected to be familiar with these specifications.
OpenID Connect implements authentication as an extension to the OAuth 2.0 authorization process.
Use of this extension is requested by Clients by including the OpenID scope value in the Authorization Request. Information about the authentication performed is returned in a JSON Web Token (JWT) called an ID Token. OAuth 2.0 Authentication Servers implementing OpenID Connect are also referred to as OpenID Providers (OPs). OAuth 2.0 Clients using OpenID Connect are also referred to as Relying Parties (RPs).
The LogicNets system implemented both the RP and the OpenID Provider roles of the OpenID Connect 1.0 specification (https://openid.net/specs/openid-connect-core-1_0.html).
The OP is also known as an Identity Provider (IdP) and the Relying Party is also known as the (OpenID) client.
The LogicNets system also implements the SMART-App-Launch Framework (http://hl7.org/fhir/smart-app-launch/) which is also based on the OAuth2.0 specification and used in Healthcare to connect third-party applications to Electronic Health Record data and allowing apps to be launched from an EHR system.
LogicNets Application Authentication and Authorization
The authentication and authorization of a LogicNets application are implemented by the LogicNets run-time and the LogicNets logon system component even when a remote IdP is used. The author/modeler of the application does not need to be concerned about this functionality.
The LogicNets infrastructure supports both local users and remote users.
- Local users are managed on the same LogicNets instance as the Application; the authentication and authorization happen all within the same instance.
- LogicNets also supports remote users. In that use case, the Application instance connects with an external/remote IdP using the OpenID Connect protocol. The application instance acts as an RP. The user authentication happens on the remote IdP / OP. The application instance performs the authorization based on the user group memberships reported back by the OP.
The logon component shows a selection form in case both local and remote IdPs are configured or when multiple remote IdPs are configured.
The selection form is omitted when only one remote IdP is configured. The application instance logon-component will redirect immediately to this remote IdP for authentication.
The remote IdP can also be a LogicNets instance.
First-time Login with Local Credentials
The next sequence diagram shows the flow between the user, the application, and the logon component when the application has authentication enabled and the user uses a local account. Since the user is not logged in before the user is asked to enter his/her credentials.
Key:
Green arrows show the steps that are visible in the browser (=visible to the user).
Black arrows show the LogicNets run-time and system components logic.
Blue arrows show the application-specific logic.
Single Sign-on with a Local User Account
The next diagram shows the flow when the user is already authenticated before. An cookie is used to store the authentication id in the client’s browser. The cookie has the scope of the host-name and company name (e.g. https://hostname/companyname) and is browser session bounded (cookie is shared between all tabs of a browser, please check your browser for the exact details when a browser session is ended).
First-time Login using a Remote IdP
The next sequence diagram shows the flow between the application, application-instance-logon-component, and a remote IdP. In this diagram, the remote IdP is also a LogicNets instance acting as the OP. The application instance logon-component acts as an RP.
Single Sign-on with a Remote User Account
The next sequence diagram shows the flow between the applications, application-instance-logon-components, and a remote IdP. In this diagram, the remote IdP is also a LogicNets instance acting as the OP. The application instance logon-components acts as an RP. This diagram shows the use case when the user is already logged before and therefore the step of entering the user’s credentials is skipped. In this diagram, two applications are hosted on two different application instances.
LogicNets as OpenID Provider
LogicNets can be configured as the OP. As starting point, the client (RP) can obtain the LogicNets OpenID Provider Configuration information (see https://openid.net/specs/openid-connect-discovery-1_0.html) via https://<host-name>/<company|tenant>/.well-known/openid-configuration.
As result, the list of available endpoints and other meta-data is returned. The following end-points are supported (starting with HTTPS://<host-name>/<company|tenant>/oauth2token)
End Point | Description |
/authorize | Starts the OAuth flow |
/token | End-point to retrieve initial and refreshed access-, id- and refresh-tokens |
/logout | End-point to log out a user |
/keys | API endpoint to retrieve all active signing public keys |
/introspect | API endpoint to check the active-status of an access-token or refresh-token |
/userinfo | API endpoint to retrieve the information about the authenticated user |
Client Registration
A server user must be created at the OpenID Provider instance before the IDP Client (RP) can use LogicNets as an OP. This can be done via AccessManagement (HTTPS://<host-name>/<company|tenant>/AccessManagement). Via AccessManagement a server-user must be created. The system will generate a client-id and secret. Both need to be copied to the IDP Client. The client-secret is only visible during the creation. The LogicNets OP supports confidential clients (the client can protect the client-secret).
Authorize Endpoint
The LogicNets system supports the ‘Authorization Code Flow’, which means that via the authorize-end-point the user is authenticated and an authorization-code is returned to the client. This authorization-code can be exchanged with an access-token, id-token, and refresh-token via the token-end-point.
Authorize Request Parameters
Parameter | Description |
response_type | REQUIRED. OAuth 2.0 Response Type value that determines the authorization processing flow to be used, including what parameters are returned from the endpoints used. The value must be code since LogicNets uses the Authorization Code Flow. |
scope | REQUIRED. Space delimited, case sensitive list of scopes. The supported scopes are listed below. |
client_id | REQUIRED. OAuth 2.0 Client Identifier created during the client-registration step. |
redirect_uri | REQUIRED. LogicNets will redirect to this URI after the authentication is finished (not-) successfully. The URI should use the HTTPS scheme. The OP will add parameters to this URI to return the results (see authorize-response). |
state | RECOMMENDED. An opaque value used to maintain the state between the request and the callback. |
nonce | OPTIONAL. A string value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient entropy MUST be present in the nonce values used to prevent attackers from guessing values. |
prompt | OPTIONAL. If left empty the OP tries to use single-sign-on (when the authentication session is not yet timed-out (see below for more information)). If the authentication session is timed out the user is forced to enter their credentials. login - will force the user to enter their credentials on that request, negating single-sign-on. create - will open the request-access page to allow the user to sign up for an account, after the sign-up the user will return to the application with the newly created account. |
max_age | OPTIONAL. Maximum Authentication Age. Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated by the OP. If the elapsed time is greater than this value, the OP will attempt to actively re-authenticate the End-User. The max_age hint is also passed to the next OP/IdP when IdPs are chained (see below). |
login_hint | OPTIONAL. Hint to the OP about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the OP. LogicNets will use this hint to prefill the user-name input box. The login_hint is also passed to the next OP/IdP when IdPs are chained (see below). The login_hint is also compared with the currently logged-in user. When these do not match the single-sign-on is aborted and the LogicNets OP will force the user to enter their credentials. |
iss | OPTIONAL. Issuer identifier. In case the LogicNets OP is used as a proxy to other (remote) IdPs this parameter identifies the remote IdP base URL or in the case of SMART-on-FHIR, it Identifies the EHR's FHIR endpoint. The iss value must be equal to one of the configured valid iss-uris in the LogicNets client-OpenID-configuration (see “LogicNets as Relying Party”). The LogicNets OP will use this value to obtain additional details about the remote IdP or EHR, including its authorization URL. This value will also be stored in the access-token. Application can use this information to access the FHIR Server (=iss). Check http://hl7.org/fhir/smart-app-launch/ for more information. |
launch | OPTIONAL. An opaque identifier for this specific launch, and any EHR context associated with it. This parameter is passed to the EHR / remote IdP at authorization time. Check http://hl7.org/fhir/smart-app-launch/ for more information |
alert | OPTIONAL. Currently only ‘not_authorized’ is supported. This is used by the application to indicate to the authorize end-point why the application asks for re-authentication. ‘not_authorized’ is used to indicate that the authenticated user is not authorized to use this application. The authorize end-point will show a proper message to the user and forces the user to enter the credentials (SSO is ignored). |
username | OPTIONAL. The username and pw parameters are used as credentials to authenticate the user without showing a logon-form or use the active SSO authentication session. |
pw | OPTIONAL. The username and pw parameters are used as credentials to authenticate the user without showing a logon-form or use the active SSO authentication session. |
action | OPTIONAL the authorize end-point can also be used to open the user’s account page, to open a change password form etc. This can be controlled via the ‘action’ parameter.
|
license_code | OPTIONAL. This parameter can contain a license-code created via the LogicNets AccessManagement license manager. The license is attached to the authenticated user when a valid and active license-code is supplied. |
Supported Scopes
Scope | Description |
openid | The id_token will be returned when the token-endpoint is called. |
profile | This scope value requests access to the End-User's default profile claims, which are: email, company, name, family_name, given_name, middle_name, preferred_username, locale, updated_at. |
legacy_profile | This scope value requests access to legacy/deprecated first_name and last_name claims. These claims were supported till 7.4.2 and now replaced by the OpenID Connect claims given_name and family_name. |
This scope value requests access to the email claims. | |
phone | This scope value requests access to the phone_number claims. |
groups | This scope value requests access to the groups and groups_str claims. |
category.<category> | This scope value requests access to the roles-claim. The roles-claim will list all user-roles of the authenticated user for the specific category (of an application). The category-value is equal to the project-name of the application and can also be found in the PackageInstaller. |
Based on the current authentication-state and the configuration the authorize-request will
- Automatically authentication the user or
- Redirect to a remote IdP directly or
- Show logon controls to user
The user is automatically authenticated when:
-
the authentication session (single-sign-on) is not expired
-
the authentication session is not logged out by the user
-
the authentication session is not older than the provided max_age parameter
The LogicNets OpenID provider will redirect directly to a remote IdP when automatic authentication is not possible and there is only one remote IdP configured and usage of local user accounts is disabled.
The LogicNets OpenID provider will show logon controls if the user is not automatically logged on and there are more than 1 remote IdPs configured or the usage of local user accounts is enabled.
When the authorize-request is finished (successfully or failed) the provided redirect-uri is called with the authentication-response parameters.
Authentication Response Parameters
These parameters are added to the redirect-uri when the authorization-step is finished.
Parameter | Description |
state | Equal to the state parameter passed in the authorize-request |
code | (success case) the authorization-code that must be used by the IdP Client to obtain the tokens via the token-endpoint. The authorization-code expires after 5 minutes. The authorization-code can only be used once. |
session_state | -not released yet- (success case) Used for session-management (see https://openid.net/specs/openid-connect-session-1_0.html). JSON [RFC7159] string that represents the End-User's login state at the OP. This value is opaque to the RP. |
error | (error case) OAuth 2.0 error code |
error_description | (error case) Human-readable ASCII encoded text description of the error |
Token Endpoint
The RP can retrieve the tokens after the authorize-endpoint returned to the RP (client). The client retrieves the tokens typically via a back-channel (server-to-server) using the LogicNets token-endpoint. The client should send the parameters to the token-endpoint using the HTTP POST method and the Form Serialization (application/x-www-form-urlencoded). Tokens could be leaked (causing security leaks) in case the parameters would be sent via the request query parameters.
The token-endpoint is protected against unauthorized access so the client-id and client-secret must be passed for authentication. The client-id and client-secret can be passed via the POST-body or via the request-authorization-header.
Token Request Parameters
Parameter | Description |
client_id | REQUIRED. OAuth 2.0 Client Identifier created during the client-registration step. The same client-id as passed to the authorization-endpoint must be used. |
client_secret | REQUIRED. OAuth 2.0 Client secret created during the client-registration step. |
redirect_uri | OPTIONAL. Redirect-URI which is also passed to the authorize-endpoint. |
grant_type | REQUIRED. This must be either ‘authorization_code’ or ‘refresh_token’. When ‘authorization_code’ is passed also the ‘code’ parameter must be filled. The token-endpoint will return the tokens that a generated during the authentication-step (using the authorize-endpoint). When ‘refresh_token’ is passed also the ‘refresh_token’ parameter must be filled with a refresh-token that is received from a previous call to the token-endpoint. |
code | REQUIRED when grant_type=’authorization_code’. This parameter shall contain the code received via the call-back of the authorization-endpoint. This code can only be used immediately after the callback is called. |
refresh_token | REQUIRED when grant_type=’refresh_code’. This parameter shall contain the refresh_token received in an earlier call to the token-endpoint. The call will only succeed when the refresh_token is not expired. |
Successful Token Response
The token-request will return with an HTTP status code 200 when the request could be handled successfully. In a successful response, the following data will be returned in a JSON format.
Parameter | Description |
access_token | The Access-token that can be used to authenticate a client in out-bound web-api calls. |
token_type | “bearer” |
refresh_token | Refresh tokens that can be used to retrieve new access- and id-tokens when they are expired. |
expires_in | Number of seconds until the access-token is expired |
id_token | JWT OpenID connects compatible ID-token. This token is only part of the response in case the ‘openid’ scope is passed to the initial call to the authorize-endpoint. This id-token contains all known information about the authenticated user |
Failed Token Response
In case of an error, a non-200 HTTP-status-code will be returned with a JSON response that includes an error-id and error-description.
Parameter | Description |
error | OAuth 2.0 error code |
error_description | Human-readable ASCII encoded text description of the error |
Possible Errors
Status Code | Error | Description |
401 | invalid_client | client_id and/or secret are not correct |
400 | invalid_client | client_id doesn't match with the client-id belonging to the code or refresh_code. |
400 | invalid_request | code not found |
400 | invalid_request | user is logged out |
400 | invalid_request | authentication/keep-me-logged-on session is expired |
500 | invalid_request | internal error during updating state |
400 | invalid_request | token is retrieved too late |
400 | invalid_request | token already retrieved earlier |
400 | invalid_request | redirect uris doesn't match |
Introspect Endpoint
The introspect-endpoint can be used to validate a token. This end-point will return whether the passed token is still active. The introspect-endpoint is protected against unauthorized access so the client-id and client-secret must be passed for authentication. The client-id and client-secret can be passed via the POST-body or via the request-authorization-header. Tokens could be leaked (causing security leaks) in case the parameters would be sent via the request query parameters.
Introspect Request Parameters
Parameter | Description |
client_id | REQUIRED. OAuth 2.0 Client Identifier created during the client-registration step. The same client-id as passed to the authorization-endpoint must be used. |
client_secret | REQUIRED. OAuth 2.0 Client secret created during the client-registration step. |
token | REQUIRED. access-token, id-token, or refresh-token received earlier from a LogicNets token-endpoint. |
token_hint | OPTIONAL. The following values are supported ‘access_token’, ‘id_token’, or ‘refresh_token’. |
Successful Introspect Response
The introspect-request will return with an HTTP status code 200 when the request could be handled successfully. In a successful response, the following data will be returned in a JSON format.
Parameter | Description |
active | True when the token is correct and still active. Otherwise, the value will be false when the token is no longer active, when the token is expired (or in case of a refresh-token when the authentication session is expired), when the related authentication session is logged out, when the token doesn’t exist, when the token is not created for this client-id, when the token structure is not correct, or when the token signature cannot be verified. The inactive reason is not returned for security reasons, however, the reason is logged by the OpenID provider. |
exp | (only in case the token is active) Expiration time on or after which the token is not valid anymore. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time. |
iat | (only in case the token is active) Time at which the token was issued. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time. |
Failed Introspect Response
In case of an error, a non-200 HTTP-status-code will be returned with a JSON response that includes an error-id and error-description.
Parameter | Description |
error | OAuth 2.0 error code |
error_description | Human-readable ASCII encoded text description of the error |
Possible Errors
Status Code | Error | Description |
401 | invalid_client | client_id and/or secret are not correct |
400 | invalid_request | missing token |
Userinfo Endpoint
The userinfo endpoint can be used to retrieve information about the logged-on user. This end-point will return similar claims as present in the id-token. The userinfo endpoint is protected against unauthorized access so a valid access-token must be present as a bearer token in the authorization-header for authentication.
Successful Userinfo Response
The userinfo request will return with an HTTP status code 200 when the request could be handled successfully. In the response, the sub-claim is always returned and based on the authorized scope also other claims, such as email, name, and given_name.
Failed Userinfo Response
In case of an error, a non-200 HTTP-status-code will be returned with a JSON response that includes an error-id and error-description.
Parameter | Description |
error | OAuth 2.0 error code |
error_description | Human-readable ASCII encoded text description of the error |
Possible Errors
Status Code | Error | Description |
401 | invalid_token | the bearer token must be an access-token |
401 | invalid_token | the access-token signature is not valid |
401 | invalid_token | unknown access-token |
401 | invalid_token | the access-token authentication session is logged out |
401 | invalid_token | the access-token is expired |
401 | invalid_token | missing bearer token |
End-session/Log-out Endpoint
The LogicNets OpenID provider also provides an end-session-endpoint to logout the user (authentication-session). The current active authentication-session is logged out when this endpoint is called via the browser. The user is asked to authenticate again when the authorization-endpoint is accessed after this call. The token-introspect will return active=false when a token is passed that is created using the authentication-session. The token-endpoint will return an error when a refresh-token is used that is created using the authentication-session.
End-session Request Parameters
Parameter | Description |
post_logout_redirect_uri | OPTIONAL. This is used to redirect back to the client after sign-out. The LogicNets OpenID provider will show a message that the user is logged out when no post_logout_redirect_uri is specified. |
state | OPTIONAL. If a valid post_logout_redirect_uri is passed, then the client may also send a state parameter. This will be returned to the client as a query string parameter after the user is signed out. This is typically used by clients to round-trip state across the redirect. |
id_token_hint | OPTIONAL. An id_token can be passed to log out a specific authentication-session that belongs to the passed id_token. If left empty the authentication-cookie is used to identify the session that must be logged out. |
Authentication Timeout/Keep-Me-Logged-In Timeout
The LogicNets OpenID Provider supports single-sign-on (the user doesn’t need to authenticate again when the user last authentication happened recently). The single-sign-on information is stored in a browser-cookie called ‘authenticationid’ and it is protected against theft using constantly changing session-pin-cookies. By default the authentication cookie using the SameSite ‘lax’ setup, which means that the cookie is not shared between parent and iframes, but shared between browser-tabs. The SameSite settings shall be changed via the System Configuration to support single-sign-on i.c.w. iframes. To support iframes the SameSite should be set to ‘none’.
The authentication session time-out is equal to the configured session-time-out (default: 240 minutes). The timeout is extended every time the authorize-endpoint is accessed (via the browser) before the authentication session is expired. The authentication session time-out is also extended when the token-endpoint is used to refresh the tokens. The authentication session is also ended when the end-session-endpoint is called.
Authentication of Outbound and Inbound Web APIs
Most Web APIs are protected against unauthorized access. LogicNets supports different kinds of authentication methods for outbound and inbound Web API calls.
Inbound authentication for Web-APIs happens always via the HTTP-authorization-header. LogicNets supports both basic and bearer authorization-headers. LogicNets supports 2 types of bearer tokens: (1) private_key_jwt (similar as in client-authentication in https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication) and (2) access token.
The access-token is the access-token received from the token-endpoint (see above). The private_key_jwt is a JWT token that is signed with a private key that only the client has. The public key is stored at the Web-API endpoint. Via AccessManagement on the Web-API endpoint, a public and private key can be generated for a server-user. The public key is stored and the private is only available during generation and needs to be copied to the client of the Web-API.
Authentication for outbound Web-APIs can be configured in the call-webservice-part. Which supports basic, bearer, and manual authorization headers. It also provides the option to use the access token of the currently logged-in user.