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. |
^ sandboxes created prior to 21 February 2024 should use integrations.lawvu.com |
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 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 |
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 relevant API gateway. For a developer sandbox environment, use |
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>`); })