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

It porvides

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

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:

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]

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="openid profile email",
)

FastAPI

from typing import Optional

from fastapi import FastAPI
from py_oidc_auth import FastApiOIDCAuth
from py_oidc_auth.schema import 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="openid profile email",
)

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

@app.get("/me")
async def me(token: IDToken = auth.required()):
    return {"sub": token.sub}

@app.get("/feed")
async def feed(token: Optional[IDToken] = auth.optional()):
    return {"authenticated": token is not None}

Flask

from flask import Flask
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",
)

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

@app.get("/protected")
@auth.required()
def protected(token):
    return {"sub": token.sub}

Quart

from quart import Quart
from py_oidc_auth import QuartOIDCAuth

app = Quart(__name__)

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

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

@app.get("/protected")
@auth.required()
async def protected(token):
    return {"sub": token.sub}

Django

Decorator style:

from django.http import JsonResponse
from django.urls import path
from py_oidc_auth import DjangoOIDCAuth

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

@auth.required()
async def protected_view(request, token):
    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 tornado.web
from py_oidc_auth import TornadoOIDCAuth

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

class ProtectedHandler(tornado.web.RequestHandler):
    @auth.required()
    async def get(self, token):
        self.write({"sub": token.sub})

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

Litestar

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",
)

@get("/protected")
@auth.required()
async def protected(token):
    return {"sub": token.sub}

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

Scopes and claim constraints

All adapters support:

  • scopes="a b c" to require scopes on a protected endpoint
  • claims={...} to enforce simple claim constraints

Example:

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

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-2602.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-2602.0.0-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: py_oidc_auth-2602.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.7

File hashes

Hashes for py_oidc_auth-2602.0.0.tar.gz
Algorithm Hash digest
SHA256 76c42df455f761e1d2d5089eabf18a5f73071f7caf7140002269a3720f658f4e
MD5 768f5de9d6e744edb67787e6358e49e9
BLAKE2b-256 10633d6203c500d906f034f807f206c7c911080ce1f804dc24a43158f2a08ba9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for py_oidc_auth-2602.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fd6795fad7aef126c142ca6bb74fb5808ec073d9cbca5c8a9bc2e69a443ae300
MD5 f57c458326e7a399ba3d25d6f445ef16
BLAKE2b-256 cf0efe2ece62b0f61abe27905133c0646294441e07d1a39987a66b386322d805

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