Search code examples
pythonpython-3.xoauth-2.0python-requestsrequests-oauthlib

(How) can I send (prepared) requests with requests.OAuth2Session?


When running the following (while replacing example.com with our API, obviously)

req = Request('GET', 'https://example.com')
# client is a customized OAuth2Session
client.authorize(self.username, self.password, self.auth_key)
print(self.client.authorized) # True

the following returns <Response [200]>:

response = client.request(req.method, req.url)

But this returns <Response [401]>:

 prepped = client.prepare_request(req)
 response = client.send(prepped)

How can I reuse the original Request object when sending it through the OAuth2Session?


Solution

  • The OAuth2Session implementation doesn't override the Session.prepare_request() or Session.send() methods, only Session.request() has been specialised. That's because it handles auto-refreshing in the same method, requiring more requests to be sent out.

    To support altering those requests, the library offers a compliance hooks facility, where at specific points in the process a hook is called that can alter request details. The OAuth2Session object, since version 0.4.0, supports 3 different hooks:

    • access_token_response: passed the response from a access token request, before the response is parsed to extract the token.
    • refresh_token_response: passed the response from a refresh token request, again before the response is parsed.
    • protected_request: passed the url, headers and body of requests used to access protected resources (so a request that should include a valid token).

    Several included compliance fixers use these hooks to add missing elements from responses from certain providers, and to update outgoing requests when certain APIs deviate from the OAuth standard as to how they handle tokens.

    protected_request is the interesting hook here, as it is passed the same data you would normally want to alter when using a request.Request() / session.prepare_request() / session.send() pattern. You get to alter the same request data in slightly different packaging, before the oauthlib client gets to add the token to that data.

    That said, if you don't need to use auto-refreshing or can handle token expiration yourself, you can access the oauthlib client that the OAuth2Sesson wraps, directly. Provided you already fetched a token, you can sign your request, before preparing, with:

    from oauthlib.oauth2 import TokenExpiredError
    
    req = Request('GET', 'https://example.com')
    try:
        req.url, req.headers, req.data = client._client.add_token(
            req.url, http_method=req.method, body=req.data, headers=req.headers
        )
    except TokenExpiredError:
        # handle token expiration
        pass
    
    else:
        prepped = client.prepare_request(req)
        response = client.send(prepped)
    

    This uses the oauthlib client's add_token() method directly.