tomplus / kubernetes_asyncio

Python asynchronous client library for Kubernetes http://kubernetes.io/
Apache License 2.0
364 stars 71 forks source link

OIDC token refresh response may not include a new refresh_token #296

Open kieran-bellew opened 11 months ago

kieran-bellew commented 11 months ago

The client should not assume a refresh token is returned in an OpenID token refresh request as there several token management strategies like "sliding", "rolling" and "rotation".

Traceback:

in __aenter__
    self._client: client.ApiClient = await config.new_client_from_config(
  File ".../.venv/lib/python3.8/site-packages/kubernetes_asyncio/config/kube_config.py", line 639, in new_client_from_config
    await load_kube_config(config_file=config_file, context=context,
  File ".../.venv/lib/python3.8/site-packages/kubernetes_asyncio/config/kube_config.py", line 575, in load_kube_config
    await loader.load_and_set(client_configuration)
  File ".../.venv/lib/python3.8/site-packages/kubernetes_asyncio/config/kube_config.py", line 379, in load_and_set
    await self._load_authentication()
  File ".../.venv/lib/python3.8/site-packages/kubernetes_asyncio/config/kube_config.py", line 206, in _load_authentication
    await self._load_oid_token()
  File ".../.venv/lib/python3.8/site-packages/kubernetes_asyncio/config/kube_config.py", line 274, in _load_oid_token
    await self._refresh_oidc(provider)
  File ".../.venv/lib/python3.8/site-packages/kubernetes_asyncio/config/kube_config.py", line 301, in _refresh_oidc
    provider['config'].value['refresh-token'] = resp['refresh_token']
KeyError: 'refresh_token'

Current workaround:

try:
    self._client: ApiClient = await config.new_client_from_config(
        context=self.cluster_name, persist_config=False
    )
except KeyError:
    kconf = config.kube_config.KubeConfigMerger(
        os.environ.get("KUBECONFIG", "~/.kube/config")
    )
    context = kconf.config["contexts"].get_with_name(self.cluster_name)[
        "context"
    ]
    provider_config = kconf.config["users"].get_with_name(context["user"])[
        "user"
    ]["auth-provider"]["config"]
    requestor = config.kube_config.OpenIDRequestor(
        provider_config["client-id"],
        provider_config["client-secret"],
        provider_config["idp-issuer-url"],
    )
    resp = await requestor.refresh_token(provider_config["refresh-token"])
    provider_config.value["id-token"] = resp["id_token"]
    kconf.save_changes()
    self._client: ApiClient = await config.new_client_from_config_dict(
        config_dict=kconf.config, context=self.cluster_name
    )

This workaround is limited though, since if a token refresh is needed after a request failure and the client has been initialized, then the built-in retry mechanisms that refresh id tokens won't work.