Okay LogoOkay Logo

How to Setup Two Factor Authentication with Okay

11/05/2020

artifact

Though a cloud-based multi-tenant solution, Okay provides a simple way to integrate a secure two-factor authentication (2FA) in your app without the need to rewrite or build a security system yourself. This tutorial acts as your step by step guide on how to add 2FA to your service using the Okay solution.

Setup Requirements

Configuring the demo project

This demo project is just a simple Node.js web server written in Typescript that simply provides basic endpoints for user registration and login with 2FA enabled using Okay’s secure application. You will need to checkout to branch "okay-2fa"

First. we need to set some environment variables in our project. You can find the "development.env" file inside "/app/env" folder then specify these environment variables. Replace the TENANT_ID with your tenant id and SECRET with the secret token you provided for your tenant in your dashboard. More information about how to get your tenant id and secret can be found here.

Environment Variables:

# Environment

NODE_ENV=development

# Server

PORT=3000

HOST=localhost

# DB

DB_NAME=dummydatastore.json

DATA_SOURCE_IN_USE=filebasedb

#DATA_SOURCE_IN_USE=lokijs

# Okay Env Variables

PSS_BASE_URL = "https://demostand.okaythis.com"

TENANT_ID=your_tenant_id

SECRET=yoursupersecuretoke

You will need to install all dependencies by running "yarn install". If the dependencies were installed successfully, you can start the server by running the "yarn start" command on your terminal.

We will be using a file-based database as our preferred datastore (we also have an optional in-memory LokiJs database which can be selected in our env files as well) to store user data after registration. Our server application uses the following model for every user.

//User Model

{

"userExternalId": "357437875835-357437875835",

"name": "A.M Turing",

"email": "am@turing.com",

"id": 357437875835

}


The server accepts just the "email" and "name" as JSON payload to the server during registration and auto-generates the remaining fields. We use  "userExternalId"  as a Unique Identifier for every end-user, in order to differentiate all our users when interacting with Okay servers. The userExternalId can be generated by any means you deem fit. As you can see in our example, it is just a repetition of the existing id separated by a hyphen.

Create a new user on our server

Now you can proceed to create a new user on your local instance of the server.
You can do this by sending the following JSON payload to this endpoint localhost:3000/api/users/register

Our Request Payload:

{

"user": {

"name": "Iverson James",

"email": "james@iverson.com"

}

}

Okay provides three basic forms of authorization: a single click authorization, pin authorization and biometric authorization. We will be exploring how to utilize all three forms of authorization in our server to support 2FA.

User Linking

Okay requires every tenant to be linked on the server-side, this is to ensure that all unique users are properly identified and authorized. This is where the userExternalId that we generated beforehand comes into use. When a user has registered on our app, We will need to send a request to Okay server passing in the userExternalId as a field in the body of the payload to Okay. For more information on Okay request body please check out this linking guide.

We will send a POST request with the JSON body below to Okay servers at this endpoint:https://demostand.okaythis.com/gateway/link

// JSON payload

{

"tenantId": "<your tenant id>",

"userExternalId": "User Unique Identifier",

"signature": "BASE64[SHA256(tenantId | userExternalId | secret)]"

}

The signature key in our payload above is a hash that is generated from concatenating your tenantId + userExternalId + secret (also known as the Token you added to your tenant), then passing the concatenated string as a value to SHA256() algorithm. We encode whatever value or string we get from the SHA256() algorithm in BASE64.

# code sample for signature generation

import crypto from 'crypto'

export function createHashSignature(hashStr: string) {

return crypto

.createHash('sha256')

.update(hashStr)

.digest('base64')

}

Linking Endpoint

Make a POST request to this endpoint: 
localhost:3000/api/link/673581707264-335420534864

If the request was made successfully, the response returned contains a linking code we must extract to finish our linking.

If you haven't installed the Okay mobile app from Play Store or App Store yet, this is a good time to download the app to continue with further integration. If you have the mobile app installed on your device, enter the linkingCode found in your response on your Okay app running on your mobile device (for a more thorough guide on how to use Okay application see this guide on linking with the app).

If your linking was successful, we can now proceed to authorize login requests with an extra 2FA from our backend for the linked user. 


Authorizing Login Requests with Okay

In our server application, we have Okay2FAService which implements all three authorization types that Okay provides. This service can be found in the /app/src/services/auth directory. This service simply handles sending POST requests to the Okay server with the following JSON payload to this endpoint https://demostand.okaythis.com/gateway/auth.

{

"tenantId": "<your tenant id>",

"userExternalId": "User Unique Identifier",

"type": "<Authorization type>",

"authParams": {

"guiText": "message that is shown in the Okay application",

"guiHeader": "header of the message that is shown in the Okay application"

},

"signature": "BASE64[SHA256(tenantId | userExternalId | guiHeader | guiText | type | secret)]"

}

The request body contains two new fields  "type" and "authParams". The "type" field allows us to specify what authorization type we would like to trigger. All supported authorization types can be found in the following file /app/src/services/auth/OkayAuthTypes.ts  in our demo project. The  "authParams"  field contains details about the current request/transaction, in this case, a login request that needs authorization. To customize your  "authParams"  field you can change the values in the OKAY_2FA_GUI_HEADER, OKAY_2FA_GUI_TEXT  fields in the demo project in this file /app/src/shared/constants.ts.

Auth Endpoints

Make a POST request to these endpoints to test all support auth types:
localhost:3000/api/auth/2fa/biometric?userExternalId=673581707264-335420534864 (biometric authorization)
localhost:3000/api/auth/2fa/pin?userExternalId=673581707264-335420534864 (pin authorization)
localhost:3000/api/auth/2fa/simple?userExternalId=673581707264-335420534864 (simple button authorization)

If the request was sent successfully (in this case simple button authorization) you should see a screen like the image below, on your Okay mobile application.

The Okay solution provides two ways to get you notified about a user's action. Okay allows a tenant to register a callback endpoint, which it uses to send a POST request with details about a particular transaction. For more information on this please check this documentation on callbacks.

Okay provides an API that allows us to check the status of a particular transaction (or session). We will send our "sessionExternalId" as a unique identifier to Okay, in order to retrieve the details for the transaction we are interested in. In our demo application we implemented this functionality in our Okay2FAService class as getAuthSessionStatus() method. This method simply sends a POST request to Okay server with the following body shown below, to this endpoint https://demostand.okaythis.com/gateway/check.

// JSON payload

{

"tenantId": "<your tenant id>",

"sessionExternalId": "User Unique Identifier",

"signature": "BASE64[SHA256(tenantId | userExternalId | secret)]"

}

Check Session Status Endpoint

Make a GET request to this endpoint to test check status as described above: 
localhost:3000/api/auth/2fa/status/<sessionExternalId> (replace <sessionExternalId> with an actual sessionExternalId from previous auth request to Okay server)

To be sure that everything is working correctly, try sending a login request to the demo server at this route localhost:3000/api/auth/user/login. You should receive a push notification on your Okay mobile application.

Below you can find the code sample that handles login and sending of 2FA requests:

import express from 'express';

import {INTERNAL_SERVER_ERROR} from "http-status-codes";

import {AuthServiceImpl} from "../../../services/auth/user/AuthServiceImpl";

import {AuthService} from "../../../services/auth/user/AuthService";

const router = express.Router();

const authService: AuthService = new AuthServiceImpl();

router.post('/login', async (req, res) => {

const {email} = req.body;

// verify user credentials here.

// normally you will have to check if this is a valid email

// and also check the user's password

// but we skip those to keep things concise

authService

.login(email, "")

.then((response) => {

res.json({

msg: "login was successful. Push notification was sent",

sessionDetails: response.data

})

})

.catch((error) => {

res.status(INTERNAL_SERVER_ERROR).json({

msg: 'login was not successful. Push notification was not sent',

err: error.response.data

});

});

});

export default router;

This is the code inside AuthServiceImpl.authService.login(email, "") function that handles 2FA for our users.

export class AuthServiceImpl implements AuthService {

private readonly userRepo: UserRepository = new UserRepositoryImpl()

private readonly okay2FAService: Okay2FAService = new Okay2FAService()

async login(email: string, password: string): Promise<any> {

if (!email) {

return Promise.reject(EMAIL_NOT_PRESENT_ERROR);

}

const user = await this.userRepo.getOneByEmail(email);

if (!user) {

return Promise.reject(USER_NOT_FOUND_ERROR);

}

// user has been authenticated successfully

// we can now send 2fa request to the mobile device

return this.okay2FAService

.authorizeWithSimpleButtonClick(user.userExternalId)

}

logout(email: string): Promise<any> {

return Promise.resolve(undefined);

}

}


If you followed the steps correctly from the sections above, you have successfully enabled 2FA in your service with Okay.