Session Handover

Introduction

When you are accessing a third-party application from Pleo, we generate a JSON Web Token (JWT) token that is passed along with the log in request. The external application validates the token to verify whether the user is authorised to manage the integration. If the token is verified, the integration creates a local session and the user is successfully authenticated to access the application.

Hence, Session Handover is a protocol that defines a single redirect from Pleo application to the external application's pre-configured URL. The redirect request contains a pleo_id_token request parameter that represents an ID Token — a JWT-encoded object containing information about the Pleo user making the request. The application consuming this token uses the information to establish its own user session.

💡

Note: The session handover token is not an access token. It is a token that is used to verify whether the user is authorised to manage the integration. The third-party application must not use the token to access Pleo APIs.

How to Generate a JWT Token?

  1. In the Pleo application, go to Settings.
  2. Select an application that you want to integrate with Pleo.
  3. Click Connect.
  4. Pleo creates and signs a JWT token, containing key information:
    • Company ID
    • User ID
    • Locale
    • Expiry date
  5. Pleo redirects the user to the integration with the JWT token attached in a query parameter (pleo_id_token)
    The integration verifies the JWT token.

💡

Note: Please note that before the token is confirmed valid, no information in the token should be trusted.

Implementation

The standards are defined by OpenID Connect OpenID.Core .

Components of JWT Token

Header Parameters

The header of a JWT ID Token simplifies implementation, mitigates possible token misuse, and token substitution attacks. The header MUST contain the following parameters:

Header ParameterDescription
algCryptographic algorithm used to secure the JWT. MUST be RS256.
typMedia type. MUST be pleo_id+jwt.
kidKey ID. Indicates which key was used to secure the JWT.

Example:

{
  "alg": "RS256",
  "typ": "pleo_id+jwt",
  "kid": "sig-1696245492"
}

Mandatory Claims

The following claims are mandatory and MUST be present in all ID Tokens:

ClaimDescription
issIssuer Identifier. MUST be a URL of an issuer controlled by Pleo.
subSubject. Contains the ID of the End-User represented by the ID token.
audAudience. MUST be the Client ID of the OAuth Client.
expExpiration time as a UNIX timestamp.
iatThe time the token was issued as a UNIX timestamp.

A Client receiving the ID Token MUST validate these claims before using the token, as described in ID Token validation section below.

Example:

{
  "iss": "https://auth.pleo.io",
  "sub": "04fbc415-e5fc-4acc-937c-8964747ad43c",
  "aud": "67e70bba-088d-47c7-a542-e631bb8cca7f",
  "exp": 1696242931,
  "iat": 1696239331
}

End-User Claims

The user's personal information is included in this section:

ClaimRequired/OptionalDescription
subRequiredSubject. The ID of a user as used in Pleo APIs.
nameFull name, including any titles and suffixes.
given_nameGiven name(s) or first names(s).
family_nameFamily name(s) or last names(s).
localeEnd-User's locale, represented as a BCP47 RFC5646 language tag.
{
  "sub": "04fbc415-e5fc-4acc-937c-8964747ad43c",
  "name": "Jeppe Carøe Rindom",
  "given_name": "Jeppe",
  "family_name": "Rindom",
  "locale": "da-DK"
}

Additional Claims

In the urn:pleo namespace, you would the find the additional claims:

ClaimDescription
urn:pleo:companyCompany Information Claim.

Company Information Claim

The Company Information Claim represents a legal entity:

FieldRequired/OptionalDescription
subREQUIREDThe company ID as recorded in Pleo
nameCompany name
addressLegal address of the company

Example:

{
  "urn:pleo:company": {
    "sub": "3f4d3cf9-806f-4f6f-8cb0-94b69d23109e",
    "name": "Pleo Technologies A/S",
    "address": {
      "formatted": "Ravnsborg Tværgade 5 C, 4. Copenhagen N, 2200, Denmark",
      "street_address": "Ravnsborg Tværgade 5 C",
      "locality": "Copenhagen",
      "postal_code": "2200",
      "country": "Denmark"
    }
  }
}

JWT Token validation

A client receiving the JWT Token must validate it, before using any of the claims that the token contains. For reference, see Section 3.1.3.7 ID Token Validation of OpenID.Core. The following algorithm is adapted from Section 3.1.3.7 ID Token Validation of OpenID.Core.

  • JWT Header Parameters:
    • The typ JWT Header Parameter MUST be pleo_id+jwt.
    • The alg value of JWT Header Parameter MUST be RS256.
    • The public key identified by the kid Header Parameter must be present in public key set published in JWKS (see JWKS locations below) format by the Issuer.
    • The client must validate the signature of all ID Tokens as per JWS RFC7515, using the algorithm specified in the JWT alg Header Parameter. The client must use the keys provided by the issuer.
  • JWT Payload:
    • The issuer identifier presented by iss claim must be a URL that belongs to a Pleo-controlled issuer trusted by the client.
    • The client must validate the aud (audience) claim and check that it contains its client_id value registered at the issuer identified by the iss (issuer) claim as an audience.
    • The current time must be the before time, as represented by the exp Claim.
    • The iat claim might be used to reject tokens that were issued a long time back in the past.

📘

Info: When comparing timestamps, some small leeway MAY be allowed to account for clock skew.

During verification, if any of the MUST conditions are non-compliant, the client must reject the JWT Toke, and should not use any of the values represented in its claims.

JWKS location

Example of a full ID Token

Here is an example of a full JWT Token, with line breaks for illustrative purposes only. Also note, the values in this token are for demonstration only:

eyJhbGciOiJSUzI1NiIsInR5cCI6InBsZW9faWQrand0Iiwia2lkIjoic2l
nLTE2OTYyNDU0OTIifQ
.
eyJpc3MiOiJodHRwczovL2F1dGgucGxlby5pbyIsInN1YiI6IjA0ZmJjNDE
1LWU1ZmMtNGFjYy05MzdjLTg5NjQ3NDdhZDQzYyIsImF1ZCI6IjY3ZTcwYm
JhLTA4OGQtNDdjNy1hNTQyLWU2MzFiYjhjY2E3ZiIsImV4cCI6MTY5NjI0M
jkzMSwiaWF0IjoxNjk2MjM5MzMxLCJuYW1lIjoiSmVwcGUgQ2Fyw7hlIFJp
bmRvbSIsImdpdmVuX25hbWUiOiJKZXBwZSIsImZhbWlseV9uYW1lIjoiUml
uZG9tIiwibG9jYWxlIjoiZGEtREsiLCJ1cm46cGxlbzpjb21wYW55Ijp7In
N1YiI6IjNmNGQzY2Y5LTgwNmYtNGY2Zi04Y2IwLTk0YjY5ZDIzMTA5ZSIsI
m5hbWUiOiJQbGVvIFRlY2hub2xvZ2llcyBBL1MiLCJhZGRyZXNzIjp7ImZv
cm1hdHRlZCI6IlJhdm5zYm9yZyBUdsOmcmdhZGUgNSBDLCA0LiBDb3Blbmh
hZ2VuIE4sIDIyMDAsIERlbm1hcmsiLCJzdHJlZXRfYWRkcmVzcyI6IlJhdm
5zYm9yZyBUdsOmcmdhZGUgNSBDIiwibG9jYWxpdHkiOiJDb3BlbmhhZ2VuI
iwicG9zdGFsX2NvZGUiOiIyMjAwIiwiY291bnRyeSI6IkRlbm1hcmsifX19
.
oSk73ScKYTji8SssmlXmLxF2uFFFMYKFs3VWeug1HSk7ilQgWs0N1dask2m
ngVKrZIRPLYLFJnKYH83Ywua52Y63QFjHlTrLytLkvIcXMHEaQYNGEBJJ-6
dM8qBsHULxyUO6lhTDgBzdddgcpX2NmE9iJlw3wajsedatui3uazuAZvbTz
dSjSJIXNzIUCxG18X4pWn6n4GqbAxzfjpLQcAa8G_-nYjf51iK2egGEymG6
WhyvTyf0C0nDH9uWnJCiDDNbncTdlA1_XgrEaUyMVNe0Nt04t9TIffzweYx
U1NQgIm19DSila3ic58mH5WKQbO4Su-UuEhx4Ad3hzfOQqw

📘

Info: Examine this example on jwt.io

JWT Header

{
   "alg": "RS256",
   "typ": "pleo_id+jwt",
   "kid": "sig-1696245492"
}

This represents the same header as was used as an example in the normative section. It uses the public key identified by sig-1696245492 Key ID for signature verification using RS256 algorithm.

JWT Payload

{
   "iss": "fa",
   "sub": "04fbc415-e5fc-4acc-937c-8964747ad43c",
   "aud": "67e70bba-088d-47c7-a542-e631bb8cca7f",
   "exp": 1696242931,
   "iat": 1696239331,
   "name": "Jeppe Carøe Rindom",
   "given_name": "Jeppe",
   "family_name": "Rindom",
   "locale": "da-DK",
   "urn:pleo:company": {
      "sub": "3f4d3cf9-806f-4f6f-8cb0-94b69d23109e",
      "name": "Pleo Technologies A/S",
      "address": {
         "formatted": "Ravnsborg Tværgade 5 C, 4. Copenhagen N, 2200, Denmark",
         "street_address": "Ravnsborg Tværgade 5 C",
         "locality": "Copenhagen",
         "postal_code": "2200",
         "country": "Denmark"
      }
   }
}

In this example:

  • In the sub claim, 04fbc415-e5fc-4acc-937c-8964747ad43c is the ID of a resource representing the user by the name of Jeppe Rindom.
  • In the aud claim, 67e70bba-088d-47c7-a542-e631bb8cca7f is the client_id of a hypothetical client that receives this token.
  • In the sub claim under urn:pleo:company, 3f4d3cf9-806f-4f6f-8cb0-94b69d23109e is the ID of a resource representing Pleo Technologies A/S company.