Skip to main content

altium-auth (Python)

PyPI Python CI license

Altium 365 OAuth2 / OpenID Connect authentication for Python. Supports both client types across Commercial Cloud, GovCloud, and AES (on-prem):

  • Public clients (desktop, native) — browser sign-in with PKCE over Altium's ActionWait long-poll: AltiumAuthClient.sign_in.
  • Confidential clients (web/server backends) — the standard authorization-code redirect flow: create_authorization_url + exchange_code.
  • Workspace tokens, refresh, revocation, and first-class GovCloud + AES support.

Zero runtime dependencies (stdlib only). Python 3.10+.

Documentation

The library implements the protocol described in the language-neutral guides (start here if you're new to Altium Identity):

Installation

pip install altium-auth

Quick start

Construct an AltiumAuthClient once with an AltiumAuthConfig. Only client_id and scopes are required — endpoints default to the Commercial Cloud.

Public apps (desktop — ActionWait sign-in)

from altium_auth import AltiumAuthClient, AltiumAuthConfig

client = AltiumAuthClient(AltiumAuthConfig(client_id="your-client-id", scopes="openid profile"))

# Opens a browser login page and waits for the callback.
tokens = client.sign_in()

# Persist `tokens` yourself — a keyring, an OS credential store, or a file.
workspace = client.sign_into_workspace(tokens.access_token, "workspace-id-here")

Provide a custom browser opener (e.g. an IDE/host bridge) via config; the library still owns PKCE, ActionWait polling, state correlation, CSRF validation, and the token exchange:

config = AltiumAuthConfig(
    client_id="your-client-id",
    scopes="openid profile",
    open_browser=lambda url: my_host.open_external(url),
)

Confidential apps (web / server — authorization-code redirect)

from altium_auth import AltiumAuthClient, AltiumAuthConfig

client = AltiumAuthClient(
    AltiumAuthConfig(
        client_id="your-client-id",
        client_secret="your-client-secret",  # confidential client → HTTP Basic
        scopes="openid profile offline_access",
    )
)
redirect_uri = "https://my-service.example.com/oauth/callback"

# On your login route: build the URL, stash state + verifier, then redirect.
req = client.create_authorization_url(redirect_uri=redirect_uri)
session["oauth"] = {"state": req.state, "code_verifier": req.code_verifier}
# redirect(req.url)

# On your callback route: verify state, then exchange the code.
if request.args["state"] != session["oauth"]["state"]:
    raise ValueError("state mismatch")
tokens = client.exchange_code(
    request.args["code"],
    code_verifier=session["oauth"]["code_verifier"],
    redirect_uri=redirect_uri,
)

GovCloud

from altium_auth import AltiumAuthClient, AltiumAuthConfig, GOV_CLOUD_ENDPOINTS

client = AltiumAuthClient(
    AltiumAuthConfig(
        client_id="your-gov-client-id",
        scopes="openid profile",
        endpoints=GOV_CLOUD_ENDPOINTS,
    )
)
tokens = client.sign_in()  # secure=1 is added to token requests automatically

AES (on-prem)

from altium_auth import AltiumAuthClient, AltiumAuthConfig, aes_endpoints

endpoints = aes_endpoints("https://aes.server.example:9785")
scopes = AltiumAuthClient.get_client_scopes(endpoints.scope_endpoint, "your-aes-client-id")
client = AltiumAuthClient(
    AltiumAuthConfig(
        client_id="your-aes-client-id",
        scopes=" ".join(scopes),
        endpoints=endpoints,
    )
)
tokens = client.sign_in()

Using from async code

The library is synchronous. From an event loop, bridge with the stdlib:

import asyncio

tokens = await asyncio.to_thread(client.refresh_token, refresh_token)

API reference

Method Description
create_authorization_url(*, redirect_uri=None, state=None, code_verifier=None, scopes=None, select_workspace=WorkspaceSelection.NONE) Build a PKCE authorization URL (no I/O). Returns AuthorizationRequest(url, state, code_verifier).
exchange_code(code, *, code_verifier=None, redirect_uri=None, timeout=30.0) Authorization-code grant → TokenSet.
sign_in(*, select_workspace=WorkspaceSelection.NONE, timeout=180.0) Full ActionWait sign-in → TokenSet.
sign_into_workspace(base_access_token, workspace_auth_id, *, timeout=30.0) RFC 8693 workspace exchange → TokenSet.
refresh_token(refresh_token, *, timeout=30.0) Refresh grant (no scope resent) → TokenSet.
revoke_refresh_token(refresh_token, *, timeout=30.0) RFC 7009 revocation (idempotent).
AltiumAuthClient.get_client_scopes(scope_endpoint, client_id, *, timeout=30.0) Static; scope introspection → list[str].

Types

  • AltiumAuthConfig(client_id, scopes, client_secret=None, endpoints=COMMERCIAL_CLOUD_ENDPOINTS, secure=None, open_browser=None) — properties is_confidential, use_secure.
  • AltiumEndpoints(authorize_endpoint, token_endpoint, action_wait_endpoint, redirect_uri, scope_endpoint=None) — constants COMMERCIAL_CLOUD_ENDPOINTS, GOV_CLOUD_ENDPOINTS; factory aes_endpoints(origin).
  • TokenSet(access_token, token_type, expires_in, expires_at, refresh_token, id_token, scope) — expires_at computed with a 30 s clock-skew buffer.
  • WorkspaceSelection — NONE / STRICT / OPTIONAL.

access_token is a signed JWT — decode it to read iss, workspaceId, secure, and scopes. See Access token claims.

Error handling

All errors subclass AltiumAuthError:

Class When
ConfigurationError Missing client_id/scopes, invalid endpoint URL, or empty required argument.
OAuthError Token/revocation/scope endpoint returned a non-success status (carries .status, .error, .error_description).
ActionWaitError ActionWait timed out, was cancelled (410), or returned an unusable body.
StateMismatchError Returned state ≠ wait token (CSRF guard). Subclass of ActionWaitError.
TransportError Network-level failure.

Development

uv sync --extra dev
uv run pytest            # unit + conformance
uv run ruff check .
uv run mypy
uv build

Live E2E sign-in

uv run python tools/signin_test.py YOUR_CLIENT_ID
uv run python tools/signin_test.py --env gov YOUR_GOV_CLIENT_ID
uv run python tools/signin_test.py --env aes --aes-origin https://aes.server.example:9785 YOUR_AES_CLIENT_ID

Security

Report vulnerabilities privately — see SECURITY.md.

License

MIT © Altium Limited

Release files for altium-auth 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for altium-auth 0.2.0
File Size Uploaded
altium_auth-0.2.0.tar.gz 11.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for altium-auth 0.2.0
File Interpreter ABI Platform
altium_auth-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 26.0 kB

Release files / altium_auth-0.2.0.tar.gz

Download URL altium_auth-0.2.0.tar.gz
Size 11.5 kB
Tags Source
SHA-256 checksum
How to use checksums
9218a1ac92f8b69d60440a7e89b21ae43e1013891225b7396b20b3e410751ccd
BLAKE2b-256 checksum
How to use checksums
9f81e98a932e5910fd08d8ed6b841c0c96e25aab87463686c675afe5d60e73da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.

Transparency log

Release files / altium_auth-0.2.0-py3-none-any.whl

Download URL altium_auth-0.2.0-py3-none-any.whl
Size 14.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3923941c88f1d6d021f836f9ffd0c05c57589bf1ee5b133f564f66a8c2995654
BLAKE2b-256 checksum
How to use checksums
35558fa445e852bb29215ff8cbe219a439730153ba11299488ff6dd74aa419bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page