authlib icon indicating copy to clipboard operation
authlib copied to clipboard

Enhancement: Litestar OAuth Client

Open JacobCoffee opened this issue 2 years ago • 1 comments

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.

JacobCoffee avatar Dec 06 '23 17:12 JacobCoffee

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.

zhangxingeng avatar Nov 08 '25 17:11 zhangxingeng