Skip to main content

Python client SDK for Signward Identity Server — OIDC authentication for FastAPI and other Python apps.

Project description

signward-idserver-client

Python client SDK for Signward Identity Server — OIDC authentication for FastAPI and other Python apps. Open source, no Microsoft-stack dependency.

Features

  • OIDC Authorization Code flow (with optional PKCE)
  • Local JWT validation via the server's JWKS (HS256 today, algorithm-agnostic)
  • Discovery + JWKS caching
  • Typed Pydantic v2 user model with built-in and per-tenant custom roles
  • Async-first (httpx.AsyncClient)
  • First-class FastAPI integration (Depends-friendly auth helpers)
  • MIT licensed

Install

pip install "signward-idserver-client[fastapi]"

Core-only (no framework integration):

pip install signward-idserver-client

Python 3.10+.

Quickstart — FastAPI (protecting an API)

from fastapi import FastAPI, Depends
from idserver import IdServerClient
from idserver.fastapi import IdServerAuth, CurrentUser

app = FastAPI()

idserver = IdServerClient(
    authority="https://mytenant.signward.com",
    client_id="my-api",
    client_secret="...",          # if confidential
    options=None,
)
auth = IdServerAuth(idserver)


@app.get("/me")
async def me(user: CurrentUser = Depends(auth.current_user)):
    return {
        "id": user.user_id,
        "email": user.email,
        "tenant_id": user.tenant_id,
        "roles": user.roles,
        "custom_roles": user.custom_roles,
    }


@app.get("/admin")
async def admin(user: CurrentUser = Depends(auth.require_role("admin"))):
    return {"ok": True}


@app.get("/editors-or-admins")
async def editors(
    user: CurrentUser = Depends(auth.require_role("admin", "editor")),
):
    return {"ok": True}


@app.get("/billing-admin-only")
async def billing(
    user: CurrentUser = Depends(
        auth.require_role("admin", "billing", require_all=True)
    ),
):
    return {"ok": True}

Clients call your API with a bearer token obtained from Signward:

GET /me
Authorization: Bearer eyJhbGciOi...

Quickstart — Server-side login flow

For a Python web app that wants to perform the full OIDC login flow itself:

from idserver import IdServerClient

client = IdServerClient(
    authority="https://mytenant.signward.com",
    client_id="my-webapp",
    client_secret="...",
)

# 1) Redirect the user to the login page:
url, verifier = client.authorize_url_with_pkce(
    redirect_uri="https://myapp.com/callback",
    state="random-state",
)
# Store `verifier` and `state` in the session, redirect to `url`.

# 2) On callback (?code=...&state=...):
tokens = await client.exchange_code(
    code,
    redirect_uri="https://myapp.com/callback",
    code_verifier=verifier,
)

# 3) Validate the access token locally:
user = await client.validate_token(tokens.access_token)
print(user.email, user.roles)

# 4) Or fetch userinfo remotely:
user = await client.userinfo(tokens.access_token)

# 5) Later, refresh:
new_tokens = await client.refresh_token(tokens.refresh_token)

# 6) Logout URL:
logout = await client.end_session_url(
    id_token_hint=tokens.id_token,
    post_logout_redirect_uri="https://myapp.com/",
)

User model

class User:
    user_id: UUID | None         # "sub" claim
    email: str | None
    email_verified: bool | None
    name: str | None
    given_name: str | None
    family_name: str | None
    tenant_id: UUID | None
    roles: list[str]             # built-in roles
    custom_roles: list[str]      # per-tenant RBAC roles
    claims: dict                 # raw JWT / userinfo payload

    def has_role(self, role: str) -> bool: ...
    def has_custom_role(self, role: str) -> bool: ...
    def has_any_role(self, *roles: str, include_custom: bool = True) -> bool: ...
    def has_all_roles(self, *roles: str, include_custom: bool = True) -> bool: ...

Configuration

Pass values directly to IdServerClient or use an IdServerOptions dataclass for more control:

from idserver import IdServerClient, IdServerOptions

client = IdServerClient(options=IdServerOptions(
    authority="https://mytenant.signward.com",
    client_id="my-app",
    client_secret="...",
    scopes=["openid", "profile", "email", "roles"],
    audience="idserver-api",     # expected JWT aud claim
    issuer=None,                 # default: discovery.issuer
    timeout=10.0,
    verify_ssl=True,             # set False for localhost dev
))

Using your own HTTP client

If you want to share an httpx.AsyncClient with the rest of your app:

import httpx
from idserver import IdServerClient

shared_http = httpx.AsyncClient(timeout=20.0)
client = IdServerClient(
    authority="...",
    client_id="...",
    http_client=shared_http,
)
# When constructed this way, IdServerClient will NOT close shared_http.

Error handling

All exceptions derive from idserver.IdServerError:

from idserver import IdServerError, InvalidTokenError, TokenExchangeError

try:
    user = await client.validate_token(token)
except InvalidTokenError as e:
    ...
except TokenExchangeError as e:
    print(e.error, e.description, e.status_code)
except IdServerError as e:
    ...

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

signward_idserver_client-1.0.0.tar.gz (13.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

signward_idserver_client-1.0.0-py3-none-any.whl (15.3 kB view details)

Uploaded Python 3

File details

Details for the file signward_idserver_client-1.0.0.tar.gz.

File metadata

  • Download URL: signward_idserver_client-1.0.0.tar.gz
  • Upload date:
  • Size: 13.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for signward_idserver_client-1.0.0.tar.gz
Algorithm Hash digest
SHA256 3dbf279899b9dcdfd9f4cf1df72e9819fa11aceea8c60a30f099bc3cb07d679a
MD5 9146003c46ffd2b8e262b52667c97c1f
BLAKE2b-256 4f73d1352f69659424e5cc595c5d2fe868cb7e43817d0f28593bc4a487938a20

See more details on using hashes here.

File details

Details for the file signward_idserver_client-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for signward_idserver_client-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 10ebc027df85739c294edc100b965e4452879d2183a1a7d591538c36f43cddad
MD5 e64ec580dcbd7a93bfe0a8377548ab8f
BLAKE2b-256 49d63057e4045cb889cc0931b7ea17ab83489906b9f9cafbf452621888011d8c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page