SSO Authentication

How to implement SSO authentication with Azure as the provider, including token retrieval and refresh.

Nomad Media has built-in SSO capability. The following examples use JavaScript and Python with Azure as the SSO provider.

CLIENT_DOMAIN — Your domain ID provided during environment setup.

SSO_AUTH_PROVIDER — The constant value is "azure". This is the SSO provider name, must be consistent, and appears in both the URL and token names.

Login

// Build the SSO provider URL
const authProviderLoginUrl = `${adminApiUrl}/auth/${constants.SSO_AUTH_PROVIDER}/login`;

// Navigate to the provider URL
location.href = authProviderLoginUrl;

// When it returns, get query string parameters
const urlParams = new URLSearchParams(window.location.search);
const authCallbackFromQueryString = urlParams.get("authCallback");

// Check for valid authCallback
if (authCallbackFromQueryString) {
  // Get both tokens from the query string
  const token = urlParams.get(`${constants.SSO_AUTH_PROVIDER}_token`);
  const refreshToken = urlParams.get(`${constants.SSO_AUTH_PROVIDER}_refresh_token`);

  // Store both tokens
  sessionStorage.setItem("token", token);
  sessionStorage.setItem("refreshToken", refreshToken);
}
import urllib.parse
import os

login_url = adminApiUrl + "/auth/" + constants.SSO_AUTH_PROVIDER + "/login"
auth_callback_from_query_string = None

# Redirect to login URL
os.system("start " + login_url)

# Get authCallback from query string
url_params = urllib.parse.parse_qs(urllib.parse.urlparse(window.location).query)
if "authCallback" in url_params:
    auth_callback_from_query_string = url_params["authCallback"][0]

if auth_callback_from_query_string:
    token = url_params.get(constants.SSO_AUTH_PROVIDER + "_token")
    refresh_token = url_params.get(constants.SSO_AUTH_PROVIDER + "_refresh_token")

    # Store both tokens
    sessionStorage["token"] = token
    sessionStorage["refreshToken"] = refresh_token

Refreshing Tokens

The token is valid for the number of seconds returned in the login response (default: 1 hour). After expiry, all API calls return 401 Not Authorized. Use the refreshToken from the initial login to request a new token without re-authenticating.

Endpoint:

POST https://admin-app.${CLIENT_DOMAIN}/api/auth/${SSO_AUTH_PROVIDER}/renew
// headers
Content-Type: application/json

// body
{
  "refreshToken": "<refresh_token>"
}
export default async function refreshToken() {
    const refreshToken = sessionStorage.getItem("refreshToken");

    if (!refreshToken) {
        throw new Error("Refresh Token: The refresh token is invalid");
    }

    const headers = new Headers();
    headers.append("Content-Type", "application/json");

    const body = { refreshToken: refreshToken };

    const refreshTokenUrl = `https://{adminApiUrl}/api/auth/${constants.SSO_AUTH_PROVIDER}/renew`;

    const response = await fetch(refreshTokenUrl, {
        method: "POST",
        headers: headers,
        body: JSON.stringify(body)
    });

    if (response && response.ok) {
        const refreshTokenResponse = await response.json();
        sessionStorage.setItem("token", refreshTokenResponse.token);
        sessionStorage.setItem("refreshToken", refreshTokenResponse.refreshToken);
        return;
    }
}
import json, requests

def refreshToken(REFRESH_TOKEN: str) -> dict:
    if (not REFRESH_TOKEN):
        raise Exception("Refresh Token: The refresh token is invalid")

    API_URL = "https://{adminApiUrl}/api/auth/{SSO_AUTH_PROVIDER}/renew"

    HEADERS = {
        "Content-Type": "application/json"
    }

    BODY = {
        "refreshToken": REFRESH_TOKEN
    }

    try:
        RESPONSE = requests.post(API_URL, headers=HEADERS, data=json.dumps(BODY))
        INFO = json.loads(RESPONSE.text)

        if RESPONSE.status_code != 200:
            raise Exception("Response returned " + str(RESPONSE.status_code))

        return INFO
    except:
        raise Exception("Refresh Token failed: " + json.dumps(RESPONSE.text))

The new token returned should replace the previous token for all subsequent API calls. The refreshToken can be reused repeatedly to continue obtaining new tokens.