Programmers Guide

How to use the PRIS Token Security API

This guide is targeted at anyone (external clients) who may be developing a PRIS consuming application, and wishes to use token-based security.

PRIS acts as both:

  • Authorisation Server (via the v1/system/security/token endpoint as explained below)
  • Resource Owner (it contains the routes that then consume the security token it has previously created)

Although the pattern of OAuth2 allows for external authorisation servers (common examples are Facebook, Google etc), we do not currently support this, and PRIS itself looks after the token creation. PRIS currently obtains authentication and authorisation information from the Pitram Portal (via the existing user/role management module).

IIS Bindings

If your endpoint will be exposed outside of your firewall, you MUST provide and require the use of SSL. Even if this is within your firewall, SSL is still recommended, for the extra security (especially when getting the initial token retrieval via a username/password), and also, most likely, the added benefit of the higher-performance http/2 protocol (if both client app and server support it).

In the IIS Manager:

Once you have done this, for the PRIS application, select SSL Settings,

and then Require SSL.

If you do not do this, any data between a client application and PRIS is unencrypted. This data, including the username/password used when obtaining a token, can be read by anyone. If you do not force the SSL, the HTTPS binding will still allow access via http.

If you do not force the SSL, the HTTPS binding will still allow access via http.

PRIS Configuration for Token based security

To enable PRIS to use token-based security:

  1. In config/appsettings set the following:
  2. <!- the security type must be set to token  -->
    <add key="SecurityType" value="token" />
    
    <!- create and edit a security token  -->
    <add key="AudienceSecret" value="" />
    

  3. In web.config set the following:
  4. <!- You can either set this to false, or just remove the whole block so we don't need to install the Windows Authentication feature -->
    <security>
        <authentication>
          <windowsAuthentication enabled="false" />
        </authentication>
     </security>

Add a Role and User to use in PRIS (via Pitram Portal)

Add a new application to add the user role again:

Now add this role against either an existing user or a new user:

The above username/password is now what we pass to PRIS. The client_id we pass in the token endpoint will be the application name we just added.

Obtaining a Security Token

Once PRIS is configured for the (OAuth2) token-based security, it will expose the following route. For more information, do a search for OAuth2, there are many posts related to this pattern.

v1/system/security/token

An application will use this route to obtain a security token, formatted as a Json Web Token (JWT). It is this token that is included for every call to a PRIS route. The token will contain all the information PRIS requires to determine whether or not a user has permissions for the route.

Steps to use the Use PRIS token

  1. The initial token is obtained using a username/password
  2. The application stores this token, and sends with every request to PRIS
  3. The application also stores the refresh token. Since the security token may have a short expiry time, the application can use the refresh token to obtain a new security token.
  4. Before each request, the application should check the expiry of the JWT security token. If it is near expiring, then the application should request a new token.
  5. It can do this by either using a username/password (as in the initial request), or by using a refresh token (the preferred option, so that the application does not need to store sensitive username/password information).

    JWT is a standard format, so it should be easy for an application to decode, examine the expiry date (as well as other information, such as roles, contained within the token)

Obtaining a new security token using a username/password

Verb: POST

URI: v1/system/security/token

Post payload (body)

Parameter Name Example Value
grant_type password (must always be password)
client_id pitramconnect
username benny
password bennys password
device_id mm00851

The body should be in the format application/x-www-form-urlencoded.

Example JavaScript code

let headers = new HttpHeaders();         
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
let body = `grant_type=password&username=myusername&password=my_password&client_id=$pitram%20connect&device_id=My%20PC`;
let url = `https://192.111.2.3/pris/v1/system/security/token`;
this.http.post(url, body, { headers: headers });

Note: Any special characters (eg space = %20) should be url encoded.

Example response

{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTUxMiJ9.eyJ1bmlxdWVfbmFtZSI6ImJlbm55Iiwicm9sZSI6WyJ1c2VyIiwiYmxhaCBibGFoIl0sInJlc291cmNlL2VxdWlwbWVudCI6InJlYWQiLCJlcXVpcG1lbnQiOiJyZWFkIiwic3lzdGVtIjoicmVhZCIsImlzcyI6IlBSSVMiLCJhdWQiOiJQUklTIiwiZXhwIjoxNDg0MTkwNTc0LCJuYmYiOjE0ODQxOTA0NTR9.MSbpxPgLqP8QTYzFBzloUiMtagN_s74pT7UV1JsU9n5pn1dFPmS5VZGdZX96k9e83Fo5wtnw7bJJL5EbqRdEHQ",
"token_type": "bearer",
"expires_in": 119,
"refresh_token": "6bf6bd11e5fd428a8d4f6c026bcf79e2",
"ClientId": "pitramconnect",
"userName": "benny",
".issued": "Thu, 12 Jan 2017 03:07:34 GMT",
".expires": "Thu, 12 Jan 2017 03:09:34 GMT"
}

Notes

  • In production, the token SHOULD be over https as a precaution, because the password is in plain text
  • The client_id field may be confusing for us, because we use the term client id for Pitram connections. client_id is also a standard term in Oauth2 token-based security, and is used to identify a client application.
  • For more information about OAuth2 roles, follow the link here.
  • The access_token holds the JWT. This has all our claims encoded and is supplied in the header of every request to PRIS

Obtaining a security token using a refresh token

Part of the response for the above token issue is the field refresh_token.

The idea is to make the JWT (above) short-lived (say 5 minutes or so), so that if it is ever compromised, it will expire anyway in 5 minutes.

Of course, we don't want a user to have to resupply the username/password every 5 minutes, and we don't want the client application (such as an insecure mobile app) to have to store this information (the whole purpose of the token is that the username/password need only be supplied once, and we then pass around the token.)

Once the main token expires, the client app will receive a 401, because the token is no longer valid. At this stage they can use the supplied refresh token to get a new JWT.

Below is how we do this:

Verb: POST

URI: v1/system/security/token

Post payload (body)

Parameter Name Example Value
grant_type refresh_token (must always be refresh_token)
refresh_token the refresh token supplied in the JWT
client_id pitramconnect

Notice the grant type is now refresh_token. This is how PRIS will know we want to get a new JWT using a refresh token as opposed to a username/password.

The new JWT may contain new claims. When PRIS receives a refresh token request, it will take the opportunity to contact the Pitram Portal to see if there has been either:

  • Claim updates, or
  • a password change

If there are claim updates, then these will be included in the JWT.

If there is a password change, the refresh token will fail, and the client app will receive a 401 to force the user to re-authenticate (re-enter a username/password). If PRIS cannot connect to the Pitram Portal to get updates information, it will use the existing set of claims and still issue a new JWT.

How to use the JWT

The JWT is passed in the header of every request as follows:

Header Name Value
Authorization Bearer <JWT>

We always need the word Bearer, followed by a space, and then the JWT.

Security Related Configuration

Several new settings reside in the file: appSettings.config

Name Description Typical value
SecurityType Use to determine whether we either Windows, Token, or no authentication (none | windows | token) token
AudienceId When creating a JWT this describes who this is intended for. PRIS will only accept a JWT that has this matching audience encoded in it. Can leave as PRIS, but for extra security can changed per site (eg perhaps PRIS-anglogold etc etc) PRIS
AudienceSecret Private key used to sign the JWT. This MUST be different per site and MUST be kept secret. MUST be exactly 32 characters 0an8L31LRCWcH91811R0ntPfuu7278XO which is then encoded to base 64.
AccessTokenExpiryMins How long in minutes a JWT is valid for 5
RefreshTokenExpiryDays How long a refresh token remains valid (in days). This means a user has this many days to use the app before this refresh token expires. This is rolling so when a new refresh token is created, the time is reset 256

Mapping Routes to Claims

In the config folder, PRIS now has the file: identityModel.config.

A number of routes, such as the following, are stored In this file:

<policy resource="system" action="get">
      <or>
        <claim claimType="system" claimValue="ReadAccess" />
        <claim claimType="system" claimValue="FullAccess" />
      </or>
</policy>

We can use either `<and>` or `<or>` as shown above to combine up to two claims. Note a current restriction is we can have a maximum of two.

There are two options here to identify a route. The above applies for all routes within the resource group, for example, system (above). The claim type and the claimValue are the values as configured in the Roles screen in Pitram Portal.

Also, the whole route can be included, and will override any configuration that is done at the resource level, for example:

<policy resource="v1/system/logging/remotelog" action="post">
      <claim claimType="*" claimValue="*" />
</policy>