Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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: 2 additions & 2 deletions example/satosa/pyeudiw_backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ config:
module: pyeudiw.trust.handler.federation
class: FederationHandler
config:
client_id: 'openid-federation:example.com'
Comment thread
PascalDR marked this conversation as resolved.
Outdated
httpc_params: *httpc_params
cache_ttl: 0
entity_configuration_exp: 600
Expand Down Expand Up @@ -265,8 +266,7 @@ config:
module: pyeudiw.trust.handler.x509
class: X509Handler
config:
# client_id: *client_id
client_id_scheme: x509_san_dns # this will be prepended in the client id scheme used in the request.
client_id: 'x509_san_dns:example.com'
Comment thread
PascalDR marked this conversation as resolved.
Outdated
include_issued_jwt_header_param: true # default false; if true, it will include x5c header parameters in the signed presentation request issued by this trust handler
certificate_authorities:
ca.example.com: |
Expand Down
8 changes: 7 additions & 1 deletion pyeudiw/satosa/default/openid4vp_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,16 @@ def pre_request_endpoint(
"internal error: something went wrong when creating your authentication request",
e500
)

qs_params = getattr(context, "qs_params") or {}
client_id_hint = qs_params.get("client_id_hint", None)
has_client_id_hint = client_id_hint is not None and self.trust_evaluator.has_client_id(
client_id_hint
)

# PAR
payload = {
"client_id": self.client_id,
"client_id": client_id_hint if has_client_id_hint else self.client_id,
"request_uri": f"{self.absolute_request_url}?id={state}",
}

Expand Down
17 changes: 17 additions & 0 deletions pyeudiw/tests/satosa/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,23 @@ def test_pre_request_without_frontend(self):
assert resp.status == "400"
assert resp.message is not None

def test_pre_request_with_client_id_hint(self, context):
context.http_headers = dict(
HTTP_USER_AGENT="Mozilla/5.0 (Linux; Android 10; SM-G960F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36"
)
context.qs_params = {"client_id_hint": f"openid_federation:{BASE_URL}/OpenID4VP"}
Comment thread
PascalDR marked this conversation as resolved.
Outdated
resp = self.backend.pre_request_endpoint(context, InternalData())

assert resp is not None
assert resp.status == "302 Found"
assert resp.message is not None

parsed = urllib.parse.unquote(
resp.message, encoding="utf-8", errors="replace"
)

assert "openid_federation:https://example.com/OpenID4VP" in parsed

def test_pre_request_endpoint(self, context):
internal_data = InternalData()
context.http_headers = dict(
Expand Down
3 changes: 2 additions & 1 deletion pyeudiw/tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def base64url_to_int(val):
"module": "pyeudiw.trust.handler.federation",
"class": "FederationHandler",
"config": {
"client_id": f"openid_federation:{BASE_URL}/OpenID4VP",
Comment thread
PascalDR marked this conversation as resolved.
Outdated
"entity_configuration_exp": 600,
"metadata": _METADATA,
"metadata_type": "openid_credential_verifier",
Expand Down Expand Up @@ -338,7 +339,7 @@ def base64url_to_int(val):
"module": "pyeudiw.trust.handler.x509",
"class": "X509Handler",
"config": {
"client_id": f"{BASE_URL}/OpenID4VP",
"client_id": f"x509_san_dns:{BASE_URL}/OpenID4VP",
Comment thread
PascalDR marked this conversation as resolved.
Outdated
"include_issued_jwt_header_param": True,
"relying_party_certificate_chains_by_ca": {
"ca.example.com": DEFAULT_X509_CHAIN,
Expand Down
15 changes: 15 additions & 0 deletions pyeudiw/trust/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,21 @@ def build_metadata_endpoints(
f"found collision in metadata endpoint: {all_paths}",
)
return endpoints

def has_client_id(self, client_id: str) -> bool:
"""
Check if the trust source has a client id.

:param issuer: The issuer
:type issuer: str

:returns: If the trust source has a client id
:rtype: bool
"""
for handler in self.handlers:
if handler.is_it_me(client_id):
return True
return False

@staticmethod
def from_config(
Expand Down
12 changes: 9 additions & 3 deletions pyeudiw/trust/handler/interface.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from typing import Any, Callable
from typing import Any, Callable, Optional

import satosa.context
import satosa.response

from pyeudiw.trust.model.trust_source import TrustSourceData
from pyeudiw.storage.db_engine import DBEngine


class TrustHandlerInterface:
Expand Down Expand Up @@ -133,14 +132,21 @@ def validate_trust_material(
:rtype: bool
"""
raise NotImplementedError

def get_client_id(self) -> Optional[str]:
"""
Return the client ID associated with this trust evaluator.
This is typically used for OAuth2 or OpenID Connect flows.
"""
return getattr(self, 'client_id', None)

def is_it_me(self, client_id: str) -> bool:
"""
Returns true if, according to this trust framework implementation,
the argument client_id refers to the implementation itself as a
*member* of the trust framework.
"""
return client_id == self.client_id
return client_id == self.get_client_id()

@property
def name(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion pyeudiw/trust/handler/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def extract_and_update_trust_materials(
self, issuer: str, trust_source: TrustSourceData
) -> TrustSourceData:
# Return the first valid chain
if issuer == self.client_id:
if issuer == self.client_id.split(":", 1)[-1]:
for ca, chain in self.relying_party_certificate_chains_by_ca.items():
crls = self._extract_crls(trust_source, chain)

Expand Down
Loading