Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add metadata_store to context #438

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/satosa/backends/apple.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
from oic.oauth2.message import Message
from oic.oic.message import AuthorizationResponse
import satosa.logging_util as lu
from .oauth import _get_metadata_to_decorate
from ..context import Context
from ..exception import SATOSAAuthenticationError

import json
import requests

Expand Down Expand Up @@ -110,6 +113,7 @@ def response_endpoint(self, context, *args):
raise SATOSAAuthenticationError(context.state, "No user info available.")

all_user_claims = dict(list(userinfo.items()) + list(id_token_claims.items()))
context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))

# convert "string or Boolean" claims to actual booleans
for bool_claim_name in ["email_verified", "is_private_email"]:
Expand Down
3 changes: 3 additions & 0 deletions src/satosa/backends/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from oic.oauth2.message import AuthorizationResponse

from satosa.backends.oauth import _OAuthBackend
from .oauth import _get_metadata_to_decorate
from satosa.context import Context
from satosa.internal import AuthenticationInformation
from satosa.internal import InternalData
from satosa.response import Redirect
Expand Down Expand Up @@ -99,6 +101,7 @@ def _authn_response(self, context):
internal_response.attributes = self.converter.to_internal(
self.external_type, user_info)
internal_response.subject_id = str(user_info[self.user_id_attr])
context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))
return self.auth_callback_func(context, internal_response)

def user_information(self, access_token):
Expand Down
3 changes: 3 additions & 0 deletions src/satosa/backends/linkedin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from oic.oauth2.message import AuthorizationResponse

from satosa.backends.oauth import _OAuthBackend
from .oauth import _get_metadata_to_decorate
from satosa.context import Context
from satosa.internal import AuthenticationInformation
from satosa.internal import InternalData
from satosa.response import Redirect
Expand Down Expand Up @@ -110,6 +112,7 @@ def _authn_response(self, context):
self.external_type, user_info)

internal_response.subject_id = user_info[self.user_id_attr]
context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))
return self.auth_callback_func(context, internal_response)

def user_information(self, access_token, api):
Expand Down
21 changes: 21 additions & 0 deletions src/satosa/backends/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from oic.utils.authn.authn_context import UNSPECIFIED

import satosa.logging_util as lu
from satosa.context import Context
from satosa.internal import AuthenticationInformation
from satosa.internal import InternalData
from satosa.exception import SATOSAAuthenticationError
Expand Down Expand Up @@ -145,6 +146,7 @@ def _authn_response(self, context):
internal_response = InternalData(auth_info=self.auth_info(context.request))
internal_response.attributes = self.converter.to_internal(self.external_type, user_info)
internal_response.subject_id = user_info[self.user_id_attr]
context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))
return self.auth_callback_func(context, internal_response)

def auth_info(self, request):
Expand Down Expand Up @@ -331,3 +333,22 @@ def get_metadata_desc_for_oauth_backend(entity_id, config):

metadata_description.append(description)
return metadata_description


def _get_metadata_to_decorate(config):
metadata_dict = {}
if "entity_info" in config:
entity_info = config["entity_info"]
if "ui_info" in entity_info:
ui_info = entity_info["ui_info"]
for name in ui_info.get("display_name", []):
if name[1] == "en":
metadata_dict["client_name"] = name[0]
metadata_dict["client_name#" + name[1]] = name[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a convention that is defined somewhere?
(meaning the <attr>#{language} notation)

Copy link
Contributor

@melanger melanger Jul 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://openid.net/specs/openid-connect-registration-1_0.html#LanguagesAndScripts

Human-readable Client Metadata values and Client Metadata values that reference human-readable values MAY be represented in multiple languages and scripts. For example, values such as client_name, tos_uri, policy_uri, logo_uri, and client_uri might have multiple locale-specific values in some Client registrations.

To specify the languages and scripts, BCP47 [RFC5646] language tags are added to Client Metadata member names, delimited by a # character. The same syntax is used for representing languages and scripts for Client Metadata as is used for Claims, as described in Section 5.2 (Claims Languages and Scripts) of OpenID Connect Core 1.0 [OpenID.Core].

If such a human-readable field is sent without a language tag, parties using it MUST NOT make any assumptions about the language, character set, or script of the string value, and the string value MUST be used as-is wherever it is presented in a user interface. To facilitate interoperability, it is RECOMMENDED that any human-readable fields sent without language tags contain values suitable for display on a wide variety of systems.

for logo in ui_info.get("logo", []):
if logo["lang"] == "en":
metadata_dict["logo_uri"] = logo["image"]
metadata_dict["logo_width"] = logo["width"]
metadata_dict["logo_height"] = logo["height"]
metadata_dict["logo_uri#" + logo["lang"]] = logo["image"]
return metadata_dict
3 changes: 3 additions & 0 deletions src/satosa/backends/openid_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from ..exception import SATOSAAuthenticationError
from ..exception import SATOSAError
from ..exception import SATOSAMissingStateError
from .oauth import _get_metadata_to_decorate
from ..context import Context
from ..response import Redirect


Expand Down Expand Up @@ -242,6 +244,7 @@ def response_endpoint(self, context, *args):
logger.error(logline)
raise SATOSAAuthenticationError(context.state, "No user info available.")

context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))
all_user_claims = dict(list(userinfo.items()) + list(id_token_claims.items()))
msg = "UserInfo: {}".format(all_user_claims)
logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
Expand Down
3 changes: 3 additions & 0 deletions src/satosa/backends/orcid.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
from oic.utils.authn.authn_context import UNSPECIFIED
from oic.oauth2.consumer import stateID
from oic.oauth2.message import AuthorizationResponse
from .oauth import _get_metadata_to_decorate

from satosa.backends.oauth import _OAuthBackend
from satosa.context import Context
from satosa.internal import InternalData
from satosa.internal import AuthenticationInformation
from satosa.util import rndstr
Expand Down Expand Up @@ -79,6 +81,7 @@ def _authn_response(self, context):
internal_response.attributes = self.converter.to_internal(
self.external_type, user_info)
internal_response.subject_id = user_info[self.user_id_attr]
context.decorate(Context.KEY_METADATA_STORE, _get_metadata_to_decorate(self.config))
return self.auth_callback_func(context, internal_response)

def user_information(self, access_token, orcid, name=None):
Expand Down