Skip to main content

Shared authentication library for remem Python services

Project description

remem-auth

Shared authentication library for remem Python services. Supports JWT verification (Azure Entra ID / Google) and static bearer tokens, with out-of-the-box integrations for FastAPI and FastMCP.

Installation

# Core library (JWT verification + static tokens)
pip install remem-auth

# With FastAPI integration
pip install remem-auth[fastapi]

# With FastMCP integration
pip install remem-auth[fastmcp]

# All optional dependencies
pip install remem-auth[all]

Requires Python 3.11+.

Quick Start

1. Configure Environment Variables

All settings are read from environment variables with the REMEM_AUTH_ prefix:

Variable Description Default
REMEM_AUTH_AZURE_TENANT_ID Azure Entra ID tenant ID ""
REMEM_AUTH_AZURE_CLIENT_ID Azure app registration client ID ""
REMEM_AUTH_GOOGLE_CLIENT_ID Google OAuth client ID ""
REMEM_AUTH_STATIC_TOKENS Comma-separated static bearer tokens ""
REMEM_AUTH_IDPS_JSON Advanced: full JSON array of IdP configs ""
REMEM_AUTH_VERIFY_EXP Whether to verify token expiration True
REMEM_AUTH_VERIFY_AUD Whether to verify audience True

Example — Azure Entra ID:

export REMEM_AUTH_AZURE_TENANT_ID="your-tenant-id"
export REMEM_AUTH_AZURE_CLIENT_ID="your-client-id"

Example — Static tokens (useful for dev/test):

export REMEM_AUTH_STATIC_TOKENS="dev-token-1,dev-token-2"

Example — Custom IdP via JSON:

export REMEM_AUTH_IDPS_JSON='[{"name":"my-idp","issuer":"https://idp.example.com","jwks_uri":"https://idp.example.com/.well-known/jwks.json","audience":"my-app"}]'

2. FastAPI Integration

from fastapi import Depends, FastAPI
from remem.auth import AuthConfig, AuthenticatedUser, FastAPIAuth

config = AuthConfig()  # reads from environment variables
auth = FastAPIAuth(config)
app = FastAPI()

@app.get("/protected")
def protected(user: AuthenticatedUser = Depends(auth)):
    return {"subject": user.subject, "email": user.email}

Unauthenticated requests receive a 401 Unauthorized response with a WWW-Authenticate: Bearer header.

OpenAI-compatible error format:

For services that need to return errors in the OpenAI error format, pass error_format="openai" and call install() on the app:

from fastapi import Depends, FastAPI
from remem.auth import AuthConfig, AuthenticatedUser, ErrorFormat, FastAPIAuth

config = AuthConfig()
auth = FastAPIAuth(config, error_format="openai")
app = FastAPI()
auth.install(app)  # registers the OpenAI error response handler

@app.get("/protected")
def protected(user: AuthenticatedUser = Depends(auth)):
    return {"subject": user.subject}

Authentication failures will return:

{
  "error": {
    "message": "No token provided",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_api_key"
  }
}

If install() is not called, the error gracefully falls back to the default {"detail": "..."} format.

3. FastMCP Integration

from fastmcp import FastMCP
from remem.auth import AuthConfig, FastMCPAuthProvider

config = AuthConfig()
mcp = FastMCP("my-server", auth=FastMCPAuthProvider(config))

@mcp.tool()
def hello() -> str:
    return "world"

FastMCPAuthProvider implements FastMCP's TokenVerifier interface and uses asyncio.to_thread() internally so the synchronous verifier doesn't block the event loop.

4. Using the Core API Directly

If you're not using either framework, you can call the verification engine directly:

from remem.auth import AuthConfig, AuthVerifier, AuthenticationError

config = AuthConfig()
verifier = AuthVerifier(config)

try:
    user = verifier.verify_token("eyJhbGciOi...")
    print(user.subject, user.email, user.auth_method)
except AuthenticationError as e:
    print(f"Authentication failed: {e}")

Extracting tokens from request headers:

from remem.auth import extract_token

# Works with any object that has a .headers attribute (Mapping[str, str])
token = extract_token(request)

extract_token checks Authorization: Bearer <token> first, then falls back to the api-key header.

Core Concepts

Verification Flow

AuthVerifier.verify_token() processes tokens in this order:

  1. Auth not enabled — returns an anonymous user (graceful degradation)
  2. No token — raises AuthenticationError
  3. Matches a static token — returns a static-token user (O(1) set lookup)
  4. JWT — peeks the issuer from an unverified decode, then routes to the matching IdP verifier

AuthenticatedUser

The user object returned after successful authentication:

class AuthenticatedUser(BaseModel):
    subject: str                     # JWT "sub" claim
    email: str | None                # extracted from email / preferred_username / upn
    name: str | None                 # JWT "name" claim
    auth_method: AuthMethod          # "jwt" | "static" | "none"
    idp_name: str | None             # IdP name (e.g. "azure", "google")
    claims: dict[str, Any]           # full JWT payload
    token: str                       # raw token (excluded from serialization)

The token field is marked with exclude=True and repr=False, so it won't appear in .model_dump() output or print() — preventing accidental credential leaks.

Graceful Degradation

When no IdPs or static tokens are configured, auth_enabled is False and all requests are allowed through with an anonymous user. This lets you omit auth configuration in development environments.

API Reference

Module Exports

from remem.auth import (
    AuthConfig,                # pydantic-settings configuration
    AuthVerifier,              # core verification engine
    AuthenticationError,       # verification failure exception
    AuthenticatedUser,         # user model
    AuthMethod,                # authentication method enum
    ErrorFormat,               # error response format enum
    IdpConfig,                 # identity provider config model
    extract_token,             # extract token from request headers
    create_auth_config_from_env,  # AuthConfig factory function
    FastAPIAuth,               # FastAPI dependency (lazy import)
    FastMCPAuthProvider,       # FastMCP auth provider (lazy import)
)

FastAPIAuth and FastMCPAuthProvider are lazy-imported via __getattr__ — their framework dependencies are only loaded when actually accessed.

IdpConfig Fields

When using REMEM_AUTH_IDPS_JSON to define custom IdPs, each entry supports:

Field Type Required Default Description
name str Yes IdP identifier
issuer str Yes JWT issuer (used for routing)
jwks_uri str Yes JWKS endpoint URL
audience str | None No None Expected audience
algorithms list[str] No ["RS256"] Allowed signing algorithms

Development

# Create a virtualenv and install dev dependencies
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Lint
ruff check src/ tests/

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

remem_auth-0.2.0.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

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

remem_auth-0.2.0-py3-none-any.whl (10.9 kB view details)

Uploaded Python 3

File details

Details for the file remem_auth-0.2.0.tar.gz.

File metadata

  • Download URL: remem_auth-0.2.0.tar.gz
  • Upload date:
  • Size: 8.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for remem_auth-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b7a408513fc2438ea4464ce2c5c879468d414e7bdfd76a59b49f734435ab92d1
MD5 210e931303671f927e3a747f894807ed
BLAKE2b-256 bd7029532401de9efa4af38e1ff394bd406381e1e8031d7d55d19d22fd4b477e

See more details on using hashes here.

File details

Details for the file remem_auth-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: remem_auth-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for remem_auth-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 df983cb939f4c4cca94ad847f6a5888a5ed90a73e660b7efb31da48816c8721f
MD5 4b32d98ad8c5a8c51c2b7c722fef5d9f
BLAKE2b-256 e62fe197954a856bbdcdf66f87c6b579dfac75d481017aa66002ccf1e96f3169

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