Skip to content

Authentication and authorization

NOMAD uses tokens to authenticate requests and enforce access control.

For programmatic API usage (scripts, notebooks, CLI workflows, CI jobs), the recommended approach is to use Personal Access Tokens (PATs).

NOMAD supports multiple token types:

  • Personal Access Tokens (PATs) — recommended for programmatic use
  • Keycloak access tokens — mainly for interactive or legacy workflows

Personal Access Tokens

Personal Access Tokens (PATs) are the preferred way for programmatic access to the API.

Note

GUI management page for PATs is coming soon.

Create a PAT

A PAT can be created via the API with the keycloak access token. For example:

Warning

Always grant the minimum required scopes when creating a PAT. Avoid unnecessary permissions (e.g. write access if only read is needed).

Note

The raw token is only returned once when the token is created. Store it securely.

import requests

response = requests.post(
    "http://localhost:8000/nomad-oasis/api/v1/pats",
    headers={"Authorization": "Bearer <keycloak-access-token>"},
    json={
        "metadata": {
            "name": "My script token",
            "scopes": ["uploads:read", "uploads:write"]
        },
        "expires_in_days": 30
    },
)
response.raise_for_status()

pat = response.json()
raw_token = pat["raw_token"]

Use a PAT in Python

Once created, we recommend storing it in an environment variable:

export NOMAD_PAT="<personal-access-token>"

Use the PAT in subsequent requests:

import os
import requests

response = requests.get(
    "http://localhost:8000/nomad-oasis/api/v1/uploads",
    headers={"Authorization": f"Bearer {os.environ["NOMAD_PAT"]}"},
)
response.raise_for_status()

uploads = response.json()["data"]

List your PATs

You can list all PATs for your account, and optionally filter, sort, and paginate the results.

For example, this request searches for PATs whose name matches "ci", filters for active tokens, sorts by most recently created first, and returns the first page of results:

import os
import requests

response = requests.get(
    "http://localhost:8000/nomad-oasis/api/v1/pats",
    headers={"Authorization": f"Bearer {os.environ["NOMAD_PAT"]}"},
    params={
        "search": "ci",
        "state": "active",
        "order_by": "created_desc",
        "page_size": 20,
        "page": 1,
    },
)
response.raise_for_status()

result = response.json()

tokens = result["data"]

You can filter and sort PATs using query parameters, see the API dashboard for available options and examples.

Inspect a PAT

You can retrieve metadata for a specific PAT with its ID:

import requests

pat_id = "<pat-id>"

response = requests.get(
    f"http://localhost:8000/nomad-oasis/api/v1/pats/{pat_id}",
    headers={"Authorization": "Bearer <keycloak-access-token>"},
)
response.raise_for_status()

token_metadata = response.json()

Revoke a PAT

To revoke a PAT:

import requests

pat_id = "<pat-id>"

response = requests.delete(
    f"http://localhost:8000/nomad-oasis/api/v1/pats/{pat_id}",
    headers={"Authorization": "Bearer <keycloak-access-token>"},
)
response.raise_for_status()

Rotate a PAT

To replace an existing PAT with a new one:

import requests

pat_id = "<pat-id>"

response = requests.post(
    f"http://localhost:8000/nomad-oasis/api/v1/pats/{pat_id}/rotate",
    headers={"Authorization": "Bearer <keycloak-access-token>"},
)
response.raise_for_status()

new_pat = response.json()
new_raw_token = new_pat["raw_token"]

Warning

Rotating a PAT revokes the previous token and returns a new raw token value, which is only visible once.

Keycloak access tokens

NOMAD also supports keycloak access tokens for authenticated API access.

For interactive use in the dashboard, use the Authorize button. The dashboard GUI manages the keycloak access token automatically while you try out API operations.

Warning

For new programmatic integrations, prefer Personal Access Tokens (PATs) over directly using account credentials to obtain keycloak access tokens.

Keycloak access tokens may also be used to call the API directly. For example:

import os
import requests

response = requests.post(
    "http://localhost:8000/nomad-oasis/api/v1/auth/token",
    data={
        "username": os.getenv("NOMAD_USERNAME"),
        "password": os.getenv("NOMAD_PASSWORD"),
        "grant_type": "password",
    },
)
response.raise_for_status()

token = response.json()["access_token"]

response = requests.get(
    "http://localhost:8000/nomad-oasis/api/v1/uploads",
    headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()

uploads = response.json()["data"]

If you have the NOMAD Python package installed, you can also use the nomad.client.Auth helper:

Warning

This username/password-based flow is kept mainly for compatibility and trusted first-party usage. For new programmatic integrations, prefer Personal Access Tokens (PATs) instead.

import os
import requests
from nomad.client import Auth

response = requests.get(
    "http://localhost:8000/nomad-oasis/api/v1/uploads",
    auth=Auth(
        user=os.getenv("NOMAD_USERNAME"),
        password=os.getenv("NOMAD_PASSWORD"),
    ),
)
response.raise_for_status()

uploads = response.json()["data"]

App token

Deprecated tokens

app token is deprecated and will be removed in a future release. They should not be used for new integrations. Use Personal Access Tokens (PATs) instead.

If the short-term expiration of the default access token does not suit your needs, you can request an app token with a user-defined expiration. For example, you can send the GET request /auth/app_token?expires_in=86400 together with some way of authentication, e.g. header Authorization: Bearer <access token>. The API will return an app token, which is valid for 24 hours in subsequent request headers with the format Authorization: Bearer <app token>. The request will be declined if the expiration is larger than the maximum expiration defined by the API config.

Warning

Despite the name, the app token is used to impersonate the user who requested it. It does not discern between different uses and will only become invalid once it expires (or when the API's secret is changed).