Authentication
Learn how to authenticate with the Nomad Media API using API keys and SSO.
Authentication
The Nomad Media API supports two authentication methods: direct username/password login and SSO (Single Sign-On). Both methods return a bearer token used for all subsequent API calls.
API Key / Username-Password Login
Use your Nomad Media username and password to obtain a bearer token via the login endpoint. The token is returned in the response and must be included in the Authorization header as Bearer <token> on all subsequent requests.
Tokens are valid for approximately 45 minutes. When the token expires, API calls return 401 Not Authorized. Use the refresh token flow (see below) to renew access without re-authenticating.
For more details on the login endpoint, see the API Reference.
SSO Authentication
Nomad Media has built-in SSO capability. The SSO provider (e.g. Azure AD, Okta) is configured per environment. The SSO_AUTH_PROVIDER constant is dictated by your environment setup — for Azure AD it is "azure".
SSO Login Flow
JavaScript:
// 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);
}Python:
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_tokenNote:
CLIENT_DOMAINis your domain ID provided when your environment was set up.SSO_AUTH_PROVIDERis part of the URL and token names, and must be consistent across your integration.
Refreshing the Token
Tokens expire after the duration specified in the login response (default: 1 hour). When a token expires, use the refresh token to obtain a new one without re-authenticating.
Endpoint:
POST https://admin-app.{CLIENT_DOMAIN}/api/auth/{SSO_AUTH_PROVIDER}/renew
Content-Type: application/json
{
"refreshToken": "<refresh_token>"
}
JavaScript:
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);
}
}Python:
import json, requests
def refresh_token(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")The new token replaces the old token from this point forward. The refresh token can be used repeatedly to continue retrieving new access tokens.
Static API Token
As an alternative to the username/password JWT flow, Nomad Media supports a static API token — a fixed token value that is included in every API call instead of establishing a session.
Pass the static token in the Authorization header exactly as you would a JWT bearer token:
Authorization: Bearer <static-api-token>
Static tokens do not expire and do not require a login or refresh step, which makes them simpler to implement. However, they are harder to rotate securely and are not recommended for production integrations. Use the username/password JWT flow for server-to-server integrations where you control the credential lifecycle.
Contact your Nomad Media administrator to generate a static API token for your environment.
Server-to-Server Best Practices
For backend integrations that use the username/password JWT flow:
- Store credentials in a secrets manager. Treat your service account email and password like an access key/secret pair. Store them in AWS Secrets Manager, Azure Key Vault, or an equivalent service. Fetch credentials at runtime — never hardcode them in source code or commit them to version control.
- Keep the JWT in session memory only. After a successful login, store the
authorizationTokenin memory for the life of the process or request session. Do not persist it to disk or a database. - Use the refresh endpoint proactively. Monitor token age and call the refresh endpoint before the token expires (~45 minutes) to avoid interruption. Most SDKs handle this automatically.
- Use a dedicated service account. Do not use a personal user account for integrations. A dedicated service account makes it easier to manage permissions, audit API activity, and rotate credentials without affecting individuals.
