Skip to content
This repository was archived by the owner on Jun 12, 2021. It is now read-only.

Commit 3b34729

Browse files
committed
Different endpoint accept different audience in a JWT used for client authentication. New specifications like the pushed authorization adds complexity to this. So a refactoring was needed.
1 parent bccb108 commit 3b34729

4 files changed

Lines changed: 39 additions & 6 deletions

File tree

src/oidcendpoint/client_authn.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ def verify(self, request, **kwargs):
150150
else:
151151
raise NotForMe("Not for me!")
152152
else:
153-
if self.endpoint_context.endpoint[_endpoint].full_path in ca_jwt["aud"]:
153+
if set(ca_jwt["aud"]).intersection(
154+
self.endpoint_context.endpoint[_endpoint].allowed_target_uris()):
154155
pass
155156
else:
156157
raise NotForMe("Not for me!")

src/oidcendpoint/endpoint.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
"response" MUST be present
5353
"http_headers" MAY be present
5454
"cookie": MAY be present
55-
"response_placement": If absent defaults the endpoints response_placement parameter value
56-
or if that is also missing 'url'
55+
"response_placement": If absent defaults to the endpoints response_placement
56+
parameter value or if that is also missing 'url'
5757
"""
5858

5959

@@ -187,6 +187,9 @@ def __init__(self, endpoint_context, **kwargs):
187187
self.endpoint_info = construct_endpoint_info(
188188
self.default_capabilities, **kwargs
189189
)
190+
# This is for matching against aud in JWTs
191+
# By default the endpoint's endpoint URL is an allowed target
192+
self.allowed_targets = [self.name]
190193

191194
def parse_request(self, request, auth=None, **kwargs):
192195
"""
@@ -209,7 +212,7 @@ def parse_request(self, request, auth=None, **kwargs):
209212
request,
210213
"jwt",
211214
keyjar=self.endpoint_context.keyjar,
212-
verify=self.endpoint_context.verify_ssl,
215+
verify=self.endpoint_context.httpc_params["verify"],
213216
**kwargs
214217
)
215218
elif self.request_format == "url":
@@ -425,3 +428,12 @@ def do_response(self, response_args=None, request=None, error="", **kwargs):
425428
pass
426429

427430
return _resp
431+
432+
def allowed_target_uris(self):
433+
res = []
434+
for t in self.allowed_targets:
435+
if t == "":
436+
res.append(self.endpoint_context.issuer)
437+
else:
438+
res.append(self.endpoint_context.endpoint[t].full_path)
439+
return set(res)

src/oidcendpoint/oidc/userinfo.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def __init__(self, endpoint_context, **kwargs):
3636
self.scope_to_claims = None
3737
if "client_authn_method" not in kwargs:
3838
self.client_authn_method = self.default_capabilities["client_authn_method"]
39+
# Add the issuer ID as an allowed JWT target
40+
self.allowed_targets.append("")
3941

4042
def get_client_id_from_token(self, endpoint_context, token, request=None):
4143
sinfo = self.endpoint_context.sdb[token]

tests/test_02_client_authn.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from oidcendpoint.exception import NotForMe
2323
from oidcendpoint.oidc.authorization import Authorization
2424
from oidcendpoint.oidc.token import AccessToken
25+
from oidcendpoint.oidc.userinfo import UserInfo
2526

2627
KEYDEFS = [
2728
{"type": "RSA", "key": "", "use": ["sig"]},
@@ -39,7 +40,8 @@
3940
"verify_ssl": False,
4041
"endpoint": {
4142
"token": {"path": "token", "class": AccessToken, "kwargs": {}},
42-
"authorization": {"path": "auth", "class": Authorization, "kwargs": {}}
43+
"authorization": {"path": "auth", "class": Authorization, "kwargs": {}},
44+
"userinfo": {"path": "user", "class": UserInfo, "kwargs": {}}
4345
},
4446
"template_dir": "template",
4547
"jwks": {
@@ -138,7 +140,7 @@ def test_private_key_jwt_reusage_other_endpoint():
138140
request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}
139141

140142
# This should be OK
141-
authn_info = PrivateKeyJWT(endpoint_context).verify(request, endpoint="token")
143+
PrivateKeyJWT(endpoint_context).verify(request, endpoint="token")
142144

143145
# This should NOT be OK
144146
with pytest.raises(NotForMe):
@@ -375,3 +377,19 @@ def test_verify_client_bearer_header():
375377
res = verify_client(endpoint_context, request, token, get_client_id_from_token)
376378
assert set(res.keys()) == {"token", "method", "client_id"}
377379
assert res["method"] == "bearer_header"
380+
381+
382+
def test_jws_authn_method_aud_userinfo_endpoint():
383+
client_keyjar = KeyJar()
384+
client_keyjar[conf["issuer"]] = KEYJAR.issuer_keys[""]
385+
# The only own key the client has a this point
386+
client_keyjar.add_symmetric("", client_secret, ["sig"])
387+
388+
_jwt = JWT(client_keyjar, iss=client_id, sign_alg="HS256")
389+
390+
# audience is the OP - not specifically the user info endpoint
391+
_assertion = _jwt.pack({"aud": [conf["issuer"]]})
392+
393+
request = {"client_assertion": _assertion, "client_assertion_type": JWT_BEARER}
394+
395+
assert JWSAuthnMethod(endpoint_context).verify(request, endpoint="userinfo")

0 commit comments

Comments
 (0)