authlib
authlib copied to clipboard
Enhancement: Litestar OAuth Client
Is your feature request related to a problem? Please describe.
No
Describe the solution you'd like
Add an OAuth client akin to FastAPI/Starlette/Flask for the Litestar framework
- https://litestar.dev/
- https://docs.litestar.dev/latest/
- https://docs.litestar.dev/latest/usage/middleware/creating-middleware.html
- https://docs.litestar.dev/latest/usage/security/abstract-authentication-middleware.html
Describe alternatives you've considered
Rolling my own
Additional context
This would benefit the user base of Litestar and increase the usage of authlib.
Litestar seems to be able to use starlette's client, even though the types are different. Mainly the function authorize_redirect:
Current Starlette code:
authlib/integrations/starlette_client/apps.py
from starlette.datastructures import URL
from starlette.responses import RedirectResponse
# StarletteAppMixin.authorize_redirect
if redirect_uri and isinstance(redirect_uri, URL):
redirect_uri = str(redirect_uri)
return RedirectResponse(rv["url"], status_code=302)
Litestar equivalent:
from litestar.datastructures import URL
from litestar.response import Redirect
if redirect_uri and isinstance(redirect_uri, URL):
redirect_uri = str(redirect_uri)
return Redirect(rv["url"], status_code=302)
everything else seems to be fine.
MRE
from authlib.integrations.starlette_client import OAuth
from litestar import Litestar, get, Request
from litestar.testing import create_test_client
from litestar.middleware.session.server_side import ServerSideSessionConfig
from starlette.config import Config
from starlette.responses import RedirectResponse
from typing import Any
# Mock config for OAuth
config = Config(environ={
'GOOGLE_CLIENT_ID': 'fake-client-id',
'GOOGLE_CLIENT_SECRET': 'fake-client-secret',
})
# Create OAuth instance with config
oauth = OAuth(config=config)
# Register a provider
oauth.register(
name='google',
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email profile'},
)
@get('/login')
async def login(request: Request) -> RedirectResponse:
"""Actually return the Starlette RedirectResponse from handler"""
google = oauth.create_client('google')
redirect_uri = 'http://localhost/callback'
# Return the actual RedirectResponse (Starlette type!)
return await google.authorize_redirect(request, redirect_uri)
session_config = ServerSideSessionConfig()
try:
with create_test_client(
route_handlers=[login],
middleware=[session_config.middleware],
) as client:
print("Testing if Litestar can accept Starlette RedirectResponse as return type...")
response = client.get('/login', follow_redirects=False)
print(f"Success! Status: {response.status_code}")
print(f"Location: {response.headers.get('location')}")
print(f"Is redirect: {response.status_code == 302}")
except Exception as e:
import traceback
print(f"ERROR: {type(e).__name__}: {e}")
print(traceback.format_exc())
result
/home/....../.venv/lib/python3.14/site-packages/litestar/plugins/pydantic/utils.py:28: UserWarning: Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater.
from pydantic import v1 as pydantic_v1
Testing if Litestar can accept Starlette RedirectResponse as return type...
INFO - 2025-11-08 12:37:06,158 - httpx - _client - HTTP Request: GET https://accounts.google.com/.well-known/openid-configuration "HTTP/1.1 200 OK"
Success! Status: 302
INFO - 2025-11-08 12:37:06,172 - httpx - _client - HTTP Request: GET http://testserver.local/login "HTTP/1.1 302 Found"
Location: https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=fake-client-id&redirect_uri=http%3A%2F%2Flocalhost%2Fcallback&scope=openid+email+profile&state=zrhm1s9ZU4uaI7WL2Y9hhQEQD4j0gF&nonce=1fWItVCtgLeyWxBkWHhq
Is redirect: True
hope this helps.