Authentication & Authorization in TIM BPM


Authentication (with JWT)

Introduction

What is JWT?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Session Timeout (on user interaction) & Auto-Logout (without user interaction)

What is an access token?

Access tokens are used in token-based authentication to allow an application to access an API. The client application receives an access token after a user successfully authenticates and authorizes access, then passes the access token as a credential when it calls the target API. The signed access token informs the API that the bearer of the token has been authorized to access the API and perform specific actions specified by the scope that was granted during authorization.

What is a refresh token?

A refresh token is a special kind of token used to obtain a renewed access token. You can request new access tokens upon their expiration. Applications must store refresh and access tokens securely because they essentially store authorization and can be used eternally (refresh - access => refresh - access…).

Refresh tokens improve the authentication experience significantly. The user has to authenticate only once, through the web authentication process. Subsequent re-authentication can take place without user interaction, using the refresh token.


TIM’s approach of using JWT

JWT calls

Fast Track documentation
Login Request
  • The endpoint /tim/api/auth/login with HTTP method POST and Body Parameter username + password must be called after following these steps:

    1. the password MUST be base64 encoded (the username must NOT be encoded)

    2. Content-Type: application/x-www-form-urlencoded

    3. POST body must be with variables like username=testuser&password=testpass

As a result of the login request, the server sends a response with 2 generated tokens (accessToken + refreshToken).

Calling an API
  • make sure that you have an access token or send a login request to get it

  • all other requests to API must be sent after following one of these steps:

    • relying on the HttpOnly cookie which is automatically set after login request

    • the Authorization header must be added. It has the format: Bearer AccessToken

Relying on the HttpOnly cookie is a better solution since the attacker will not be able to access the access token.

Refresh Tokens Request
  • if your number of requests is not more than ~1 per hour you may not refresh your token but just do a new auth (depending on your module setup)

  • make sure you have a refresh token or send a login request to get it

  • POST body must be with variables like refreshToken=yourRefreshToken

  • The endpoint /tim/api/auth/refresh with HTTP method POST must be called

As a result of the refresh tokens request, the server sends a response with 2 new generated tokens (accessToken + refreshToken).

Logout Request
  • The endpoint /tim/api/auth/logout must be called

  • the HttpOnly cookie access_token gets deleted from the browser automatically

As a result of the logout request, the user is not able to access the server resources as the HttpOnly cookie is deleted from the client-side. However, since the access token blacklist is not implemented in TIM, the authorization can be still valid until the token's expiration if the user was able to save the access token prior to logout.

What has to be changed for systems calling TIM?

The systems have to follow the next steps:

  • send a login request to receive a pair of tokens (access + refresh).

  • following one of these steps:

    • relying on the HttpOnly cookie which is automatically set after login request
      OR

    • create an Authorization header and place into it the access token, that was received as a response from the server during the login request.

  • send a request to the API.

Each time a request is received from a user or another system, TIM will automatically look for the Authorization header or HttpOnly cookie and validate their token.

Sending requests from the client-side

This part will show you how requests to the server can be made via Javascript, with examples of authentication and authorized requests using the Authorization header.

  1. Create an XMLHttpRequest object and set all required parameters to perform requests to the server, as in the example below:

    let Util = { request: function (method, url, parameters, accessToken, callback) { let xhr = new XMLHttpRequest(); if (method.toUpperCase() === "GET" && parameters != null) url += "?" + parameters; xhr.open(method, url); xhr.responseType = "json"; xhr.setRequestHeader("Accept", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (callback) callback(xhr); } } if (accessToken !== null) { xhr.setRequestHeader("Authorization", "Bearer " + accessToken); } if (method.toUpperCase() === "POST") { xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(parameters); } else { xhr.send(); } return xhr; } };

     

  2. Send login request

    $(document).ready(function () { let accessToken; $("#login-btn").click(function (e) { e.preventDefault(); Util.request("POST", "http://localhost:8280/tim/api/auth/login", "username=" + $('#username').val() + "&password=" + $('#password').val(), null, function (jsonCall) { if (jsonCall.status === 200) { accessToken = jsonCall.response.accessToken; } }); });

     

  3. Send a request to API

    $("#get-tasks-btn").click(function (e) { e.preventDefault(); Util.request("GET", "http://localhost:8280/tim/api/tasks", "status=open&limit=10&offset=0&mygroup=false", accessToken, function (res) { if (res.status === 500) { alert("Please login first."); } else if (res.status === 200) { console.log(res.response); } }); }); });

 

You can find the example of web forms in the attachments below:

  • Webform with 2 XmlHttpRequests (login request and API request):

 

  • Webform with login request (XMLHttpRequest) and API request (AJAX):

 

Calling a web service

There may be a case when some system needs to use the TIM web service. Let's say the system wants to send a request to archive a process instance. To do that, the system should use the GenericEntityManager web service and the archive method located there.

As an example, the SoapUI tool will be used to call a web service method.

Example of a web service method call

As another example, the browser console can be used directly to call the web service. In this case, no configuration is required as the access token was set as an HttpOnly cookie during the login request and TIM will use this cookie to authorize the request.

Example of a web service call from the browser console
Detailed documentation with examples

Firstly, authentication is needed to get access to the server resources. The user has to log in to the system to be authenticated.
In TIM when the user is sending a request to the server to log in, the server sends a response that contains access and refresh tokens and the access token is also placed on the cookie.

An example of getting a JWT token will be shown to understand how the login process works internally. In this example, the postman will be used as a tool to communicate with the server by calling the appropriate endpoints.

After the postman is opened, a few parameters must be filled in to prepare communication with the server.
Before starting communication with the server, a list of the steps must be taken:

  1. Fill request URL
    Firstly, the request URL field must be filled. In this example, the next URL http://localhost:8280/tim/api/auth/login is used where localhost is a hostname (IP address of the host server ) and port (the address of the port on which the host server is listening for requests).

Typically, when a user logs in, the system calls the /tim /api/auth/login endpoint to generate JWT tokens (a pair of access and refresh tokens) and then perform an authentication. Make sure to use the HTTP POST method.

  1. Adding custom header

In Postman when the tab Headers is opened, an additional header has to be added.
As the key of the header, the Content-Type has to be added, as value - application/x-www-form-urlencoded.

  1. Adding a request body

When the tab body is opened, 2 parameters have to be added:

  • 1st parameter: username.
    As the key of the first parameter, the username has to be added.
    As value - actual username, which is usually used to log on to the system.

  • 2nd parameter: password
    As the key of the first parameter, the password has to be added.
    As value - an actual password that is usually used to log on to the system. However, the encoded password has to be added to this field. 

Login request

After all the steps have been completed and all the necessary parameters have been added, a login request to the server can be sent.

When the login request to the server was sent, a response from the server is received as a result which contains 2 tokens (access + refresh).

Moreover, the HttpOnly cookie access_token is automatically set that enable to make any call to the server. The cookie can also be found in the browser.

As an alternative to an HttpOnly cookie, the Authorization header can be added when sending the request. To add the authorization header, the Authorization tab must be opened. In the authorization tab 2 steps should be done:

  1. the Bearer token must be chosen as the type of authorization.

  2. The access token, that was received before, must be added to the Token field.

Refresh tokens request

To refresh both tokens (access + refresh), the request to the server with endpoint /tim/api/auth/refresh and HTTP method POST has to be sent.

In addition, the previously obtained refresh token must be added to the body as a parameter.

  • Key - refreshToken

  • Value - refresh token received during login request.

In this case, a new pair of access and refresh tokens was received. In the case of using the TIM product, the request for tokens renewal is made automatically, which allows the user to remain authenticated in the system for a long time.

Logout Request

Logout requests can be sent to the server using the /tim/api/auth/logout endpoint and HTTP method GET.

When the user logs out, the HttpOnly cookie access_token is deleted and the user will no longer be able to access the server's resources. As can be seen in the screenshot below, the cookie was deleted during the logout request.


Using JWT in REST Connectors

Connectors allow using REST API for connecting to the TIM API. The request must be authorized to call TIM API. This section shows how to configure authentication in connectors to execute authorized requests. The following steps must be taken:

  1. Open connectors designer and navigate to REST connectors

  2. In the authentication section, choose method OAuth and enter the appropriate username and password as you do in the login form.

     

  3. Open the OAuth Headers tab and enter the following values in the parameters:

    • oAuthClientId -> -

    • oAuthClientSecret -> -

    • oAuthTokenUrl -> https://${server}/tim/api/auth/login

  4. Set values for all parameters on the Endpoint Configuration and, if necessary, on the Query Parameters tab.

  5. Navigate to the Testing tab and execute the request.

General Information & FAQ


What are the benefits of using JWT?

  • No Database calls needed: As JWTs are self-contained, all the necessary information is there inside the token, and storage of additional data about issued sessions is not required, all the server has to do is verify the signature. Therefore, it implies fewer database queries and a faster response time. It should also be noted that this advantage is omitted when the deny list of tokens is implemented.

  • Time-Based Tokens: JWTs expire at specific intervals making them more secure.

  • No Session to Manage (stateless): JWT tokens can be saved in cookies or local storage, instead of the traditional approach of creating a session in the server. The best option is to store them in the httpOnly cookie.

  • Portable: A single token can be used with multiple backends (if backends use the same private keys).

  • Digitally signed: Information is verified and trusted. JWTs should be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Use

JWTs can be used in various ways:

  • Authentication: When a user successfully logs in using their credentials, an ID token is returned.

  • Authorization: Once a user is successfully logged in, an application may request to access routes, services, or resources (e.g., APIs) on behalf of that user. To do so, in every request, it must pass an Access Token, which may be in the form of a JWT. Single Sign-on (SSO) widely uses JWT because of the small overhead of the format, and its ability to easily be used across different domains.

  • Information Exchange: JWTs are a good way of securely transmitting information between parties because they can be signed, which means you can be sure that the senders are who they say they are. Additionally, the structure of a JWT allows you to verify that the content has not been tampered with.

Structure of JWT

JWT consists of 3 parts separated by a period.

 

The payload contains the claims. This is displayed as a JSON string, usually containing no more than a dozen fields to keep the JWT compact. This information is typically used by the server to verify that the user has permission to perform the action they are requesting.

There are no mandatory claims for a JWT, but overlapping standards may make claims mandatory. For example, when using JWT as a bearer access token some of the registered claims are present:

  1. "iss" (issuer) claim

  2. "sub"(subject) claim

  3. "aud" (Audience) claim

  4. "exp"(Expiration Time) claim

  5. "nbf" (Not before) claim

  6. "iat" (Issued At) claim

  7. "jit" (JWT ID) claim

The signature ensures that the token hasn’t been altered. The party that creates the JWT signs the header and payload with a secret that is known to both the issuer and receiver, or with a private key known only to the sender. When the token is used, the receiving party verifies that the header and payload match the signature. 

How does JWT work?

In authentication, when the user successfully logs in using their credentials, JWT will be returned. Since tokens are credentials, great care must be taken to prevent security issues. In general, tokens should not be kept longer than required.

Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema. The content of the header should look like the following:

This can be, in certain cases, a stateless authorization mechanism. The server's protected routes will check for a valid JWT in the Authorization header, and if it's present, the user will be allowed to access protected resources. If the JWT contains the necessary data, the need to query the database for certain operations may be reduced, though this may not always be the case.

If the token is sent in the Authorization header, Cross-Origin Resource Sharing (CORS) won't be an issue as it doesn't use cookies.

The following diagram shows the login scenario: