Authenticate#

get_jmap_client() reads configuration from explicit keyword arguments, environment variables, then a YAML config file, checked in that order. If none of the three sources supplies a url, it returns None.

The first source that supplies a url wins outright for that call: passing url explicitly, even with no other arguments, uses only the explicit arguments, and doesn’t fall back to environment variables or the config file for username/password/auth_type/timeout. If you mix sources, supply url from the lowest-priority source you want to use; a source that doesn’t supply url still has its other fields merged in underneath whichever source does.

Resolve configuration without building a client#

get_connection_params() runs the same resolution get_jmap_client() does, but returns the resolved dict instead of a client, or None if nothing was found. Useful if you want to inspect or adjust the result before constructing a client yourself, or you’re building your own thin wrapper around this project’s config sources (for example, a CLI tool that passes every flag through unconditionally, whether or not the user set it, without wiping out an environment variable for the flags they left off):

from calendaring_jmap import get_connection_params

params = get_connection_params(url=cli_args.url, username=cli_args.username)
if params is None:
    raise SystemExit("No JMAP configuration found")

Use environment variables or a config file#

Environment variables:

export JMAP_URL=https://jmap.example.com/.well-known/jmap
export JMAP_USERNAME=alice
export JMAP_PASSWORD=secret
export JMAP_AUTH_TYPE=basic
export JMAP_TIMEOUT=30

JMAP_URL, JMAP_USERNAME, and JMAP_PASSWORD map directly to get_jmap_client()’s own url/username/password arguments. JMAP_AUTH_TYPE (basic or bearer) forces the auth type instead of inferring it; JMAP_TIMEOUT sets the request timeout in seconds. Both are optional.

Or a YAML config file, at ~/.config/calendaring-jmap/calendar.yaml by default:

url: https://jmap.example.com/.well-known/jmap
username: alice
password: secret

Override the config file’s location with the JMAP_CONFIG_FILE environment variable, or by passing config_file directly to get_jmap_client().

With either in place, no arguments are needed:

client = get_jmap_client()

Use Basic or Bearer auth explicitly#

Supplying a username alongside a password uses HTTP Basic auth. Supplying only a password (token), with no username, uses Bearer token auth.

# Basic auth
client = get_jmap_client(
    url="https://jmap.example.com/.well-known/jmap",
    username="alice",
    password="secret",
)

# Bearer token (password argument holds the token, no username supplied)
client = get_jmap_client(
    url="https://jmap.example.com/.well-known/jmap",
    password="my-bearer-token",
)

Use a pre-built auth object#

Pass any requests-compatible auth object directly via the auth parameter (niquests is API-compatible with requests):

try:
    from niquests.auth import HTTPBasicAuth
except ImportError:
    from requests.auth import HTTPBasicAuth
client = get_jmap_client(
    url="https://jmap.example.com/.well-known/jmap",
    auth=HTTPBasicAuth("alice", "secret"),
)

JMAP doesn’t use a 401 challenge and retry. Credentials are sent on every request, and a 401 or 403 is a hard JMAPAuthError.

Release the connection without a context manager#

If you don’t use with/async with, call close() (or aclose() on the async client) when you’re done, to release the underlying HTTP session and its connection pool:

client = get_jmap_client()
try:
    calendars = client.get_calendars()
finally:
    client.close()

Calling it is optional. Forgetting isn’t harmful: the session is recreated automatically on the next request.

Last updated on 2026-10-08.