I want to use an API that is authenticated with the OAuth2
client_credentials flow from Python.
In pyhton the most widely used HTTP client is Requests, and Requests
has many advanced features and extensions, some of which revolve around using it with OAuth2
.
However, Oauth2 is a complex beast that support 4 different flows, of which client_credentials is the simplest. Try as I might, I have not found a simple to-the-point example of using Requests
with the client_credentials flow. It is always mentioned at the end of the article as a sidenote or afterthought after explaining all the othe flows in intricated detail.
In client_credentials flow, the following will happen:
{
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "all", # Or whatever scope you care about
}
where the client_id and client_secret are provided by the service you are accessing.
{
"token_type":"Bearer",
"scope":"openid email profile offline_access roles read:tickets read:customers ...",
"access_token":"XXX_THIS_TOKEN_IS_VERY_SECRET_XXX"
}
You use it by setting it in the Authorization:
Bearer header in all requests to protected resources in the following way:
Authorization: Bearer XXX_THIS_TOKEN_IS_VERY_SECRET_XXX
The token will last 1 hour from being generated and when a resource request returns 401 auth errors, simply repeat steps #1 and #2 to generate a new token to use.
My current code manually requests a token from the token endpoint, parses the token from the resulting json and stuffs that into subsequent requests' HTTP headers. I then proceed to detect 401 errors and re-authenticate. This works but it is very tediouos. I now have to wrap every request usage with this ugly mess.
I was hoping there would be a way to do it similar to this:
client = MagicRequestsWrapper(client_id=client_id, client_secret=client_secret, token_endpoint=token_endpoint, scope=scope)
res = client.get(protected_resource_endpoint)
where all the oauth stuff is now hidden and client behaves exactly like a normal Requests client.
How can I do that?
I found the answer shortly after writing this question.
import requests
from requests_oauth2client import OAuth2Client, OAuth2ClientCredentialsAuth
def oauth2_session(client_id, client_secret, base_url, scope="all"):
token_url = f"{base_url}/auth/token" # This may be different for your endpoint
oauth2client = OAuth2Client( token_endpoint=token_url, client_id=client_id, client_secret=client_secret)
auth = OAuth2ClientCredentialsAuth( oauth2client, scope=scope, resource=base_url)
session = requests.Session()
session.auth = auth
return session
Example usage as follows:
# Create a session
base_url = "https://myservice.com"
my_session = oauth2_session(client_id=os.environ.get("CLIENT_ID"), client_secret=os.environ.get("CLIENT_SECRET"), base_url=base_url)
# Use it as normal requests object to post and get
result = my_session.get(f"{base_url}/api/some_resource")
my_session.post(f"{base_url}/api/some_resource", json={"test":123})