Authlib#
Authlib is a package that profides intefaces for handling authentication through OAuth and OpenID. It also provides specific integrations with popular web frameworks.
from authlib.integrations.requests_client import OAuth2Session
Refresh token#
If you have specified the token endpoint for the session, Authlib will update the token automatically.
For more details check the Refresh & Auto Update Token page of the official documentation.
The following cell defines the client and requests the initial token.
token_endpoint = (
"http://localhost:8080/realms/master"
"/protocol/openid-connect/token"
)
client = OAuth2Session(
client_id="admin-cli",
leeway=60, # keycloak have token lifetime 60 by default
token_endpoint=token_endpoint
)
initial_token = client.fetch_token(
token_endpoint,
username="admin",
password="admin",
grant_type="password"
)
The following cell shows the request the client generates. Check the Authorization attribute.
client.get("http://localhost:3232")
<Response [200]>
GET / HTTP/1.1
Host: localhost:3232
User-Agent: python-requests/2.32.5
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJscV9OdmVDaUFpanByYTU4YXR2QklCZzM5NVgtb0MyMFo1aW5kN2Z4ZUtVIn0.eyJleHAiOjE3NzE4MzQ3MzAsImlhdCI6MTc3MTgzNDY3MCwianRpIjoib25sdHJ0OmRlZWNmMTBmLWMzZjktN2JiZS1jMDA4LWQ2YzBhYTVhNTcxNCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvbWFzdGVyIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2lkIjoiZjZmMWY0YzMtY2FkZC0yYWRiLWEyYTMtMDk0ZGFhM2ViNDYxIiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIn0.Y0kZfOzoyz8Xg_GhUnULLbGLIYY-5EmLjwDvwFoIx9lAcl2b28QfpBbUQL5XrBnnSrapBBxULZBns4JsSPxVwEje-WrVi9jUmthhpZG9Myqj9bVWOhRLmRhxyQLdOetqY7-0uj27kSAogjt6rKmGPuJ2ikr4CmlC-8O-VUNw4oP4tZgPfOC7rSrMO9wxNN-Tpys8wOEvkimmd_TdVrHj_Es5shBSBfbwNKtX_EuWy2cv9Ao_oaRYgeY6wgXD2KBO_LiNnjMuXWB9hAe34ogyTRK213dhdesUqdKTYnV1m9m2HJLGXJR1_ZFWAQFbB2RKHqOR4toz7W6-p6Kc3-Sqdg
After some time, the same request will generate a different token.
client.get("http://localhost:3232")
<Response [200]>
GET / HTTP/1.1
Host: localhost:3232
User-Agent: python-requests/2.32.5
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJscV9OdmVDaUFpanByYTU4YXR2QklCZzM5NVgtb0MyMFo1aW5kN2Z4ZUtVIn0.eyJleHAiOjE3NzE4MzQ3NDEsImlhdCI6MTc3MTgzNDY4MSwianRpIjoib25sdHJ0OjU5YmJmMjM1LTAwNTQtNjdmNC1kMDA0LTJmNDVjOGNlYjNiYyIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9yZWFsbXMvbWFzdGVyIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2lkIjoiZjZmMWY0YzMtY2FkZC0yYWRiLWEyYTMtMDk0ZGFhM2ViNDYxIiwic2NvcGUiOiJlbWFpbCBwcm9maWxlIn0.NAV7w0GDP2_4rP51NqwMkSbJygYYdP7C9dY3SAj6VLjmi2PC5aV0OHzuClw1eBKR3_hZEeqbSIJjg9x-OSkhK8Uk1rB4FQfnP1JyulycYE4lphqcV6Kx9Xvhvyw8uEixV0jNZRCNdG30-3IwBobA07aWJ5HbyBSzy5QTIPCdXpt4GW1ps2zeJf_tciVNuVovkrQQNyczUZAVWzb8-pZ2CYpmeS26IHzDdJgcCOM3CH9wdKVGYYh5IPgo2hHJqslJ3DpjHXoz__gIKqZEEbEhsGBc92EQb-PAPNRecvd7FbncUuFbDtm9izvkQgoiWRvyJVP7mcGcP5cxEUgBWc0eKQ
Removed session#
Consider what happens when a session is removed and the application attempts to refresh the token. Typicallyk, OAuth returns the 400 error.
The following cell requests the token that creates a new Keycloak sesion.
client = OAuth2Session(client_id="admin-cli")
token = client.fetch_token(
"http://localhost:8080/realms/master/protocol/openid-connect/token",
username="admin",
password="admin",
grant_type="password",
)
The text code removes the session, emitating that session was accidentally dropped.
access_token = token["access_token"]
session_state = token["session_state"]
!curl -X DELETE \
-H "Authorization: Bearer $access_token" \
"http://localhost:8080/admin/realms/master/sessions/$session_state"
Requesting a token refresh results in OAuthError with the relevant description.
try:
client.fetch_token(
"http://localhost:8080/realms/master/protocol/openid-connect/token",
grant_type="refresh_token",
refresh_token=token["refresh_token"]
)
except Exception as e:
print(type(e))
print(e)
<class 'authlib.integrations.base_client.errors.OAuthError'>
invalid_grant: Session not active
Requests integration#
The authlib provides the authlib.integrations.requests_client.OAuth2Session class - the custom requests.Session to which the authentication logic is included.
The following cell shows the mro of authlib.integrations.requests_client.
OAuth2Session.__mro__
(authlib.integrations.requests_client.oauth2_session.OAuth2Session,
authlib.oauth2.client.OAuth2Client,
requests.sessions.Session,
requests.sessions.SessionRedirectMixin,
object)
The list of ancestors includes requests.sessions.Session.
This allows to use the OAuth2Session instance as a regular instance of requests.sessions.Session and not to care about the injecting the authentication information to the header.
The following code uses the get method of the OAuth2Session:
client = OAuth2Session(client_id="admin-cli", leeway=30)
client.fetch_token(
"http://localhost:8080/realms/master/protocol/openid-connect/token",
username="admin",
password="admin",
grant_type="password",
)
response = client.get("http://localhost:8080/admin/realms/master/users/count")
response.status_code, response.text
(200, '1')