Skip to content

Commit ae942d4

Browse files
authored
Merge pull request #99 from IdentityPython/upstream_get
Upstream get
2 parents 2dfb25d + f5c8c3a commit ae942d4

34 files changed

+381
-133
lines changed

src/idpyoidc/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__author__ = "Roland Hedberg"
2-
__version__ = "4.0.0"
2+
__version__ = "4.1.0"
33

44
VERIFIED_CLAIM_PREFIX = "__verified"
55

src/idpyoidc/client/claims/transform.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,16 @@ def supported_to_preferred(
6262
_pref_val = preference.get(key) # defined in configuration
6363
_info_val = info.get(key)
6464
if _info_val:
65-
# Only use provider setting if less or equal to what I support
66-
if key.endswith("supported"): # list
67-
preference[key] = [x for x in _pref_val if x in _info_val]
65+
if isinstance(_info_val, bool):
66+
if _info_val is False and _pref_val is True:
67+
# Turn off support if server doesn't support
68+
preference[key] = _info_val
6869
else:
69-
pass
70+
# Only use provider setting if less or equal to what I support
71+
if key.endswith("supported"): # list
72+
preference[key] = [x for x in _pref_val if x in _info_val]
73+
else:
74+
pass
7075
elif val is None: # No default, means the RP does not have a preference
7176
# if key not in ['jwks_uri', 'jwks']:
7277
pass

src/idpyoidc/client/client_auth.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,9 +520,7 @@ def _get_audience_and_algorithm(self, context, keyjar, **kwargs):
520520

521521
def _construct_client_assertion(self, service, **kwargs):
522522
_context = service.upstream_get("context")
523-
_entity = service.upstream_get("entity")
524-
if _entity is None:
525-
_entity = service.upstream_get("unit")
523+
_entity = service.upstream_get("unit")
526524

527525
_keyjar = service.upstream_get("attribute", "keyjar")
528526
audience, algorithm = self._get_audience_and_algorithm(_context, _keyjar, **kwargs)

src/idpyoidc/client/entity.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ def __init__(
145145
)
146146

147147
self.setup_client_authn_methods(config)
148-
149148
self.upstream_get = upstream_get
150149

151150
def get_services(self, *arg):
@@ -170,8 +169,8 @@ def get_service_by_endpoint_name(self, endpoint_name, *arg):
170169

171170
return None
172171

173-
def get_entity(self):
174-
return self
172+
# def get_entity(self):
173+
# return self
175174

176175
def get_client_id(self):
177176
_val = self.context.claims.get_usage("client_id")

src/idpyoidc/client/oauth2/add_on/dpop.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44
from typing import Optional
55

66
from cryptojwt.jwk.jwk import key_from_jwk_dict
7-
from cryptojwt.jws.jws import JWS
87
from cryptojwt.jws.jws import factory
8+
from cryptojwt.jws.jws import JWS
99
from cryptojwt.key_bundle import key_by_alg
1010

11+
from idpyoidc.client.client_auth import BearerHeader
12+
from idpyoidc.client.client_auth import find_token_info
1113
from idpyoidc.client.service_context import ServiceContext
14+
from idpyoidc.message import Message
1215
from idpyoidc.message import SINGLE_OPTIONAL_STRING
1316
from idpyoidc.message import SINGLE_REQUIRED_INT
1417
from idpyoidc.message import SINGLE_REQUIRED_JSON
1518
from idpyoidc.message import SINGLE_REQUIRED_STRING
16-
from idpyoidc.message import Message
1719
from idpyoidc.metadata import get_signing_algs
1820
from idpyoidc.time_util import utc_time_sans_frac
1921

@@ -91,13 +93,13 @@ def verify_header(self, dpop_header) -> Optional["DPoPProof"]:
9193

9294

9395
def dpop_header(
94-
service_context: ServiceContext,
95-
service_endpoint: str,
96-
http_method: str,
97-
headers: Optional[dict] = None,
98-
token: Optional[str] = "",
99-
nonce: Optional[str] = "",
100-
**kwargs
96+
service_context: ServiceContext,
97+
service_endpoint: str,
98+
http_method: str,
99+
headers: Optional[dict] = None,
100+
token: Optional[str] = "",
101+
nonce: Optional[str] = "",
102+
**kwargs
101103
) -> dict:
102104
"""
103105
@@ -159,7 +161,7 @@ def dpop_header(
159161
return headers
160162

161163

162-
def add_support(services, dpop_signing_alg_values_supported):
164+
def add_support(services, dpop_signing_alg_values_supported, with_dpop_header=None):
163165
"""
164166
Add the necessary pieces to make pushed authorization happen.
165167
@@ -185,3 +187,53 @@ def add_support(services, dpop_signing_alg_values_supported):
185187
_userinfo_service = services.get("userinfo")
186188
if _userinfo_service:
187189
_userinfo_service.construct_extra_headers.append(dpop_header)
190+
# To be backward compatible
191+
if with_dpop_header is None:
192+
with_dpop_header = ["userinfo"]
193+
194+
# Add dpop HTTP header to these
195+
for _srv in with_dpop_header:
196+
if _srv == "accesstoken":
197+
continue
198+
_service = services.get(_srv)
199+
if _service:
200+
_service.construct_extra_headers.append(dpop_header)
201+
202+
203+
class DPoPClientAuth(BearerHeader):
204+
tag = "dpop_client_auth"
205+
206+
def construct(self, request=None, service=None, http_args=None, **kwargs):
207+
"""
208+
Constructing the Authorization header. The value of
209+
the Authorization header is "Bearer <access_token>".
210+
211+
:param request: Request class instance
212+
:param service: The service this authentication method applies to.
213+
:param http_args: HTTP header arguments
214+
:param kwargs: extra keyword arguments
215+
:return:
216+
"""
217+
218+
_token_type = "access_token"
219+
220+
_token_info = find_token_info(request, _token_type, service, **kwargs)
221+
222+
if not _token_info:
223+
raise KeyError("No bearer token available")
224+
225+
# The authorization value starts with the token_type
226+
# if _token_info["token_type"].to_lower() != "bearer":
227+
_bearer = f"DPoP {_token_info[_token_type]}"
228+
229+
# Add 'Authorization' to the headers
230+
if http_args is None:
231+
http_args = {"headers": {}}
232+
http_args["headers"]["Authorization"] = _bearer
233+
else:
234+
try:
235+
http_args["headers"]["Authorization"] = _bearer
236+
except KeyError:
237+
http_args["headers"] = {"Authorization": _bearer}
238+
239+
return http_args

src/idpyoidc/client/oauth2/add_on/par.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,9 @@ def push_authorization(request_args, service, **kwargs):
4545
_context.client_authn_methods[_name] = execute(spec)
4646
authn_method = _name
4747

48-
_args = {}
48+
_args = kwargs.copy()
4949
if _context.issuer:
5050
_args["iss"] = _context.issuer
51-
if _name == "client_attestation":
52-
_wia = kwargs.get("client_attestation")
53-
if _wia:
54-
_args["client_attestation"] = _wia
5551

5652
_headers = service.get_headers(
5753
request_args, http_method=_http_method, authn_method=authn_method, **_args

src/idpyoidc/client/oauth2/server_metadata.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def get_endpoint(self):
3838
:return: Service endpoint
3939
"""
4040
try:
41-
_iss = self.upstream_get("context").issuer
41+
_iss = self.upstream_get("attribute","issuer")
4242
except AttributeError:
4343
_iss = self.endpoint
4444

@@ -72,13 +72,16 @@ def _verify_issuer(self, resp, issuer):
7272

7373
# In some cases we can live with the two URLs not being
7474
# the same. But this is an excepted that has to be explicit
75-
try:
76-
self.upstream_get("context").allow["issuer_mismatch"]
77-
except KeyError:
78-
if _issuer != _pcr_issuer:
79-
raise OidcServiceError(
80-
"provider info issuer mismatch '%s' != '%s'" % (_issuer, _pcr_issuer)
81-
)
75+
_allow = self.upstream_get("attribute", "allow")
76+
if _allow:
77+
_allowed = _allow.get("issuer_mismatch", None)
78+
if _allowed:
79+
return _issuer
80+
81+
if _issuer != _pcr_issuer:
82+
raise OidcServiceError(
83+
"provider info issuer mismatch '%s' != '%s'" % (_issuer, _pcr_issuer)
84+
)
8285
return _issuer
8386

8487
def _set_endpoints(self, resp):
@@ -131,9 +134,10 @@ def _update_service_context(self, resp):
131134
# is loaded not necessarily that any keys are fetched.
132135
if "jwks_uri" in resp:
133136
LOGGER.debug(f"'jwks_uri' in provider info: {resp['jwks_uri']}")
134-
_hp = self.upstream_get('entity').httpc_params
135-
if "verify" in _hp and "verify" not in _keyjar.httpc_params:
136-
_keyjar.httpc_params["verify"] = _hp["verify"]
137+
_hp = self.upstream_get("attribute","httpc_params")
138+
if _hp:
139+
if "verify" in _hp and "verify" not in _keyjar.httpc_params:
140+
_keyjar.httpc_params["verify"] = _hp["verify"]
137141
_keyjar.load_keys(_pcr_issuer, jwks_uri=resp["jwks_uri"])
138142
_loaded = True
139143
elif "jwks" in resp:

src/idpyoidc/client/oidc/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ def __init__(
9494
jwks_uri: Optional[str] = "",
9595
**kwargs
9696
):
97-
self.upstream_get = upstream_get
9897
if services:
9998
_srvs = services
10099
else:

src/idpyoidc/client/oidc/access_token.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def gather_verify_arguments(
4242
:return: dictionary with arguments to the verify call
4343
"""
4444
_context = self.upstream_get("context")
45-
_entity = self.upstream_get("entity")
45+
_entity = self.upstream_get("unit")
4646

4747
kwargs = {
4848
"client_id": _entity.get_client_id(),

src/idpyoidc/client/oidc/authorization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def update_service_context(self, resp, key="", **kwargs):
9090
_context.cstate.update(key, resp)
9191

9292
def get_request_from_response(self, response):
93-
_context = self.upstream_get("service_context")
93+
_context = self.upstream_get("context")
9494
return _context.cstate.get_set(response["state"], message=oauth2.AuthorizationRequest)
9595

9696
def post_parse_response(self, response, **kwargs):

0 commit comments

Comments
 (0)