-
-
Notifications
You must be signed in to change notification settings - Fork 856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Public middleware API #345
Comments
Yes this would be a great help. Specially I'm looking for an usecase for supporting OAuth2. I want the middleware ability so that I can add logic for getting OAuth token and attach required bearer header to actual request. |
@kesavkolla Actually, the import httpx
class OAuth2:
def __init__(self, token: str):
self.token = token
def __call__(self, request):
request.headers["<oauth-header>"] = build_oauth_bearer_header(self.token)
return request
client = httpx.Client(auth=OAuth2(token="...")) |
It's not just injecting the header. There can be more auth flow that need to be executed. If token is expired need to call an API to refresh token etc... I was looking something similar to https://github.com/requests/requests-oauthlib |
Okay so, I took a look at what the The high-level API of So I think you'd have more luck subclassing An example with from httpx import ConnectionPool
class OAuth2Dispatch(ConnectionPool):
async def request(self, method, url, **kwargs):
# …Attach header and check for token expiry here…
return await super().request(method, url, **kwargs)
# Helper methods for the OAuth2 flow:
def authorization_url(self, ...): ...
async def fetch_token(self, ...): ... from httpx_oauthlib import OAuth2Dispatch
from starlette.applications import Starlette
from starlette.responses import RedirectResponse, JSONResponse
from starlette.middleware.session import SessionMiddleware
app = Starlette()
app.add_middleware(SessionMiddleware)
# This information is obtained upon registration of a new GitHub
client_id = "<your client key>"
client_secret = "<your client secret>"
authorization_base_url = 'https://github.com/login/oauth/authorize'
token_url = 'https://github.com/login/oauth/access_token'
@app.route("/login")
async def login(request):
github = OAuth2Dispatch(client_id)
authorization_url, state = github.authorization_url(authorization_base_url)
# State is used to prevent CSRF, keep this for later.
request.session['oauth_state'] = state
return RedirectResponse(url=authorization_url)
@app.route("/callback")
async def callback(request):
github = OAuth2Dispatch(client_id, state=session['oauth_state'])
token = await github.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url)
r = await github.request("GET", "https://api.github.com/user")
return JSONResponse(r.json()) |
@kesavkolla I think what you need is something like https://docs.authlib.org/en/latest/client/httpx.html Stay tuned, Authlib HTTPX feature is not released yet, it will be released in v0.13. |
Looking great @lepture! I see there's also a Starlette integration — wondering how much of it is Starlette vs plain ASGI? — looks like two nice components for implementing complex authentication schemes in async web apps. If you'd like any feedback on those integrations, feel free to ping @tomchristie, @sethmlarson, @yeraydiazdiaz or myself on a PR or issue. :) I think @tomchristie might be interested in trying out the Starlette integration for HostedAPI? (See tweet) |
@florimondmanca The Starlette integration is not async currently. It was implemented by lepture/authlib#153. I'm working on Starlette integration now to get it async since I've implemented the HTTPX async OAuth clients already. Basically, web frameworks integrations API are all the same. In your web routes: def login_via_github(request):
redirect_uri = 'your-callback-uri'
return oauth.github.authorize_redirect(request, redirect_uri)
def authorize_github(request)
token = oauth.github.authorize_access_token(request) The documentation for Starlette is not updated yet. But basically all framework integrations are the same with a little differences among each of them. So the actually documentation is https://docs.authlib.org/en/latest/client/frameworks.html |
@florimondmanca @tomchristie @sethmlarson Here is a demo for Starlette Google login with Authlib. It works with Authlib wip-starlette branch. |
Oh, I just found that I only imported one thing from starlette: from starlette.responses import RedirectResponse That means I can actually create a plain ASGI client. |
So, I'd still like to see this, yup! I think we'd go with The middleware ought to wrap around the call into Line 416 in 831e79f
I think a good tack onto a PR for this would be to start with a PR that only supports an optional single item of middleware. We can then update the PR to support multiple middleware items. (I'm keen that we keep a close eye on making sure we're keeping things as clean as possible here, and tackling it in two stages would help us review it as fully as possible.) |
This will need some significant design work. I think we should treat this as out-of-scope at this point in time, in order to get an API-stable 1.0 release squared away. |
That's how I'm feeling about this, too. We need to make sure we can safely introduce a middleware/extension API as an addition-only change, but not sure it's 100% necessary for a solid 1.0 release. |
Great. Going to close this as out-of-scope for now, for the purposes of good housekeeping. |
Problem
We currently have an internal middleware API that was brought in via #295 and #268.
We should be looking at making that API (or a different form of it?) public so that users can extend the functionality of the client to fit their needs.
For example, a retry functionality (discussed in #108 and drafted in #134 then abandoned) could probably be implemented as a middleware (basic idea here: #268 (comment)).
Questions to be answered
request, get_response -> response
. What's the client-side API going to look like? Probably something like…IMO, yes: treat those as core middleware, and add custom middleware on top. It might depend on the use case, but we can start off with that.
@sethmlarson proposed an idea inspired by Pyramid here: #295 (comment)
This was originally mentioned in #295 (comment), and would be used to store state worth remembering such as cookies or
Alt-Svc
headers for HTTP/3 (see #275 (comment)).IMO the use case still needs to be refined for us to clearly see what needs to be done.
What needs to be done
This is a proposal. :)
All thoughts welcome!
The text was updated successfully, but these errors were encountered: