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.

As a library, remem-auth does not load .env files by default — the environment variable loading strategy is the consumer's responsibility. Common approaches:

# Option 1: Use load_dotenv() in your app's entrypoint
from dotenv import load_dotenv
load_dotenv()
config = AuthConfig()

# Option 2: Explicitly point AuthConfig to a .env file
config = AuthConfig(_env_file=".env")

# Option 3: Rely on system/container-injected env vars (no .env needed)
config = AuthConfig()

Available Variables

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 — raises AuthenticationError (auth configuration is required)
  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.

Auth Required

When no IdPs or static tokens are configured, auth_enabled is False and all requests are rejected with an AuthenticationError. This ensures authentication is always explicitly configured before a service accepts requests.

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.3.2.tar.gz (8.2 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.3.2-py3-none-any.whl (11.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: remem_auth-0.3.2.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","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.3.2.tar.gz
Algorithm Hash digest
SHA256 3cdcf332adb0dcbd612af3101b8a4ba2c4c5d161d967f912e7d857c12451e6f6
MD5 9529b7479a53cac34c2a7e13981373b4
BLAKE2b-256 66411151744b89a6ad8814f0e74636cb46e4cefdd0a993b947e7837aa9e2c2a0

See more details on using hashes here.

File details

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

File metadata

  • Download URL: remem_auth-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 11.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","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.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 567acd4576118a72dd6160c17aadf037a275c7678e44e6db47d9becfe15743b1
MD5 1b5a48ed918cee5b5f52d0653d348192
BLAKE2b-256 c9b09c787191139b03510161af0996e40d94b4481ed8e160a9c69909fbf11cc5

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