Authentication guide
Access to the LawVu Developer API is authenticated using the OAuth 2.0 authorization code flow.
The following sections on this page can be used to issue, use and refresh authentication tokens.
Getting an auth code
The authorization code flow begins with the client directing the user to the authorize endpoint:
GET https://{web_host}/#/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}
Parameter | Description |
---|---|
{web_host} | The host name used to serve the interactive login/authorize interface. For a developer sandbox environment, For Production, use |
{client_id} | The client ID which identifies your application or integration. |
{redirect_uri} | This is the URI called when the user clicks “Allow” on the LawVu consent screen. It is called as a GET request. When LawVu calls this URI, the call will include the auth code as a query parameter. Tip: You should URI encode this parameter. |
app.get('/sign-in', async function (req, res) {
const webHost = process.env.webHost;
const redirectUri = buildRedirectUri(req); // E.g. https://acme-inc.com/auth
let authorizeUri = `https://${webHost}/#/authorize`
authorizeUri += '?response_type=code';
authorizeUri += `&client_id=${process.env.clientId}`; // e.g. acme-inc
authorizeUri += `&redirect_uri=${redirectUri}`;
// Return sign in link
res.send(`<a href="${authorizeUri}">Sign in to LawVu</a>`);
});
function buildRedirectUri(req) {
// Get current host. E.g. https://acme-inc.com/
const hostUrl = `${req.protocol}://${req.get('host')}`;
// Build redirect_url. E.g. https://acme-inc.com/auth
// Encode to avoid conflicts with special characters
const encodedUri = encodeURI(`${hostUrl}/auth`);
return encodedUri;
}
Once the user is redirected to the Sign in link, they will see a login screen (will be authenticated via SSO) followed by a standard, one-time consent screen:
Subsequent calls to the API will be made under the security context of this user account, therefore the users’ access level/permissions will need to be considered in the context of the integration workflows.
Getting an access and refresh token
At this point, the redirect URI specified in the previous section is called, along with a short-lived “code” query parameter (also known as the auth code).
This auth code can be redeemed for access and refresh token pair using the token endpoint.
The resulting access token can be used to perform an authenticated API call, and the refresh token can be used to refresh tokens.
POST https://{api_host}/account-apis/v1/auth/token
{api_host} | The host name of the API gateway.
|
Parameter | Description |
---|---|
Supplied in the body – application/x-www-form-urlencoded | |
{code} | Use the auth code supplied in the “Getting an auth code” step. |
{grant_type} | Always set to authorization_code . |
{client_id} | The client ID which identifies your application or integration. |
{client_secret} | The client secret corresponding to your application or integration. |
{redirect_uri} | Needs to match the value used as a redirect_uri in the Getting an auth code step. |
app.get('/auth', async function (req, res) {
// Build request to exchange auth code for an access token
const authCode = req.query.code;
const redirectUri = buildRedirectUri(req);
const params = new URLSearchParams();
params.append('code', authCode);
params.append('grant_type', 'authorization_code');
params.append('client_id', process.env.clientId); // e.g. acme-inc
params.append('redirect_uri', redirectUri); // matches the redirect URI specified in the previous step
params.append('client_secret', process.env.clientSecret); // never hard-code sensitive information
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
// Call LawVu Auth token API
const apiHost = process.env.apiHost;
const tokenUrl = `https://${apiHost}/account-apis/v1/auth/token`;
const response = await axios.post(tokenUrl, params, config);
// Store tokens for later use
// For security best practice, tokens should be encrypted before storing
await encryptAndPersistTokens({
accessToken: response.data.access_token,
refreshToken: response.data.refresh_token,
expiresUtc: response.data.expires,
gateway: response.data.gateway
});
// Send success response
res.send('<h1>Signed in to LawVu!</h1>');
})
The resulting access token can be used to perform an authenticated API call, and the refresh token can be used to refresh tokens.
Response Property | Description |
---|---|
{access_token} | An access token which can be used to perform authenticated API calls. Note: LawVu API access tokens are Base 64-encoded JWT tokens, which include claims containing useful information about the authenticated user. See the Decoding access tokens section for more information. |
{refresh_token} | Token that can be used to exchange for a new access_token and refresh_token before or after the previous access_token is expired. |
{expires_in} | Seconds until the access token is no longer valid. Also known as “time to live”. |
{expires} | UTC timestamp that indicates from when the newly obtained access_token is no longer going to be valid for making authorized requests. |
{gateway} | A base URI intended to be used for authenticated API calls. Note: if any other public API gateway is used, http requests will result in 401 http responses, even if the access token is valid. |
Performing an authenticated API call
All calls to API endpoints must be authenticated using an access token.
To do this, the access token retrieved from the previous section must be provided as a “bearer token” in the Authorization HTTP header.
app.get('/user-list', async function (req, res) {
// Retrieve a valid access from storage, refreshing if expired
const accessToken = await getValidAccessToken(req, res);
// Build authorized request
const config = {
headers: {
'Authorization': `Bearer ${accessToken}`
}
};
const apiHost = process.env.apiHost;
const lawVuEndpoint = `${apiHost}/account-apis/v1/users`;
const response = await axios.get(lawVuEndpoint, config);
const users = response.data
.map(user => `<li>${user.firstName} ${user.lastName}</li>`)
.join('');
res.send(`<ul>${users}</ul>`);
})
Refreshing tokens
Token rotation is defined by the OAuth V2 RFC spec.
Instead of having a permanent access token to make authenticated calls to LawVu, the access token expires. One benefit is that if an access token is stolen, it will only be valid for a relatively short time frame. A refresh token provides a means of refreshing your access tokens without having to ask your users to re-authenticate each time their access token is expired.
To get a fresh access token, you can exchange an existing refresh token for new access and refresh tokens.
Refresh tokens are single use and cannot be used multiple times. If a refresh token is provided that has already been used, you will receive an HTTP 400 Bad Request.
POST https://{api_host}/account-apis/v1/auth/token
{api_host} | The host name of the API gateway.
|
Parameter | Description |
---|---|
Supplied in the body – application/x-www-form-urlencoded | |
{refresh_token} | Use the previously issued refresh_token . |
{grant_type} | Always set to refresh_token . |
{client_id} | The client id which identifies your application or integration. |
{client_secret} | The client secret that was provided during the Getting an access and refresh token section. If client secret was not provided during Getting an access and refresh token then this parameter can be omitted. |
Refresh tokens eventually expire as well. If you receive an HTTP 400 Bad Request response from LawVu when trying to refresh a token, this means the user has been idle for too long. This means the user will have to authenticate to LawVu once again. Consider redirecting the user to the start of the process (per the Getting an auth code section).
/**
* Retrieve a valid access from storage, refreshing if expired
* @returns string
*/
async function getValidAccessToken() {
// Retrieve decrypted token from secure storage
const savedTokens = await getDecryptedTokens();
const now = getUtcNowDate();
const accessTokenExpiryDate = new Date(savedTokens.expiresUtc);
const isTokenExpired = accessTokenExpiryDate < now;
if (!isTokenExpired) {
return savedTokens.accessToken;
}
// Refresh token request payload
const params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('refresh_token', savedTokens.refreshToken);
params.append('client_id', process.env.clientId); // e.g. acme-inc
// Headers
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
// Refresh token request
const apiHost = process.env.apiHost;
const tokenUrl = `https://${apiHost}/account-apis/v1/auth/token`;
const response = await axios.post(tokenUrl, params, config);
// Store updated tokens and expiry timestamp
// For security best practice, tokens should be encrypted before storing
await encryptAndPersistTokens({
accessToken: response.data.access_token,
refreshToken: response.data.refresh_token,
expiresUtc: response.data.expires,
gateway: response.data.gateway
});
return response.data.access_token;
}
Decoding access tokens
The access tokens returned by the LawVu API are Base64-encoded JSON Web Tokens (JWT).
Once decoded, you can pull useful information from the “claims” stored within such as the authenticated user’s ID, name, permissions and Organization ID.
In the following sample, we demonstrate the retrieval of the authenticated user’s Organization ID using the jwt-decode library:
app.get('/users/whats-my-org-id', async function (req, res) {
// Retrieve a valid access from storage, refreshing if expired
const accessToken = await getValidAccessToken(req, res);
const decodedToken = jwt_decode(accessToken);
res.send(`<h1>Your org id: ${decodedToken.organisationId}</h1>`);
})
Handling errors
Here is a list of some common errors and how you might resolve them.
Getting an authorization code
Required parameter missing from request url: redirect_uri
- Ensure you have included a query parameter “redirect_uri={your_redirect_uri}” in the url
Required parameter missing from request url: client_id
- Ensure you have included a query parameter “client_id={your_client_id}” in the url
Supplied parameter does not match a whitelisted value: redirect_uri
- Ensure redirect_uri passed in the url matches the redirect_uri that was provided to LawVu on client registration
Getting an access token
Required parameter missing from request body: grant_type
- Ensure that you have included a grant_type parameter in the body of your request.
Required parameter missing from request body: client_id
- Ensure that you have included a client_id parameter in the body of your request.
Required parameter missing from request body: code
- Ensure that you have included a code parameter in the body of your request.
Supplied authorization_code is not valid or has expired
- The code provided is invalid. Please ensure that the code is correct, has not already been used, and the client_id is the client the code was issued for
Supplied redirect URI doesn't match the one used for authorize endpoint ({redirect_uri})
- Ensure the redirect_uri provided matches the redirect_uri that was used to generate the authorization_code
Client is not valid: "{your_client_id}"
- The client_id value supplied in your request is not a registered client in the system, or is a disabled client in the system
Supplied parameter is not correct: client_secret
- The client_secret value is not the correct secret for the provided client_id. Please ensure that you are using the secret provided by LawVu
Refresh token is not valid
- The refresh_token provided is invalid. Please ensure that refresh_token is correct, has not already been used, and the client_id is the client the refresh_token was issued for
Supplied refresh_token expired at '{utc_time}'
- The refresh_token provided has expired. Please request an access token again.