Authentication

There are 2 types of authentication supported by LOKE. Depending on the use-case you may need to implement both.

  1. Authenticate as your app using client credentials (ID + secret). This is for backend (offline) and behind the scenes requests.
  2. Authenticate on behalf of a user using the classic OAuth authorization code flow. Primarily for web to authenticate a user interacting with a UI.

Each grant will provide you with an access token to access the API:

  1. In the case of client credentials you will have access to organizations that have been installed for your client. A LOKE user or an organisation admin will need to install one of their organizations to your client for you to have access here.
  2. In the case of user (authorization code) credentials, you will beed have access to the organizations that the user can access.

Code examples using a Node js library are provided below.

When To Use

  • Everything happens in UI in response to a user interaction - authorization code.
  • Pure headless/backend, no UI - client credentials (can install for organization independently)
  • Mix of UI and backend - both

Client credential tokens will have acesss to every organization installed for the client. The developer must ensure proper security and segregation of data. Only show organizations that a logged in user has access to by fetching /organizations with that user's token.

Common Use Cases

Configured Done By Account Manager or Admin (Managed)

You can likely just use your client credentials for this. LOKE support can install required organizations to your client so you can access them. Your admin tooling will need some way to allow admins to pick and connect an installed LOKE organisation for your accounts.

Configured by Customer (Self-Service)

You will likely need to use a combination of client and user credentials. Have the user authenticate (approve) to your UI.

Your user-facing UI can then list all the organizations the user has access to, and allow them to select the location to connect.

Once the user has connected the organization, for complete self-service you will likely need to guide them to "install" the organization for your client for offline access.

Alternatively you can just present an intersection of the organizations your client and the user have access to, so someone will first need to install, then the user will be able to connect and configure in your user-facing UI.

OpenID Connect

The current authentication system for LOKE adhers to the OpenID connnect standard. As such you can use any OpenID Connect client to speed up the development process.

OpenID Connect is also just a layer on top of OAuth 2.0, so any standard OAuth client will also work, albeit with some extra manual work.

Access tokens are JWTs with a limited time-to-live (expiry). When this token expires you will need to get a new token. An OpenID Connect client should be able to parse these tokens for you and typically has helpers to check if it is expired.

To use this authentication system you will need the following data:

  • The OpenID Connect Issuer URL: https://auth.loke.global
  • A client ID and secret: to be provided by LOKE on request

Examples

Access Tokens using Client credentials

This is not intended as production ready code. It is only intended to demonstate usage.

import { Issuer, Client, TokenSet } from "openid-client";

const issuer = await Issuer.discover("https://auth.loke.global");

const client: Client = new issuer.Client({
  client_id: YOUR_CLIENT_ID,
  client_secret: YOUR_CLIENT_SECRET,
});

/*
Step 1: when your process starts use getToken() to authenticate once
Step 2: when the token expires re-do step 1

You can check if the token is expired before using it. The expiry is embedded in the JWT.
*/

/** Generate a URL to redirect to */
async function getToken() {
  // Client ID and secret are already initialized in client
  // This tokenSet contains an access token
  const tokenSet = await client.grant({ grant_type: "client_credentials" });
  return tokenSet;
}

Access Tokens via Authorization Codes

If you are new to OAuth then a couple of references to help understand are - this Okta article or this Auth0 article

This is not intended as production ready code. It is only intended to demonstate usage.

import { Issuer, Client, TokenSet, generators } from "openid-client";

const issuer = await Issuer.discover("https://auth.loke.global");

const client: Client = new issuer.Client({
  client_id: YOUR_CLIENT_ID,
  client_secret: YOUR_CLIENT_SECRET,
});

/*
Step 1: when a user tries to log in redirect them to a URL generated with generateAuthorizationUrl()
Step 2: after "approving" the user's browser will be redirected back to your chosen callback URL
Step 3: process the callback request using authorizationCallback()
Step 4: store the returned access token, eg in a cookie or session so that you can use it on future requests
Step 5: when the token expires you will need to redirect the user back to step 1

You can check if the token is expired before using it. The expiry is embedded in the JWT.
*/

/** Generate a URL to redirect to */
function generateAuthorizationUrl() {
  const state: string = randomBytes(20).toString("hex");
  const codeVerifier: string = generators.codeVerifier();
  const codeChallenge: string = generators.codeChallenge(codeVerifier);

  const url: string = client.authorizationUrl({
    redirect_uri: `${MY_BASE_URL}/my/callback`,
    scope: "openid",
    prompt: "consent",
    state,
    code_challenge: codeChallenge,
    code_challenge_method: "S256",
  });
  return { url, state, codeVerifier };
}

/** Process a callback request (after the user has hit approve in LOKE) */
function authorizationCallback(req) {
  const state = req.body.state;
  // Need some way to get this from when we created the URL originally.
  const codeVerifier = getPreviousCodeVerifier(req);
  const params: CallbackParamsType = client.callbackParams(req);
  // This needs to match
  const url = `${MY_BASE_URL}/my/callback`;

  // This tokenSet contains an access token and an ID token.
  // Keep this to use in the session.
  const tokenSet: TokenSet = await client.callback(url, params, {
    state,
    code_verifier: codeVerifier,
  });
}

LOKE Apps

Installing LOKE apps

Anyone can use an installation URL to install your app on their organisation.

https://auth.loke.global/install-client?client=APP_CLIENT_ID

Click install once you have selected the organisation and you will be redirected to the set URL paramater for that app. To change this URL see modify LOKE apps

Modify LOKE apps

After creating a LOKE app, you can make changes to it.

  1. Select organisation from the top navigation

  2. Select the LOKE App you want to modify

  3. You can adjust many settings related to your app including the URL to what the users will be redirected to once they have installed your LOKE App

In this article