Last updated

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}
ParameterDescription

{web_host}

The host name used to serve the interactive login/authorize interface.

For a developer sandbox environment, use demo.lawvu.com

For Production, use go.lawvu.com

{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.

Sample – constructing URL for signing in and retrieving auth code:
 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:

Images

Images

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.
  • Production: api.lawvu.com
  • Sandbox: api-sandbox.lawvu.com
ParameterDescription
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.
Sample – exchanging auth code for access and refresh tokens
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.

Sample – performing an authenticated API call:
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.
  • Production: api.lawvu.com
  • Sandbox: api-sandbox.lawvu.com
ParameterDescription
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).

Sample – refreshing tokens:
/**
* 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:

Sample – decoding an access token:
 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.