From bd25c88fc04d106969fdbe90cc0a0ee7d8c9b27c Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Fri, 15 Dec 2023 16:30:24 +0100 Subject: [PATCH] Add caching for OpenId tokens --- bitpoll/base/openid.py | 53 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/bitpoll/base/openid.py b/bitpoll/base/openid.py index 0a8d6d0..85ede22 100644 --- a/bitpoll/base/openid.py +++ b/bitpoll/base/openid.py @@ -4,6 +4,9 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import Group +from django.core.cache import cache +from simple_openid_connect.data import TokenSuccessResponse +from simple_openid_connect.integrations.django.apps import OpenidAppConfig from simple_openid_connect.integrations.django.models import OpenidUser from simple_openid_connect.integrations.django.user_mapping import UserMapper @@ -39,16 +42,46 @@ def automap_user_attrs(self, user, user_data): def refresh_group_users(group: Group): # get users from openid # request token - response = requests.post( - settings.OPENID_ISSUER + "/protocol/openid-connect/token", - data={ - "grant_type": "client_credentials", - "client_id": settings.OPENID_CLIENT_ID, - "client_secret": settings.OPENID_CLIENT_SECRET, - "scope": "openid profile email roles", - }, - ) - access_token = response.json()["access_token"] + + CACHE_KEY_ACCESS_TOKEN = "bitpoll.oidc_access_token" + CACHE_KEY_REFRESH_TOKEN = "bitpoll.oidc_refresh_token" + + def oidc_expiry2cache_expiry(n: int) -> int | None: + """ + Openid encodes *never-expires* as `0` while django treats `0` as don't cache. + This function rewrites `0` to `None` which is the django representation for *never-expires*. + """ + if n == 0: + return None + else: + return n + + oidc_client = OpenidAppConfig.get_instance().get_client() + access_token = cache.get(CACHE_KEY_ACCESS_TOKEN) + if access_token is None: + refresh_token = cache.get(CACHE_KEY_REFRESH_TOKEN) + if refresh_token is None: + # get completely new tokens + token_response = oidc_client.client_credentials_grant.authenticate() + else: + # use the cached refresh token to get a new access token + token_response = oidc_client.exchange_refresh_token(refresh_token) + + # save the new tokens + assert isinstance(token_response, TokenSuccessResponse), f"Could not get new tokens: {token_response}" + access_token = token_response.access_token + cache.set( + key=CACHE_KEY_ACCESS_TOKEN, + value=token_response.access_token, + timeout=oidc_expiry2cache_expiry(token_response.expires_in), + ) + if token_response.refresh_token: + cache.set( + key=CACHE_KEY_REFRESH_TOKEN, + value=token_response.refresh_token, + timeout=oidc_expiry2cache_expiry(token_response.refresh_expires_in), + ) + # get group id response = requests.get( settings.OPENID_API_BASE + "/groups?exact=true&search=" + quote(group.name),