Skip to main content

Pluggable OIDC/OAuth 2.1 authentication routes and token validation.

Project description

py-oidc-auth logo

A small, typed OpenID Connect helper for authentication and authorization.

License codecov docs PyPI Python Versions

It provides

  • a framework independent async core: OIDCAuth
  • framework adapters that expose common auth endpoints
  • simple required() and optional() helpers to protect routes

What it does

py-oidc-auth adds OpenID Connect authentication to your Python web application. You create one auth instance at app startup, get a pre-built router (or blueprint / URL patterns), optionally add your own custom routes to it, and include it in your app. Protected routes use required() and optional() helpers.

Supported frameworks

FastAPI
FastAPI
Flask
Flask
Quart
Quart
Tornado
Tornado
Litestar
Litestar
Django
Django

Features

  • Authorization code flow with PKCE (login and callback)
  • Refresh token flow
  • Device authorization flow
  • Userinfo lookup
  • Provider initiated logout (end session) when supported
  • Bearer token validation using provider JWKS, issuer, and audience
  • Optional scope checks and simple claim constraints
  • Full type annotation 🏷️

Install

Pick your framework for installation with pip:

python -m pip install py-oidc-auth[fastapi]
python -m pip install py-oidc-auth[flask]
python -m pip install py-oidc-auth[quart]
python -m pip install py-oidc-auth[tornado]
python -m pip install py-oidc-auth[litestar]
python -m pip install py-oidc-auth[django]

Or use conda/mamba/micromamba:

conda install -c conda-forge py-oidc-auth-fastapi
conda install -c conda-forge py-oidc-auth-flask
conda install -c conda-forge py-oidc-auth-quart
conda install -c conda-forge py-oidc-auth-tornado
conda install -c conda-forge py-oidc-auth-litestart
conda install -c conda-forge py-oidc-auth-django

Import name is py_oidc_auth:

from py_oidc_auth import OIDCAuth

Concepts

Core

OIDCAuth is the framework independent client. It loads provider metadata from the OpenID Connect discovery document, performs provider calls, and validates tokens.

Adapters

Each adapter subclasses OIDCAuth and adds:

  • helpers to register the standard endpoints (router, blueprint, urlpatterns, etc.)
  • required() and optional() helpers to validate bearer tokens on protected routes

Default endpoints

Adapters can expose these paths (customizable and individually disableable):

  • GET /auth/v2/login
  • GET /auth/v2/callback
  • POST /auth/v2/token
  • POST /auth/v2/device
  • GET /auth/v2/logout
  • GET /auth/v2/userinfo

Quick start

Create one auth instance at app startup:

auth = ...(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

FastAPI

from typing import Dict, Optional

from fastapi import FastAPI
from py_oidc_auth import FastApiOIDCAuth, IDToken

app = FastAPI()

auth = FastApiOIDCAuth(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

app.include_router(auth.create_auth_router(prefix="/api"))

@app.get("/me")
async def me(token: IDToken = auth.required()) -> Dict[str, str]:
    return {"sub": token.sub}

@app.get("/feed")
async def feed(token: Optional[IDToken] = auth.optional()> Dict[str, str]:
    if token is None:
       message = "Welcome guest"
    else:
       message = "Welcome back, {token.given_name}"
    return {"message": message}

Flask

from flask import Flask, Response, jsonify
from py_oidc_auth import FlaskOIDCAuth

app = Flask(__name__)

auth = FlaskOIDCAuth(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

app.register_blueprint(auth.create_auth_blueprint(prefix="/api"))

@app.get("/protected")
@auth.required()
def protected(token: IDToken) -> Response:
    return jsonify({"sub": token.sub})

Quart

from quart import Quart, Response, jsonify
from py_oidc_auth import QuartOIDCAuth, IDToken

app = Quart(__name__)

auth = QuartOIDCAuth(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

app.register_blueprint(auth.create_auth_blueprint(prefix="/api"))

@app.get("/protected")
@auth.required()
async def protected(token: IDToken) -> Response:
    return jsonify({"sub": token.sub})

Django

Decorator style:

from django.http import HttpRequest, JsonResponse
from django.urls import path
from py_oidc_auth import DjangoOIDCAuth, IDToken

auth = DjangoOIDCAuth(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

@auth.required()
async def protected_view(request: HttpRequest, token: IDToken) -> JsonResponse:
    return JsonResponse({"sub": token.sub})

urlpatterns = [
    *auth.get_urlpatterns(prefix="api"),
    path("protected/", protected_view),
]

Routes only:

urlpatterns = [
    *auth.get_urlpatterns(prefix="api"),
]

Tornado

import json
import tornado.web
from py_oidc_auth import TornadoOIDCAuth, IDToken

auth = TornadoOIDCAuth(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

class ProtectedHandler(tornado.web.RequestHandler):
    @auth.required()
    async def get(self, token: IDToken) -> None:
        self.write(json.dumps({"sub": token.sub}))

def make_app():
    return tornado.web.Application(
        auth.get_handlers(prefix="/api") + [
            (r"/protected", ProtectedHandler),
        ]
    )

Litestar

from typing import Dict
from litestar import Litestar, get
from py_oidc_auth import LitestarOIDCAuth

auth = LitestarOIDCAuth(
    client_id="my client",
    client_secret="secret",
    discovery_url="https://idp.example.org/realms/demo/.well-known/openid-configuration",
    scopes="myscope profile email",
    audience="my-aud",
)

@get("/protected")
@auth.required()
async def protected(token: IDToken) -> Dict[str, str]:
    return {"sub": token.sub}

app = Litestar(
    route_handlers=[
        protected,
        *auth.get_route_handlers(prefix="/api"),
    ]
)

Scopes audience and claim constraints

All adapters support:

  • scopes="a b c" to require scopes on a protected endpoint
  • claims={...} to enforce simple claim constraints
  • audience=my-aud to enforce intended audience check

FastApi Example:

@auth.required(scopes="admin", claims={"groups": ["admins"]})
def admin(token: IDToken) -> Dict[str, str]:
    return {"sub": token.sub}

Related

  • py-oidc-auth-client — typed Python client for authenticating against services that expose py-oidc-auth-compatible routes.

Contributing

See the CONTRIBUTING.md document to get involved.

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

py_oidc_auth-2604.0.0.tar.gz (2.7 MB view details)

Uploaded Source

Built Distribution

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

py_oidc_auth-2604.0.0-py3-none-any.whl (38.1 kB view details)

Uploaded Python 3

File details

Details for the file py_oidc_auth-2604.0.0.tar.gz.

File metadata

  • Download URL: py_oidc_auth-2604.0.0.tar.gz
  • Upload date:
  • Size: 2.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for py_oidc_auth-2604.0.0.tar.gz
Algorithm Hash digest
SHA256 7d7028508052627bf5c94fe863766b5c86b3c700711426d54ac27dd4278e7822
MD5 90353c41b0d769a7593d7b7bdc3a4cef
BLAKE2b-256 2cd73a6b23f7e837744a0960f0307a00395e0c174e553758e6cf87f80b28adf1

See more details on using hashes here.

File details

Details for the file py_oidc_auth-2604.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for py_oidc_auth-2604.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cce193940d9d19275421acf34a51239d80e7b328b2969dc5b65b03ce8dcf1a9e
MD5 0ccb577ad59317566609cf5ead93e44b
BLAKE2b-256 6e2a3140b2470e4e818fe8b5f65c9b3a43f802f311e4bcc671eb4ee9cdf86576

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