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

Commit 3e45129

Browse files
committed
Update tests
1 parent e51c3d5 commit 3e45129

File tree

2 files changed

+119
-74
lines changed

2 files changed

+119
-74
lines changed

src/oidcop/oidc/token.py

+28-24
Original file line numberDiff line numberDiff line change
@@ -457,27 +457,6 @@ def enforce_policy(self, request, token):
457457
error_description="Unsupported requested token type",
458458
)
459459

460-
if (
461-
"requested_token_type" in request
462-
and request["requested_token_type"] == "urn:ietf:params:oauth:token-type:refresh_token"
463-
and request["subject_token_type"] == "urn:ietf:params:oauth:token-type:access_token"
464-
and "offline_access" not in token.scope
465-
):
466-
return TokenErrorResponse(
467-
error="invalid_request",
468-
error_description="Exchange access token to refresh token forbbiden",
469-
)
470-
471-
elif (
472-
"requested_token_type" in request
473-
and request["requested_token_type"] == "urn:ietf:params:oauth:token-type:refresh_token"
474-
and request["subject_token_type"] != "urn:ietf:params:oauth:token-type:refresh_token"
475-
):
476-
return TokenErrorResponse(
477-
error="invalid_request",
478-
error_description=f"Exchange {request['subject_token_type']} to refresh token forbbiden",
479-
)
480-
481460
if (
482461
"requested_token_type" in request
483462
and request["requested_token_type"] in self.config["token_exchange"]["policy"]
@@ -488,7 +467,7 @@ def enforce_policy(self, request, token):
488467
fun = importer(self.config["token_exchange"]["policy"][""]["callable"])
489468
kwargs = self.config["token_exchange"]["policy"][""]["kwargs"]
490469

491-
return fun(request, context=_context, kwargs=kwargs)
470+
return fun(request, context=_context, subject_token=token, kwargs=kwargs)
492471

493472
def token_exchange_response(self, access_token):
494473
response_args = {}
@@ -539,7 +518,11 @@ def process_request(self, request, **kwargs):
539518
"urn:ietf:params:oauth:token-type:access_token")
540519

541520
_token_class = _requested_token_type.split(":")[-1]
542-
_token_type = token.token_type
521+
522+
if _token_class == "refresh_token":
523+
_token_type = None
524+
else:
525+
_token_type = token.token_type
543526

544527
sid = _session_info["session_id"]
545528
if request["client_id"] != _session_info["client_id"]:
@@ -579,7 +562,7 @@ def process_request(self, request, **kwargs):
579562

580563
return self.token_exchange_response(access_token=new_token)
581564

582-
def default_token_exchange_policy(request, context, kwargs):
565+
def default_token_exchange_policy(request, context, subject_token, kwargs):
583566
if "resource" in request:
584567
resource = kwargs.get("resource", [])
585568
if (not len(set(request["resource"]).intersection(set(resource)))):
@@ -604,6 +587,27 @@ def default_token_exchange_policy(request, context, kwargs):
604587
error="invalid_request", error_description="Actor token not supported"
605588
)
606589

590+
if (
591+
"requested_token_type" in request
592+
and request["requested_token_type"] == "urn:ietf:params:oauth:token-type:refresh_token"
593+
):
594+
595+
if (request["subject_token_type"] == "urn:ietf:params:oauth:token-type:access_token"
596+
and "offline_access" not in subject_token.scope
597+
):
598+
return TokenErrorResponse(
599+
error="invalid_request",
600+
error_description="Exchange access token to refresh token forbbiden",
601+
)
602+
603+
elif (
604+
request["subject_token_type"] != "urn:ietf:params:oauth:token-type:refresh_token"
605+
):
606+
return TokenErrorResponse(
607+
error="invalid_request",
608+
error_description=f"Exchange {request['subject_token_type']} to refresh token forbbiden",
609+
)
610+
607611
request["scope"] = kwargs.get("scope", ["openid"])
608612
return request
609613

tests/test_36_oauth2_token_exchange.py

+91-50
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,6 @@ def test_token_exchange(self):
284284
_resp = self.endpoint.process_request(request=_req)
285285

286286
_token_value = _resp["response_args"]["access_token"]
287-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
288-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
289287

290288
token_exchange_req = TokenExchangeRequest(
291289
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -319,16 +317,12 @@ def test_additional_parameters(self):
319317
grant = self.endpoint_context.authz(session_id, areq)
320318
code = self._mint_code(grant, areq['client_id'])
321319

322-
_cntx = self.endpoint_context
323-
324320
_token_request = TOKEN_REQ_DICT.copy()
325321
_token_request["code"] = code.value
326322
_req = self.endpoint.parse_request(_token_request)
327323
_resp = self.endpoint.process_request(request=_req)
328324

329325
_token_value = _resp["response_args"]["access_token"]
330-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
331-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
332326

333327
token_exchange_req = TokenExchangeRequest(
334328
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -368,16 +362,12 @@ def test_token_exchange_fails_if_disabled(self):
368362
grant = self.endpoint_context.authz(session_id, areq)
369363
code = self._mint_code(grant, areq['client_id'])
370364

371-
_cntx = self.endpoint_context
372-
373365
_token_request = TOKEN_REQ_DICT.copy()
374366
_token_request["code"] = code.value
375367
_req = self.endpoint.parse_request(_token_request)
376368
_resp = self.endpoint.process_request(request=_req)
377369

378370
_token_value = _resp["response_args"]["access_token"]
379-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
380-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
381371

382372
token_exchange_req = TokenExchangeRequest(
383373
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -414,15 +404,12 @@ def test_wrong_resource(self):
414404
grant = self.endpoint_context.authz(session_id, areq)
415405
code = self._mint_code(grant, areq['client_id'])
416406

417-
_cntx = self.endpoint_context
418-
419407
_token_request = TOKEN_REQ_DICT.copy()
420408
_token_request["code"] = code.value
421409
_req = self.endpoint.parse_request(_token_request)
422410
_resp = self.endpoint.process_request(request=_req)
423411

424412
_token_value = _resp["response_args"]["access_token"]
425-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
426413

427414
token_exchange_req = TokenExchangeRequest(
428415
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -444,6 +431,46 @@ def test_wrong_resource(self):
444431
assert _resp["error"] == "invalid_target"
445432
assert _resp["error_description"] == "Unknown resource"
446433

434+
def test_refresh_token_audience(self):
435+
"""
436+
Test that requesting a refresh token with audience fails.
437+
438+
We currently only allow audience that matches the owner of the subject_token or
439+
the allowed audience as configured in authz/grant_config
440+
"""
441+
areq = AUTH_REQ.copy()
442+
443+
session_id = self._create_session(areq)
444+
grant = self.endpoint_context.authz(session_id, areq)
445+
code = self._mint_code(grant, areq['client_id'])
446+
447+
_token_request = TOKEN_REQ_DICT.copy()
448+
_token_request["code"] = code.value
449+
_req = self.endpoint.parse_request(_token_request)
450+
_resp = self.endpoint.process_request(request=_req)
451+
452+
_token_value = _resp["response_args"]["access_token"]
453+
454+
token_exchange_req = TokenExchangeRequest(
455+
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
456+
subject_token=_token_value,
457+
subject_token_type="urn:ietf:params:oauth:token-type:refresh_token",
458+
audience=["https://example.com"]
459+
)
460+
461+
_req = self.endpoint.parse_request(
462+
token_exchange_req.to_json(),
463+
{
464+
"headers": {
465+
"authorization": "Basic {}".format("Y2xpZW50XzE6aGVtbGlndA==")
466+
}
467+
},
468+
)
469+
_resp = self.endpoint.process_request(request=_req)
470+
assert set(_resp.keys()) == {"error", "error_description"}
471+
assert _resp["error"] == "invalid_target"
472+
assert _resp["error_description"] == "Refresh token has single owner"
473+
447474
def test_wrong_audience(self):
448475
"""
449476
Test that requesting a token for an unknown audience fails.
@@ -457,16 +484,12 @@ def test_wrong_audience(self):
457484
grant = self.endpoint_context.authz(session_id, areq)
458485
code = self._mint_code(grant, areq['client_id'])
459486

460-
_cntx = self.endpoint_context
461-
462487
_token_request = TOKEN_REQ_DICT.copy()
463488
_token_request["code"] = code.value
464489
_req = self.endpoint.parse_request(_token_request)
465490
_resp = self.endpoint.process_request(request=_req)
466491

467492
_token_value = _resp["response_args"]["access_token"]
468-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
469-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
470493

471494
token_exchange_req = TokenExchangeRequest(
472495
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -488,38 +511,75 @@ def test_wrong_audience(self):
488511
assert _resp["error"] == "invalid_target"
489512
assert _resp["error_description"] == "Unknown audience"
490513

491-
@pytest.mark.parametrize("aud", [
492-
"https://example.com/",
493-
])
494-
def test_exchanged_refresh_token_wrong_audience(self, aud):
514+
def test_exchange_refresh_token_to_refresh_token(self):
495515
"""
496516
Test that requesting a token for an unknown audience fails.
497517
498518
We currently only allow audience that matches the owner of the subject_token or
499519
the allowed audience as configured in authz/grant_config
500520
"""
521+
AUTH_REQ["scope"] = ["openid", "offline_access"]
501522
areq = AUTH_REQ.copy()
502523

503524
session_id = self._create_session(areq)
504525
grant = self.endpoint_context.authz(session_id, areq)
505526
code = self._mint_code(grant, areq['client_id'])
506527

507-
_cntx = self.endpoint_context
528+
_token_request = TOKEN_REQ_DICT.copy()
529+
_token_request["scope"] = "openid og"
530+
_token_request["code"] = code.value
531+
_req = self.endpoint.parse_request(_token_request)
532+
_resp = self.endpoint.process_request(request=_req)
533+
_token_value = _resp["response_args"]["refresh_token"]
534+
535+
token_exchange_req = TokenExchangeRequest(
536+
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
537+
subject_token=_token_value,
538+
subject_token_type="urn:ietf:params:oauth:token-type:refresh_token",
539+
requested_token_type="urn:ietf:params:oauth:token-type:refresh_token"
540+
)
541+
542+
_req = self.endpoint.parse_request(
543+
token_exchange_req.to_json(),
544+
{
545+
"headers": {
546+
"authorization": "Basic {}".format("Y2xpZW50XzE6aGVtbGlndA==")
547+
}
548+
},
549+
)
550+
_resp = self.endpoint.process_request(request=_req)
551+
assert set(_resp.keys()) != {"error", "error_description"}
552+
553+
@pytest.mark.parametrize("scopes", [
554+
["openid", "offline_access"],
555+
["openid"],
556+
])
557+
def test_exchange_access_token_to_refresh_token(self, scopes):
558+
"""
559+
Test that requesting a token for an unknown audience fails.
560+
561+
We currently only allow audience that matches the owner of the subject_token or
562+
the allowed audience as configured in authz/grant_config
563+
"""
564+
AUTH_REQ["scope"] = scopes
565+
areq = AUTH_REQ.copy()
566+
567+
session_id = self._create_session(areq)
568+
grant = self.endpoint_context.authz(session_id, areq)
569+
code = self._mint_code(grant, areq['client_id'])
508570

509571
_token_request = TOKEN_REQ_DICT.copy()
572+
_token_request["scope"] = "openid og"
510573
_token_request["code"] = code.value
511574
_req = self.endpoint.parse_request(_token_request)
512575
_resp = self.endpoint.process_request(request=_req)
513576
_token_value = _resp["response_args"]["access_token"]
514-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
515-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
516577

517578
token_exchange_req = TokenExchangeRequest(
518579
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
519580
subject_token=_token_value,
520581
subject_token_type="urn:ietf:params:oauth:token-type:access_token",
521-
requested_token_type="urn:ietf:params:oauth:token-type:refresh_token",
522-
audience=aud
582+
requested_token_type="urn:ietf:params:oauth:token-type:refresh_token"
523583
)
524584

525585
_req = self.endpoint.parse_request(
@@ -531,9 +591,11 @@ def test_exchanged_refresh_token_wrong_audience(self, aud):
531591
},
532592
)
533593
_resp = self.endpoint.process_request(request=_req)
534-
assert set(_resp.keys()) == {"error", "error_description"}
535-
assert _resp["error"] == "invalid_request"
536-
assert _resp["error_description"] == "Exchange access token to refresh token forbbiden"
594+
if ("offline_access" in scopes):
595+
assert set(_resp.keys()) == {"error", "error_description"}
596+
else:
597+
assert _resp["error"] == "invalid_request"
598+
assert _resp["error_description"] == "Exchange access token to refresh token forbbiden"
537599

538600
@pytest.mark.parametrize("missing_attribute", [
539601
"subject_token_type",
@@ -549,16 +611,12 @@ def test_missing_parameters(self, missing_attribute):
549611
grant = self.endpoint_context.authz(session_id, areq)
550612
code = self._mint_code(grant, areq['client_id'])
551613

552-
_cntx = self.endpoint_context
553-
554614
_token_request = TOKEN_REQ_DICT.copy()
555615
_token_request["code"] = code.value
556616
_req = self.endpoint.parse_request(_token_request)
557617
_resp = self.endpoint.process_request(request=_req)
558618

559619
_token_value = _resp["response_args"]["access_token"]
560-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
561-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
562620

563621
token_exchange_req = TokenExchangeRequest(
564622
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -602,15 +660,12 @@ def test_unsupported_requested_token_type(self, unsupported_type):
602660
grant = self.endpoint_context.authz(session_id, areq)
603661
code = self._mint_code(grant, areq['client_id'])
604662

605-
_cntx = self.endpoint_context
606-
607663
_token_request = TOKEN_REQ_DICT.copy()
608664
_token_request["code"] = code.value
609665
_req = self.endpoint.parse_request(_token_request)
610666
_resp = self.endpoint.process_request(request=_req)
611667

612668
_token_value = _resp["response_args"]["access_token"]
613-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
614669

615670
token_exchange_req = TokenExchangeRequest(
616671
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -653,16 +708,12 @@ def test_unsupported_subject_token_type(self, unsupported_type):
653708
grant = self.endpoint_context.authz(session_id, areq)
654709
code = self._mint_code(grant, areq['client_id'])
655710

656-
_cntx = self.endpoint_context
657-
658711
_token_request = TOKEN_REQ_DICT.copy()
659712
_token_request["code"] = code.value
660713
_req = self.endpoint.parse_request(_token_request)
661714
_resp = self.endpoint.process_request(request=_req)
662715

663716
_token_value = _resp["response_args"]["access_token"]
664-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
665-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
666717

667718
token_exchange_req = TokenExchangeRequest(
668719
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -698,16 +749,12 @@ def test_unsupported_actor_token(self):
698749
grant = self.endpoint_context.authz(session_id, areq)
699750
code = self._mint_code(grant, areq['client_id'])
700751

701-
_cntx = self.endpoint_context
702-
703752
_token_request = TOKEN_REQ_DICT.copy()
704753
_token_request["code"] = code.value
705754
_req = self.endpoint.parse_request(_token_request)
706755
_resp = self.endpoint.process_request(request=_req)
707756

708757
_token_value = _resp["response_args"]["access_token"]
709-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
710-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
711758

712759
token_exchange_req = TokenExchangeRequest(
713760
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
@@ -742,17 +789,11 @@ def test_invalid_token(self):
742789
grant = self.endpoint_context.authz(session_id, areq)
743790
code = self._mint_code(grant, areq['client_id'])
744791

745-
_cntx = self.endpoint_context
746-
747792
_token_request = TOKEN_REQ_DICT.copy()
748793
_token_request["code"] = code.value
749794
_req = self.endpoint.parse_request(_token_request)
750795
_resp = self.endpoint.process_request(request=_req)
751796

752-
_token_value = _resp["response_args"]["access_token"]
753-
_session_info = self.session_manager.get_session_info_by_token(_token_value)
754-
_token = self.session_manager.find_token(_session_info["session_id"], _token_value)
755-
756797
token_exchange_req = TokenExchangeRequest(
757798
grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
758799
subject_token="invalid_token",

0 commit comments

Comments
 (0)