In some OpenID Connect providers (e.g. Microsoft Entra ID / Azure AD), the userinfo_endpoint returns a limited subset of the claims that are already present in the id_token.
Currently, pas.plugins.oidc retrieves the id_token claims from the token response, but if a userinfo_endpoint is configured, the call to the endpoint replaces the previously extracted claims.
This means that attributes available in the id_token may be lost if they are not also returned by the userinfo endpoint.
I am not sure if this is an issue with the configuration of my provider or if I did something wrong, but I overcome this issue by patching the get_user_info function:
def get_user_info(client, state, args, method="POST") -> message.OpenIDSchema | dict:
resp = client.do_access_token_request(
state=state,
request_args=args,
authn_method="client_secret_basic",
)
user_info = {}
if isinstance(resp, message.AccessTokenResponse):
# If it's an AccessTokenResponse the information in the response will be stored
# in the client instance with state as the key for future use.
id_token = user_info = resp.to_dict().get("id_token", {})
if client.userinfo_endpoint:
# https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
try:
user_info = client.do_user_info_request(method=method, state=state)
except RequestError as exc:
logger.error(
"Authentication failed, probably missing openid scope",
exc_info=exc,
)
user_info = {}
# <monkey>
_name_mapping = {
"foo": "bar"
}
_extended_user_info = {
_name_mapping.get(key, key): id_token.get(key)
for key in (
"foo",
"preferred_username",
)
if key in id_token and key not in user_info
}
user_info.update(_extended_user_info)
# </monkey>
# userinfo in an instance of OpenIDSchema or ErrorResponse
# It could also be dict, if there is no userinfo_endpoint
if not (user_info and isinstance(user_info, message.OpenIDSchema | dict)):
logger.error(
"Authentication failed, invalid response %s %s", resp, user_info
)
user_info = {}
elif isinstance(resp, message.TokenErrorResponse):
logger.error("Token error response: %s", resp.to_json())
else:
logger.error("Authentication failed %s", resp)
return user_info
It would be nice to have that mapping configurable in the plugin properties.
In some OpenID Connect providers (e.g. Microsoft Entra ID / Azure AD), the
userinfo_endpointreturns a limited subset of the claims that are already present in theid_token.Currently,
pas.plugins.oidcretrieves theid_tokenclaims from the token response, but if auserinfo_endpointis configured, the call to the endpoint replaces the previously extracted claims.This means that attributes available in the
id_tokenmay be lost if they are not also returned by the userinfo endpoint.I am not sure if this is an issue with the configuration of my provider or if I did something wrong, but I overcome this issue by patching the
get_user_infofunction:It would be nice to have that mapping configurable in the plugin properties.