Skip to content

Authenticate Requests

Pass a headers mapping and it is sent with every request.

A form sending an authenticated request

Static credentials

from panel_openapi import OpenAPIForm

form = OpenAPIForm(
    spec,
    base_url="https://api.example.com",
    path="/things",
    method="get",
    headers={"Authorization": "Bearer s3cret", "Accept": "application/json"},
)

Swapping credentials at runtime

The mapping is read on each submit, not copied at construction, so mutating it takes effect on the next request without rebuilding the form:

headers = {"Accept": "application/vnd.github+json"}
form = OpenAPIForm(spec, base_url=..., path=..., method="get", headers=headers)

def apply_token(event):
    if event.new:
        headers["Authorization"] = f"Bearer {event.new}"
    else:
        headers.pop("Authorization", None)

token = pmui.PasswordInput(label="Token")
token.param.watch(apply_token, "value")

A PasswordInput keeps the value out of the visible page. It still travels to the Python process, which is where the request is made from, so a served app should be over HTTPS if the token matters.

Complete example

"""Send credentials, and swap them at runtime."""

import panel as pn
import panel_material_ui as pmui

from panel_openapi import OpenAPIForm

pn.extension()

SPEC = {
    "openapi": "3.0.0",
    "info": {"title": "GitHub search", "version": "1.0.0"},
    "paths": {
        "/search/repositories": {
            "get": {
                "summary": "Search repositories",
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "required": True,
                        "schema": {"type": "string", "default": "panel holoviz"},
                        "description": "Search query.",
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "schema": {"type": "integer", "minimum": 1, "maximum": 100},
                    },
                ],
                "responses": {"200": {"description": "Matching repositories."}},
            }
        }
    },
}

# The headers dict is read on every submit, so mutating it swaps credentials
# without rebuilding the form.
headers = {"Accept": "application/vnd.github+json"}

form = OpenAPIForm(
    SPEC,
    base_url="https://api.github.com",
    path="/search/repositories",
    method="get",
    headers=headers,
)

token = pmui.PasswordInput(
    label="GitHub token",
    description="Optional. Raises the rate limit. Only ever sent to api.github.com.",
    sizing_mode="stretch_width",
)


def apply_token(event):
    if event.new:
        headers["Authorization"] = f"Bearer {event.new}"
    else:
        headers.pop("Authorization", None)


token.param.watch(apply_token, "value")

pmui.Column(token, form, sizing_mode="stretch_width").servable(title="Authentication")

Header parameters in the document

When the document declares a header parameter, it gets a widget like any other field, and the user fills it in:

{
  "name": "X-API-Key",
  "in": "header",
  "required": true,
  "schema": {"type": "string", "format": "password"}
}

Use format: password and it becomes a PasswordInput.

Widget values win over the headers mapping where both set the same header.

Per-user credentials

Panel runs a separate session per user, so build the form inside the app function and keep the token in that session rather than at module level:

def app():
    headers = {}                 # this session only
    form = OpenAPIForm(SPEC, base_url=..., path=..., method="get", headers=headers)
    ...
    return layout

A module-level dict is shared by every visitor, which is rarely what you want for credentials.