Authentication
OAuth 2
Authentication is handled via SALUS Guardian, the Identity Provider for all SALUS API services. The Developer API 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, so a given set of credentials is isolated to a single environment.
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, such as 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": "<<current_client_id>>",
"client_secret": "<<current_client_secret>>",
"scope": "sls:idn"
}'
curl -X POST --location '<<token_url>>' \
--header 'Content-Type: application/json' \
--user '<<current_client_id>>:<<current_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- the bearer token that is required with every request. This is a JWT token and contains basic user detailsrefresh_token- the refresh token that can be used to acquire anotheraccess_tokentoken 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 '<<current_client_id>>:<<current_client_secret>>' \
--data '{
"grant_type": "refresh_token",
"refresh_token": "NEW_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. Confidential clients are clients that can be protected, such as REST APIs that are hosted and secured internally or other components of this type.
Note: public clients also use PKCE 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:
- Client creates a
code_verifier - Client creates a
code_challengeusingS256method -base64_encode(sha256(ascii(code_verifier))) - Client send
code_challengewith authorization request, as in the example above - Server response with the
authorization_code. Note: the server will never respond with thecode_challengehere - Client sends
code_verifierandauthorization_codeto thetokenendpoint - Server verifies that the
base64_encode(sha256(ascii(code_verifier)))is equal to thecode_challengepreviously received; if so then it returns theaccess_tokenandrefresh_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 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
}
Updated 17 days ago
