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.

^ sandboxes created prior to 21 February 2024 should use integrations.lawvu.com
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 relevant API gateway.

For a developer sandbox environment, use demo.public-api.lawvu.com ^

^ sandboxes created prior to 21 February 2024 should use integrations.public-api.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 relevant API gateway.

For a developer sandbox environment, use demo.public-api.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>`);
})