Skip to content

Commit cd02bc7

Browse files
authored
Merge pull request #49 from ctriant/client_authn_fixes
Various client authentication related fixes
2 parents 021e36b + 37e711f commit cd02bc7

9 files changed

+58
-51
lines changed

src/idpyoidc/server/client_authn.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,15 @@ def _verify(
262262
request: Optional[Union[dict, Message]] = None,
263263
authorization_token: Optional[str] = None,
264264
endpoint=None, # Optional[Endpoint]
265+
get_client_id_from_token=None,
265266
**kwargs,
266267
):
267268
_token = request.get("access_token")
268269
if _token is None:
269270
raise ClientAuthenticationError("No access token")
270271

271272
res = {"token": _token}
272-
_client_id = request.get("client_id")
273+
_client_id = get_client_id_from_token(endpoint_context, _token, request)
273274
if _client_id:
274275
res["client_id"] = _client_id
275276
return res
@@ -483,6 +484,7 @@ def verify_client(
483484

484485
auth_info = {}
485486
methods = endpoint_context.client_authn_method
487+
client_id = None
486488
allowed_methods = getattr(endpoint, "client_authn_method")
487489
if not allowed_methods:
488490
allowed_methods = list(methods.keys())
@@ -499,48 +501,47 @@ def verify_client(
499501
endpoint=endpoint,
500502
get_client_id_from_token=get_client_id_from_token,
501503
)
502-
break
503504
except (BearerTokenAuthenticationError, ClientAuthenticationError):
504505
raise
505506
except Exception as err:
506507
logger.info("Verifying auth using {} failed: {}".format(_method.tag, err))
508+
continue
507509

508-
if auth_info.get("method") == "none":
509-
return auth_info
510+
if auth_info.get("method") == "none" and auth_info.get("client_id") is None:
511+
break
510512

511-
client_id = auth_info.get("client_id")
512-
if client_id is None:
513-
raise ClientAuthenticationError("Failed to verify client")
513+
client_id = auth_info.get("client_id")
514+
if client_id is None:
515+
raise ClientAuthenticationError("Failed to verify client")
514516

515-
if also_known_as:
516-
client_id = also_known_as[client_id]
517-
auth_info["client_id"] = client_id
517+
if also_known_as:
518+
client_id = also_known_as[client_id]
519+
auth_info["client_id"] = client_id
518520

519-
if client_id not in endpoint_context.cdb:
520-
raise UnknownClient("Unknown Client ID")
521+
if client_id not in endpoint_context.cdb:
522+
raise UnknownClient("Unknown Client ID")
521523

522-
_cinfo = endpoint_context.cdb[client_id]
524+
_cinfo = endpoint_context.cdb[client_id]
523525

524-
if not valid_client_info(_cinfo):
525-
logger.warning("Client registration has timed out or " "client secret is expired.")
526-
raise InvalidClient("Not valid client")
526+
if not valid_client_info(_cinfo):
527+
logger.warning("Client registration has timed out or " "client secret is expired.")
528+
raise InvalidClient("Not valid client")
527529

528-
# Validate that the used method is allowed for this client/endpoint
529-
client_allowed_methods = _cinfo.get(
530-
f"{endpoint.endpoint_name}_client_authn_method", _cinfo.get("client_authn_method")
531-
)
532-
if client_allowed_methods is not None and _method and _method.tag not in client_allowed_methods:
533-
logger.info(
534-
f"Allowed methods for client: {client_id} at endpoint: {endpoint.name} are: "
535-
f"`{', '.join(client_allowed_methods)}`"
536-
)
537-
raise UnAuthorizedClient(
538-
f"Authentication method: {_method.tag} not allowed for client: {client_id} in "
539-
f"endpoint: {endpoint.name}"
530+
# Validate that the used method is allowed for this client/endpoint
531+
client_allowed_methods = _cinfo.get(
532+
f"{endpoint.endpoint_name}_client_authn_method", _cinfo.get("client_authn_method")
540533
)
534+
if client_allowed_methods is not None and auth_info["method"] not in client_allowed_methods:
535+
logger.info(
536+
f"Allowed methods for client: {client_id} at endpoint: {endpoint.name} are: "
537+
f"`{', '.join(client_allowed_methods)}`"
538+
)
539+
auth_info = {}
540+
continue
541+
break
541542

542543
# store what authn method was used
543-
if auth_info.get("method"):
544+
if "method" in auth_info and client_id:
544545
_request_type = request.__class__.__name__
545546
_used_authn_method = _cinfo.get("auth_method")
546547
if _used_authn_method:

src/idpyoidc/server/endpoint.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ def set_client_authn_methods(self, **kwargs):
132132
kwargs[self.auth_method_attribute] = _methods
133133
elif _methods is not None: # [] or '' or something not None but regarded as nothing.
134134
self.client_authn_method = ["none"] # Ignore default value
135+
elif self.default_capabilities:
136+
self.client_authn_method = self.default_capabilities.get("client_authn_method")
137+
self.endpoint_info = construct_provider_info(self.default_capabilities, **kwargs)
135138
return kwargs
136139

137140
def get_provider_info_attributes(self):
@@ -249,7 +252,8 @@ def client_authentication(self, request: Message, http_info: Optional[dict] = No
249252
if authn_info == {} and self.client_authn_method and len(self.client_authn_method):
250253
LOGGER.debug("client_authn_method: %s", self.client_authn_method)
251254
raise UnAuthorizedClient("Authorization failed")
252-
255+
if "client_id" not in authn_info and authn_info.get("method") != "none":
256+
raise UnAuthorizedClient("Authorization failed")
253257
return authn_info
254258

255259
def do_post_parse_request(

src/idpyoidc/server/oidc/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ def parse_request(self, request, http_info=None, **kwargs):
361361

362362
# Verify that the client is allowed to do this
363363
auth_info = self.client_authentication(request, http_info, **kwargs)
364-
if not auth_info or auth_info["method"] == "none":
364+
if not auth_info:
365365
pass
366366
elif isinstance(auth_info, ResponseMessage):
367367
return auth_info

src/idpyoidc/server/oidc/userinfo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def parse_request(self, request, http_info=None, **kwargs):
182182
try:
183183
auth_info = self.client_authentication(request, http_info, **kwargs)
184184
except ClientAuthenticationError as e:
185-
return self.error_cls(error="invalid_token", error_description=e.args[0])
185+
return self.error_cls(error="invalid_token", error_description="Invalid token")
186186

187187
if isinstance(auth_info, ResponseMessage):
188188
return auth_info

tests/test_server_17_client_authn.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def create_method(self):
337337

338338
def test_bearer_body(self):
339339
request = {"access_token": "1234567890"}
340-
assert self.method.verify(request) == {"token": "1234567890", "method": "bearer_body"}
340+
assert self.method.verify(request, get_client_id_from_token=get_client_id_from_token) == {"token": "1234567890", "method": "bearer_body"}
341341

342342
def test_bearer_body_no_token(self):
343343
request = {}
@@ -504,13 +504,12 @@ def test_verify_per_client_per_endpoint(self):
504504
)
505505
assert res == {"method": "public", "client_id": client_id}
506506

507-
with pytest.raises(ClientAuthenticationError) as e:
508-
verify_client(
509-
self.endpoint_context,
510-
request,
511-
endpoint=self.server.server_get("endpoint", "endpoint_1"),
512-
)
513-
assert e.value.args[0] == "Failed to verify client"
507+
res = verify_client(
508+
self.endpoint_context,
509+
request,
510+
endpoint=self.server.server_get("endpoint", "endpoint_1"),
511+
)
512+
assert res == {}
514513

515514
request = {"client_id": client_id, "client_secret": client_secret}
516515
res = verify_client(

tests/test_server_20d_client_authn.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def create_method(self):
292292

293293
def test_bearer_body(self):
294294
request = {"access_token": "1234567890"}
295-
assert self.method.verify(request) == {"token": "1234567890", "method": "bearer_body"}
295+
assert self.method.verify(request, get_client_id_from_token=get_client_id_from_token) == {"token": "1234567890", "method": "bearer_body"}
296296

297297
def test_bearer_body_no_token(self):
298298
request = {}
@@ -457,13 +457,12 @@ def test_verify_per_client_per_endpoint(self):
457457
)
458458
assert res == {"method": "public", "client_id": client_id}
459459

460-
with pytest.raises(ClientAuthenticationError) as e:
461-
verify_client(
462-
self.endpoint_context,
463-
request,
464-
endpoint=self.server.server_get("endpoint", "token"),
465-
)
466-
assert e.value.args[0] == "Failed to verify client"
460+
res = verify_client(
461+
self.endpoint_context,
462+
request,
463+
endpoint=self.server.server_get("endpoint", "token"),
464+
)
465+
assert res == {}
467466

468467
request = {"client_id": client_id, "client_secret": client_secret}
469468
res = verify_client(

tests/test_server_23_oidc_registration_endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def create_endpoint(self):
127127
"registration": {
128128
"path": "registration",
129129
"class": Registration,
130-
"kwargs": {"client_auth_method": None},
130+
"kwargs": {"client_authn_method": ["none"]},
131131
},
132132
"authorization": {
133133
"path": "authorization",

tests/test_server_32_oidc_read_registration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def create_endpoint(self):
9595
"registration": {
9696
"path": "registration",
9797
"class": Registration,
98-
"kwargs": {"client_auth_method": None},
98+
"kwargs": {"client_authn_method": ["none"]},
9999
},
100100
"registration_api": {
101101
"path": "registration_api",

tests/test_server_60_dpop.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@ def create_endpoint(self):
164164
"class": Authorization,
165165
"kwargs": {},
166166
},
167-
"token": {"path": "{}/token", "class": Token, "kwargs": {}},
167+
"token": {
168+
"path": "{}/token",
169+
"class": Token,
170+
"kwargs": {"client_authn_method": ["none"]},
171+
},
168172
},
169173
"client_authn": verify_client,
170174
"authentication": {

0 commit comments

Comments
 (0)