Authentication

OAuth 2

Authentication is handled via SALUS Guardian, the Identity provider for all SALUS API services. The Developer API thus
expects a valid JWT token to be present with every request, and validates that the token was generated by the Guardian
IdP. Each environment has its own Guardian IdP instance; in this way, applications can clearly target distinct
environments in isolation.

The Guardian IdP supports the following authentication flows:

  • client credential grant - for confidential clients
  • authorization code grant (with PKCE) - both for public and confidential clients

Client Credentials Grant

The client credential grant flow is designed to facilitate integration with non-interactive system-level applications. A good example of this would be an integration service. As such, the client_id and client_secret grant the bearer fairly unrestricted access, usually at the company_owner level. This ensures the service will operate on all required data.

To generate the bearer token, use the client_credentials authorization grant protocol flow against the Guardian IdP.

The authentication credentials can be passed using the JSON body or using a HTTP Basic Auth header.

curl -X POST --location '<<token_url>>' \
--header 'Content-Type: application/json' \
--data '{
    "grant_type": "client_credentials",
    "client_id": "<<CLIENT_ID>>",
    "client_secret": "<<CLIENT_SECRET>>",
    "scope": "sls:idn"
}'
curl -X POST --location '<<token_url>>' \
--header 'Content-Type: application/json' \
--user '<<CLIENT_ID>>:<<CLIENT_SECRET>>' \
--data '{
    "grant_type": "client_credentials",
    "scope": "sls:idn"

If the authentication is successful, as per the OAuth 2.1 RFC spec, the response is a JWT access token along with a JWT refresh token.

Below is an example of a response:

{
  "access_token": "ACCESS_TOKEN_HERE",
  "refresh_token": "REFRESH_TOKEN_HERE",
  "token_type": "bearer",
  "expires_in": 3600
}
  • access_token - is the bearer token that is required with every request. This is a JWT token and contains basic user details
  • refresh_token - is the refresh token that can be used to acquire another access_token token via https://guardian.beta.salussafety.io/token

An example of exchanging a refresh_token for an access_token is as follows:

curl -X POST --location '<<token_url>>' \
--header 'Content-Type: application/json' \
--user '<<client_id>>:<<client_secret>>' \
--data '{
    "grant_type": "refresh_token",
    "refresh_token": "REFRESH_TOKEN_HERE"
}'

A successful response from the refresh token request is identical to the access token response.

Authorization Code Grant

The authorization code grant protocol flow allows a specific user to be logged in against a client ID. This flow is useful if the Developer API is used for an interactive application that needs to enrich data in real time from the SALUS Platform. There are two types of clients that are supported: public clients, and confidential clients. Public clients are suitable for usage within single page apps, mobile apps, desktop apps, or other applications that can't be trusted to keep a secret. While confidential clients are clients that can be protected, for example REST APIs that are hosted and secured internally, or other components of this type.

Note: that public clients also use PKCE (Proof Key of Code Exchange) where another piece of data is used to verify that the caller's data was not tampered with before arriving at the Guardian IdP.

Further information about PKCE can be found via the protocol section of the RFC specification for PKCE. Short summary is as follows:

  1. Client creates a code_verifier - i_am_secret_with_good_entropy
  2. Client creates a code_challenge using S256 method - base64_encode(sha256(ascii(code_verifier)))
  3. Client send code_challenge with authorization request, as in the example above
  4. Server response with the authorization_code note, the server will never respond with the code_challenge here
  5. Client sends code_verifier and authorization_code to the token endpoint
  6. Server verifies that the base64_encode(sha256(ascii(code_verifier))) is equal to the code_challenge previously received; if true return the access_token and refresh_token; otherwise returns an error.

An example of an authorize request:

curl -X GET --get 'https://guardian.dev.salussafety.io/authorize' \
  --data-urlencode 'client_id=client_333' \
  --data-urlencode 'request_scope=idtoken+email' \
  --data-urlencode 'redirect_uri=https://your-redirect-target.com/auth/callback' \
  --data-urlencode 'code_challenge_method=S256' \
  --data-urlencode 'code_challenge=B6VhlSf4UnqrkHFTVRAPHFD-7iPizqXA-vY3EW4-e-E' \
  --data-urlencode 'response_type=code' \
  --data-urlencode 'state=17bdc69a-cd4b-46fd-b7de-2b91a8c7f242' \
  --data-urlencode 'scope=sls:idn' \
  --header 'Accept: application/json'

Things of note above are the code_challenge_method if omitted it will be defaulted to S256. The code_challenge which is the hashed value that the IdP will store for later when the token endpoint is invoked. Finally, the scope which is a custom scope that lets the IdP know that you would like to encode the users identity in the JWT token.

The Guardian IdP will respond with a 302 redirect, with a GET request to the specified redirect_uri when that URI is part of the allowed redirect URIs configured in the IdP for the client specified.

And example of what the IdP will do below:

GET https://your-redirect-target.com/auth/callback
    ?state=966ae383-e5a1-49c0-bb30-b5cf554a6726
    &session_state=966ae383-e5a1-49c0-bb30-b5cf554a6726
    &code=cpvsWNeMOqEfmbznQL6UB_qgL9qCtvWiOzg63J7E3WmoRJ3OSn49NIyMp_DITFZC443Ln8J_ZnQoGKFpWpHNKcEAEpPm9t-4UTUgYg3SCPbvdXaSrLfUMX-LkcXSENzaXgBFQ-iC1wvqXUz4keHdSSHo776A-9tSl8NCgb2z
    &scope=idtoken+email

Note: newlines and whitespace are added for readability here.

Then the client callback endpoint will need to make a request to the token endpoint with the code specified, along with the original code_verifier that was used to generate the code_challenge in the authorize request.

An example token request with the code_verifier and associated code:

curl -X POST --location '<<token_url>>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "grant_type": "authorization_code",
    "code": "CODE HERE",
    "code_verifier": "i_am_secret_with_good_entropy",
    "scope": "sls:idn"
}'

Example token endpoint response given a valid authorization code and a valid verifier string.

{
  "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkFubU...nzEiEHoIWTB8VaVbg-v4lg",
  "refresh_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkFu..._LmXzp-Ji8qCFbP0GE5IP8Y",
  "token_type": "bearer",
  "expires_in": 86400
}