The Next Level3 Okta integration is designed to be used for your existing applications or sites that are using Okta for authentication. This integration will allow you to easily add Account Protection to any application the leverages Okta for authentication. Currently, Inline Hooks can not be used with integrations from the “App Integration Catalog” so a custom “App Integration” is required.



– Okta Application with custom “App Integration”
– Next Level3 Company Account
– Signing Key created for an application in the Next Level3 Company Portal

Account Protection


The first step to add an NL3 Account Protection Check to an existing application that has its own Okta “App Integration” which is used for either SAML or OpenID Connect (OIDC) authentication is to create an API endpoint that points to a function that performs the lock check and returns the appropriate responses back to Okta for locked or unlocked accounts. This API endpoint will be called from an Okta “Inline Hook” to integrate into the authentication flow. Here is some sample Python code:

					import json
import os
import requests
import base64
import logging
from datetime import datetime
import jwt

logger = logging.getLogger()

def getLockStatus(token, api_uri, api_path, validationData):
  responseDict = {}
    headers_dict = {"x-nl3-authorization-token": token, "Content-Type": "application/json"}
    responseIPInfo = {}
    location = ""
    if "." in validationData["ipAddress"] or ":" in validationData["ipAddress"]:
        responseIPInfo = requests.get("" + validationData["ipAddress"] + "?token=[ipinfo_auth_token]").json()
        if "city" in responseIPInfo:
            location = responseIPInfo["city"] + ", " + responseIPInfo["region"]    
    data_dict = {
      "userIP": validationData["ipAddress"],
      "userDevice": "N/A",
      "userLocation": location,
      "integrationType": "okta-oidc",
      "integrationData": responseIPInfo,
    response ="".join([api_uri,api_path]), headers=headers_dict, json=data_dict)
    responseDict = response.json()
  except Exception as e:
    responseDict = { "message": str(e) }

  return responseDict

def lambda_handler(event, context):
  ### This is the API Key used to authenticate from the Okta Inline Hook to this API
  ### Ideally, this API Key would be stored and retrieved from a secrets manager
  ### and not from an environmental variable
  if base64.b64decode(event["headers"]["Authorization"]).decode('utf-8') == os.environ["API_KEY"]:
    body = json.loads(event["body"])
    username = body["data"]["identity"]["claims"]["preferred_username"]
    claims = {
      "iss": os.environ["APP_URI"],
      "iat": (datetime.utcnow().timestamp() + (-1 * 60)),
      "exp": (datetime.utcnow().timestamp() + (5 * 60)),
      "aud": os.environ["API_URI"],
      "sub": username
    ### Ideally, the Signing Key would be stored and retrieved from a secrets manager
    ### and not from an environmental variable    
    decodedDomainToken = base64.b64decode(os.environ["SIGNING_KEY"])
    token = jwt.encode(
    response = getLockStatus(token, os.environ["API_URI"], os.environ["API_PATH"], body["data"]["context"]["request"])
    retVal = {
      "isBase64Encoded": False,
      "statusCode": 200,
    if response.get("locked", False):
      retVal =  {
        "isBase64Encoded": False,
        "statusCode": 200,
        "headers": { "Content-Type": "application/json" },
        "body": json.dumps({ "error": { "errorSummary": os.environ["LOCKED_MESSAGE"] } }),
    retVal =  {
      "isBase64Encoded": False,
      "statusCode": 200,
      "headers": { "Content-Type": "application/json" },
      "body": json.dumps({ "error": { "errorSummary": "Okta Hook Forbidden" } }),
  return retVal

The next step is to set up either a SAML or OIDC “Inline Hook” in Okta as follows:

Step 1

Step 2

Step 3 – Complete & Save Form

Example Completed Form

The final step is to enable the inline hook using one of the following methods depending on whether the integration is a SAML or OIDC integration:



Scroll to Top