Consuming projections from non-IFS apps using OAuth2

In this document basic instructions for consuming projections from integrations are outlined and different tactics for doing so are discussed. Depending on the integration in question, you would do things slightly differently.

Contents

In general

The Identity Providers have some differences, but they do have most things in common. The general parts of this go into this section. The flows are mostly the same with some details that differ between vendors and depending on the flow used we can support different types of clients. The point of all flows is to equip the client with an access token that can be sent up to the application server to gain access, but how a client would go about this is a bit different depending on the nature of the client. It is out of scope for IFS to describe the entire protocol, but this document contains the intricacies of how IFS Applications expects an implementation to behave as well as how the supported vendors relate to this.

Bearer token usage

Applications can get access to IFS Applications using access tokens sent as bearer tokens up to IFS Applications. Bearer tokens are sent in a special HTTP header called “Authorization”. When you have a token, the authorization header is expected to be in a certain format that can be read by IFS Applications. This format is described in detail in https://tools.ietf.org/html/rfc6750 but can be summarized as:

Authorization: Bearer <INSERT YOUR TOKEN HERE>

No matter how the token is retrieved, it is possible to do it like this. 

Authorization code flow – for clients that can render web browsers

The best method to use for getting tokens, if it is possible, is Authorization Code Flow. Graphical clients can often make use of the system browser for this. Web clients can often be redirected in the browser they are already using. If that fails, a web browser control like an Android Web View can also be utilized. Some graphical clients have native OAuth2 support that can be utilized. If these are options, it is best to do it like that.

The way that this works is that the client opens a predefined URL to the Identity Provider (called the “Authorization Endpoint”) in a browser. The query parameters to this URL contain instructions to the Identity Provider that control its behavior.

If the Authorization endpoint was accessed in a correct way, then it will redirect to a place where the application can consume the result. If the application is hosted on a server, a specific endpoint on that server will be used. If the application is a native app running on client devices such as PCs or smartphones, then there is an option of registering a URL with a scheme that is consumed by the application. This is done by registering this with the operating system, and it works much like redirections to a URL starting with “file://” would open a file explorer. For more information on how this works, see below links.

Once there, the application will have access to a single-use code that can be sent to another URL on the Identity Provider called the Token Endpoint. If sent there, the Identity Provider will award the application with an access token and optionally a refresh token which can be sent back up to get a new access token when it expires.

The links below contain more information about the Authorization Code Flow.

Using libraries or pre-packaged application support

A lot of complexity with the Authorization Code Flow can be circumvented by using a library that provides the functionality. If this is an option, it is recommended to go that route. The same goes for the integrated OAuth2 support available in some applications. The one limitation regarding integrated OAuth2 support is that sometimes the OAuth2 support is quite rudimentary and all flows are not available for configuration.

Most languages have libraries available for OAuth2 clients. Examples of libraries that can be used to do OAuth2 are listed at https://oauth.net/code/.

Client secret and/or PKCE

Depending on the type of client used, the question whether to use a confidential or public client becomes relevant. A confidential client requires the use of a client secret to retrieve access tokens and these secrets should be guarded. Client applications that cannot securely store a client secret do not have a reason to use one, and as such they use public clients that implement PKCE.

Most of the time this refers to native apps that run on client devices, whereas server-side apps should use a client secret. For this reason, most of the documentation in IFS refers to the public client used in IFS as the “Native Client” and the confidential client as the “Web Client”. If the web client is used, the client secret is required. If a native client is used, PKCE may be required and the Identity Provider may enforce some security rules to ensure that the application that performed the login at the authorization endpoint is the same that requests an access token.

If the app is hosted on a server, where the server side handles OAuth2 and sends the access token to IFS (examples include the Aurena Bot and the Aurena Client) it is appropriate to use a confidential client with a client secret. If the app is a native application (such as IEE or a mobile app) or a single page app that handles OAuth2 from a client-side Javascript library (we have no examples currently) it is appropriate to use the public client with PKCE.

The main exception to this is Azure AD where a client can be both confidential and public at the same time, detailed later in this document.

For more information about native applications, see https://oauth.net/2/native-apps/. 

Authorization requests and parameters

In this section the most important parts of a standard flow are laid out. Below is an example of the URL called in the browser when initiating a login flow when logging in to Aurena, with relevant query parameters in bold and highlighted. A third-party app would do something similar, but with some variations detailed below.

https://login.microsoftonline.com/<SOME_ID_HERE>/oauth2/authorize?client_id=<SOME_ID_HERE>/&redirect_uri=https://cmbgse1919.corpnet.ifsworld.com:48080/main/ifsapplications/web/oauth2/callback/&response_type=code&scope=openid&state=<SOME_STRING_HERE>&nonce=<SOME_STRING_HERE

If this is broken down, it makes more sense. The authorization endpoint is at “https://login.microsoftonline.com/<SOME_ID_HERE>/oauth2/authorize”. The query parameters that follow this are the instructions for how to perform the login. Below, we go through them and what they do.

  • client_id: This is the Client ID of the application. This can be found in the IFS MWS Admin Console and also at the external Identity Provider if these are configured. Either the Web or Native client id is used, depending  on the need for either a confidential or a public client.
  • redirect_uri: This is the place where the response is consumed. Upon successful login, it will redirect here. It can be a place on a server, or it can be a URL that redirects to a native application, as detailed above. It is somewhere where the response with the one-use authorization code can be consumed to get an access token.
  • response_type: This should be “code” for Authorization Code Flow.
  • scope: This parameter can vary slightly. See individual Identity Providers for more information.
  • state: The state parameter is used for correlation between requests on the client-side. Generate a random sequence and use that. Some Identity Providers require this.
  • nonce: This is a cryptographic nonce. This should be a random sequence that should be different for every request to avoid replay attacks. 

Other parameters - resource

For a third-party client, a parameter called “resource” is also needed. This makes the access tokens be of a format that can be sent to another server and properly validated. This is usually equal to the web client id configured in the IFS MWS admin console, but in the case of ADFS it is something else (see relevant section for more information). The use of this parameter is a result of the Microsoft implementation of the protocol used in Azure AD and ADFS, and is not detailed in the spec. That makes this parameter the primary source of confusion and issues when attempting this. This parameter is mandatory and would make the example would be modified as follows: 

https://login.microsoftonline.com/<SOME_ID_HERE>/oauth2/authorize?client_id=<SOME_ID_HERE>/&redirect_uri=https://cmbgse1919.corpnet.ifsworld.com:48080/main/ifsapplications/web/oauth2/callback/&response_type=code&scope=openid&state=<SOME_STRING_HERE>&nonce=<SOME_STRING_HERE>&resource=<SAME_AS_CLIENT_ID_HERE> 

A good OAuth2 client library will freely allow for adding extra parameters to the authorization URL. Unfortunately, some implementations of OAuth2 built into graphical applications are not as good which can cause issues.

Other parameters – response_mode

In order to regulate how the response is formatted when returned from the Identity Provider, there is an optional parameter called “response_mode”. The default value is “query”, which puts the one-use access code as a query parameter on the redirection that happens after login. The value “fragment” causes this to instead be put on the fragment part of the URL. The value “form_post” will make a successful login return an HTML page with a form that posts itself to the redirect_uri, rather than redirecting. The values “fragment” and “form_post” are considered better, but query is allowed to be used in flows where the Identity Provider returns one-use authorization codes and not full tokens.

Other parameters – PKCE-related

For some flows there are different parameters that need to be added. The above instructions use the confidential client which requires the client secret to be provided when exchanging the one-use authorization code for a token. If the public client is used, PKCE would be required. This introduces extra parameters which are described in more later in the document.

  • code_challenge: The PKCE code challenge used to verify this later. How to generate it depends on the algorithm used (description to follow later).
  • code_challenge_method: What algorithm is used to generate the challenge. This is usually going to be “S256”. Some Identity Providers may allow the use of “plain but this is not recommended.

Registering redirect URIs

The redirect URI that is used by the application needs to be registered at the Identity Provider, or else the authorization request will be denied. Once it is known where the authorization responses are to be consumed, register the URI(s) in the appropriate place. For the IFS Database Identity Provider, this is in the IFS MWS Admin Console. For other Identity Providers, register the URI(s) in the place where the others of the same type have been registered.

Authorization responses

Depending on the value of response_mode sent to the authorization endpoint (which defaults to “query”), the response will be different. If there is a server associated with the application and the redirect URI is going there, it is often possible to just let it open in the browser which will cause it to be sent to the specified endpoint. If this is done in a in a native client, it likely needs to do some processing. It is therefore good to know the format of these responses. These places describe response modes in greater detail:

Query response mode

The result of a successful login when requesting the response_mode “query” from the authorization server is a redirect towards the redirect_uri with the result in the query parameters. This is only allowed when doing authorization code flow (so in the case a library which uses something like hybrid flow is used, this is not possible to use). The URL the browser gets redirected to looks like below on success, with the relevant parameters in bold.

https://cmbgse1919.corpnet.ifsworld.com:48080/main/ifsapplications/web/oauth2/callback/?code=<SOME_POTENTIALLY_LONG_RANDOM_STRING>&state=<SAME_AS_AUTHORIZATION_REQUEST>

The “code” parameter is the one-use code. The state is the same as you sent up, and can be used to correlate requests.

Fragment response mode

A response with “fragment” response_mode is the same as one with “query” response_mode except that the parameters are supplied as part of the URL fragment. The example above would instead look like this (difference in bold and highlighted):

https://cmbgse1919.corpnet.ifsworld.com:48080/main/ifsapplications/web/oauth2/callback/#code=<SOME_POTENTIALLY_LONG_RANDOM_STRING>&state=<SAME_AS_AUTHORIZATION_REQUEST>

This causes the parameters to not be sent in the query string. There are advantages to this, but a server-side consumer of authorization codes sent this way must respond with a javascript file in order to retrieve them since the fragment is not sent to the server through standard browser logic. For a native app, there is little difference because it would parse it anyway.

Form post response mode

If the response_mode parameter was set to “form_post”, the Identity Provider will respond with an HTML document that contains a self-posting form directed to the redirect_uri. It will not do a redirection, but instead send back a response somewhat akin to what is seen below.

   <html>
       <head><title>Submit This Form</title></head>
       <body onload="javascript:document.forms[0].submit()">
           <form method="post" action="https://cmbgse1919.corpnet.ifsworld.com:48080/main/ifsapplications/web/oauth2/callback">
               <input type="hidden" name="state"
               value="<SAME_AS_AUTHORIZATION_REQUEST>"/>
              <input type="hidden" name="code"
               value="<SOME_POTENTIALLY_LONG_RANDOM_STRING>"/>
            </form>
        </body>
    </html> 

This causes a web browser to automatically post these values to the redirect_uri. That simplifies things in case the application is hosted on a server and ran in the browser, but in the case of a native application this is not recommended since no redirect happens.

Token requests

When the application has in some way retrieved the authorization code, it is time to get the access token. This is done through an HTTP POST request to the token endpoint of the Identity Provider. The authorization code and other parameters are posted in this request. If this is a web application hosted on a server that handles the redirect_uri, the server is likely to do this. In the case of a native client, the client handles it on its own when it gets the result of the authorization request.

 

For a token request to work, the following parameters are needed:

  • grant_type: always set to “authorization_code” when authorization code flow is used.
  • code: The one-use authorization code gotten from the authorization request.
  • redirect_uri: Use the same one as for the authorization request.
  • client_id: Use the same client id used for the authorization request. 

Optionally, these may also be needed:

  • state: Some Identity Providers require state to be supplied on token requests as well. If this is found to be the case, use the same state as for the authorization request.
  • client_secret: If the web client id is used (or the ID of another confidential client), this is needed.
  • code_verifier: paired with the code challenge if PKCE is used. See relevant section in this document. 

When the token endpoint responds, it should contain an access token. 

PKCE and how to do it

PKCE, or “Proof Key for Code Exchange” is a method for ascertaining the integrity of a client that can not securely keep a static client secret for any period of time. The biggest reason for this is that all of the logic executes client-side. Where a server can keep a client secret safe, a native app cannot. The purpose of PKCE is to allow the Authorization Server to ascertain that the client app that requests an access token with an authorization code is the same one that initiated the first request to the authorization endpoint.

The way this works is that the client generates a code verifier before initiating the flow. This is a random string of between 43 and 128 characters. See https://tools.ietf.org/html/rfc7636#section-4.1 for details on how to generate it. The client will then perform a hashing operation on it to generate the code challenge. This is what is sent in the code_challenge parameter to the authorization endpoint. Which hashing method is used is sent to the authorization endpoint in the code_challenge_method parameter at the same time. Different Identity Providers can support different ones, but the most common ones are “plain” and “S256”. If using “plain”, no hashing mechanism is applied at all and the code challenge and code verifier are simply equal. In the case of “S256”, the code challenge is a base64-URL-encoded hash of the code verifier. See https://tools.ietf.org/html/rfc7636#section-4.2 for details.

Some Identity Providers allow “plain” to be used and others disallow it. No matter what, “S256” is the recommended one.

When the token request is then sent to the token endpoint, the code verifier is sent in the code_verifier parameter. The Identity Provider then uses the code challenge method specified when sending the authorization request to transform the code verifier and verify that it matches the code challenge sent before. This serves to verify that the application that sent the authorization request is the same as the one that sent the token request.

Direct grants – for applications that lack a GUI or cannot render a browser

If Authorization Code Flow is not possible due to technical limitations, there is a way to retrieve an access token without the use of a web browser or a web browser control. If done this way, only a request to the token endpoint from the client application happens and this request supplies the needed information to authenticate the user and exchange the credentials for a token.

There are several forms of direct grants that can be found across different Identity Providers that are supported to different extents, but the one that works with all the Identity Providers supported by IFS and also produces tokens that IFS Applications can consume is called “Resource Owner Password Credentials Flow” (henceforth abbreviated “ROPC”). More information can be found at https://tools.ietf.org/html/rfc6749#section-4.3.

Understanding the limitations

There are some known issues with doing OAuth2 in this way and the use of this flow is generally discouraged if other options are available. There are several reasons for this, but it boils down to the following:

  • One major advantage of using OAuth2 is that authentication is delegated to an external service in such a way that the client applications do not handle user credentials. This is seen as a net gain to security, because the user does not need to trust the client application with their password. Using ROPC means the client must handle user credentials.
  •  Another major advantage of using OAuth2 is that the means by which the user authenticates is managed by the Identity Provider. This means that Single Sign On and other functionalities can be used without the explicit need to handle them in the client in many situations (especially if system browsers can be used). Where this becomes a major issue is with Multi-Factor Authentication. Whereas Single Sign On will be bypassed, Multi-Factor Authentication will likely prevent this method from working.

 As such, it is recommended to avoid using this unless it is needed. Do not use it simply because it is sometimes easier.

 ROPC may have to be explicitly enabled at the Identity Provider before it works.

How to do ROPC

In ROPC, the client simply makes an HTTP POST request to the token endpoint that includes the username and password of the user. It works much like the request to the token endpoint described for Authorization Code Flow, but with some differences.

  • Omit the “code” parameter, since there is no authorization code.
  • The grant_type parameter must be set to “password” rather than “code”.
  • Include a parameter called “username”. Include the username of the user there.
  • Include a parameter called “password”. Include the password of the user there.
  • Include the “resource” and “scope” parameters on the token request. Use them as they would be used in the authorization request when they were sent as query parameters.
  • Include the parameter “response_type”. It is a space-separated list that should at least include “token” 
    If a confidential client is used for this, the client secret is required.

Example

Below is an example of ROPC performed using Postman to get a token that can be used with IFS. Client details have been erased, but this request uses a confidential client. If using the public client, the client secret would be omitted.

Finding the endpoints – Discovery documents

One thing that is needed in order to do this is to find the Authorization and Token endpoints. There is a way to do this, because the supported Identity Providers supply a discovery document containing relevant metadata. See https://openid.net/specs/openid-connect-discovery-1_0.html for more information.

 This document is in JSON format and exposes where the endpoints are under the fields “authorization_endpoint” and “token_endpoint”.

 The way to find this is quite straightforward and IFS applications uses this document also for the purpose of validating tokens. The location of this document can always be derived from the configuration parameter “Identity Provider URL” input into the IFS MWS Admin console, as shown below.

 By appending “/.well-known/openid-configuration” to the end of the Identity Provider URL, the discovery document can be found. It can be noted that for IFS Database, this parameter is not shown. In that case, the discovery document exists at the following place:
https://<IFS_SYSTEM_URL>/openid-connect-provider/.well-known/openid-configuration

Identity Provider specifics

IFS Database

The IFS Database Identity Provider implements the parts of the OAuth2 specification that are used by IFS and needed to authenticate to the application server. It leaves out a lot of features that are not used, but when it comes to the parts that are supported it is fully featured as an Authorization Server.

Not implemented features

The instructions given in this document are all functional with the IFS Database Identity Provider. If something else is tried, there are not always any guarantees. Some libraries refuse to work with it. There are as of yet no plans to make a fully featured Authorization Server out of it.

Using ROPC with IFS Database identity provider

As a result of what was mentioned above, the IFS Database Identity Provider was released without ROPC support. This limitation was remedied in MWS BP 10.4.4.0. If an earlier version of IFS Middleware Server is used, please update it to 10.4.4.0 or higher.

New clients can not be registered

As of yet there is no support for registering new clients for the IFS Database Identity Provider. As such, since it is only supposed to be used to connect to IFS applications, it is acceptable to use the ones already created for other apps that fit the profile and need to run OAuth2 flows. If the integrating system can store a client secret securely (this means it has a server component and can do the token request from the server), use the web application. If it can not (meaning it is a native application or similar), use the native one.

Azure AD

The behavior of Azure AD was used as a baseline for the implementation of the protocol when developing the IFS Database Identity Provider. As such, there are not that many differences in behavior when it comes to the parts that are supported by the IFS Database Identity Provider. Nevertheless, there are some places where Azure AD sticks out.

Lifecycle

What needs to be understood about Azure AD in the first place is that it is a cloud service. This means it follows an evergreen release model where customers have little choice in the matter with regards to whether it is updated. Azure AD is always on the current version, which impacts the lifecycle of documentation that covers it. All documentation, including this one that covers Azure AD will be at least partially outdated within the lifetime of Apps 10 installations that use Azure AD.

ADAL

Active Directory Authentication Library, or ADAL, is a library released by Microsoft that works very well with Azure AD. If it is possible to use it, it can save a lot of time when developing an integration that specifically works with Azure AD. ADAL does not work with the IFS Database Identity Provider.

Public and confidential clients

In Azure AD, a client can be both public and confidential at the same time depending on what redirect URI is used. This is the reason why there are cases where the native and web clients in the IFS MWS Admin Console are the same. The resource parameter used is also the same in these cases.

 Not all IFS Applications installations are set up like this. When this feature was introduced to Azure AD, IFS Applications 10 was already released, and some deployments are set up prior to this being introduced.

 Out of the supported Identity Providers, Azure AD is the only one to behave like this.

ADFS 2016

ADFS 2016 has a lot of options available and can be made to differ quite a lot from how the other Identity Providers work. As a result of this, the IFS setup constitutes an attempt to get it into a position where as many things as possible work as similar as possible to the others. There are still a few differences though.

The resource parameter

The biggest difference when it comes to ADFS is the resource parameter. It is still present, but its value is different from what Azure AD expects. The resource parameter follows the pattern of “api://<INSTANCE_NAME>”, replacing <INSTANCE_NAME> with the instance name of the installation chosen during installation.

Using different clients

The way ADFS structures clients is different from how the other Identity Providers work. If the public client is to be used, it is possible to use the native one that IEE uses. In order to use a confidential client, a confidential client in the same application group that also has access to a Web API resource of the same identifier as the resource parameter is needed.