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')