Skip to content

Commit 36431dc

Browse files
authored
Merge pull request #13 from s-hal/authz_code_owner
Fix code exchange ensure the authz code was issued to the requesting client
2 parents 656449b + 6eb415a commit 36431dc

4 files changed

Lines changed: 31 additions & 10 deletions

File tree

src/pyop/client_authentication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ def verify_client_authentication(clients, parsed_request, authz_header=None):
6161
raise InvalidClientAuthentication(
6262
'Wrong authentication method used, MUST use \'{}\''.format(expected_authn_method))
6363

64-
return True
64+
return client_id

src/pyop/provider.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from .exceptions import AuthorizationError
3030
from .exceptions import InvalidAccessToken
3131
from .exceptions import InvalidTokenRequest
32+
from .exceptions import InvalidAuthorizationCode
3233
from .request_validator import authorization_request_verify
3334
from .request_validator import client_id_is_known
3435
from .request_validator import client_preferences_match_provider_capabilities
@@ -339,7 +340,11 @@ def _do_code_exchange(self, request, # type: Dict[str, str]
339340
raise InvalidTokenRequest(str(e), token_request) from e
340341

341342
authentication_request = self.authz_state.get_authorization_request_for_code(token_request['code'])
342-
343+
344+
if token_request['client_id'] != authentication_request['client_id']:
345+
logger.info('Authorization code \'%s\' belonging to \'%s\' was used by \'%s\'',
346+
token_request['code'], authentication_request['client_id'], token_request['client_id'])
347+
raise InvalidAuthorizationCode('{} unknown'.format(token_request['code']))
343348
if token_request['redirect_uri'] != authentication_request['redirect_uri']:
344349
raise InvalidTokenRequest('Invalid redirect_uri: {} != {}'.format(token_request['redirect_uri'],
345350
authentication_request['redirect_uri']),
@@ -405,7 +410,7 @@ def _verify_client_authentication(self, request_body, http_headers=None):
405410
if http_headers is None:
406411
http_headers = {}
407412
token_request = dict(parse_qsl(request_body))
408-
verify_client_authentication(self.clients, token_request, http_headers.get('Authorization'))
413+
token_request['client_id'] = verify_client_authentication(self.clients, token_request, http_headers.get('Authorization'))
409414
return token_request
410415

411416
def handle_userinfo_request(self, request=None, http_headers=None):

tests/pyop/test_client_authentication.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ def test_wrong_authentication_method(self):
4141
def test_authentication_method_defaults_to_client_secret_basic(self):
4242
del self.clients[TEST_CLIENT_ID]['token_endpoint_auth_method']
4343
authz_header = self.create_basic_auth()
44-
assert verify_client_authentication(self.clients, self.token_request_args, authz_header)
44+
assert verify_client_authentication(self.clients, self.token_request_args, authz_header) == TEST_CLIENT_ID
4545

4646
def test_client_secret_post(self):
4747
self.clients[TEST_CLIENT_ID]['token_endpoint_auth_method'] = 'client_secret_post'
48-
assert verify_client_authentication(self.clients, self.token_request_args)
48+
assert verify_client_authentication(self.clients, self.token_request_args) == TEST_CLIENT_ID
4949

5050
def test_client_secret_basic(self):
5151
self.clients[TEST_CLIENT_ID]['token_endpoint_auth_method'] = 'client_secret_basic'
5252
authz_header = self.create_basic_auth()
53-
assert verify_client_authentication(self.clients, self.token_request_args, authz_header)
53+
assert verify_client_authentication(self.clients, self.token_request_args, authz_header) == TEST_CLIENT_ID
5454

5555
def test_unknown_client_id(self):
5656
self.token_request_args['client_id'] = 'unknown'
5757
with pytest.raises(InvalidClientAuthentication):
58-
verify_client_authentication(self.clients, self.token_request_args)
58+
verify_client_authentication(self.clients, self.token_request_args) == TEST_CLIENT_ID
5959

6060
def test_wrong_client_secret(self):
6161
self.token_request_args['client_secret'] = 'foobar'
@@ -68,7 +68,7 @@ def test_public_client_no_auth(self):
6868
self.clients[TEST_CLIENT_ID]['token_endpoint_auth_method'] = 'none'
6969
del self.clients[TEST_CLIENT_ID]['client_secret']
7070

71-
assert verify_client_authentication(self.clients, self.token_request_args, None)
71+
assert verify_client_authentication(self.clients, self.token_request_args, None) == TEST_CLIENT_ID
7272

7373
def test_invalid_authorization_scheme(self):
7474
authz_header = self.create_basic_auth()
@@ -78,4 +78,4 @@ def test_invalid_authorization_scheme(self):
7878

7979
def test_invalid_userid_password(self):
8080
with pytest.raises(InvalidClientAuthentication):
81-
verify_client_authentication(self.clients, self.token_request_args, 'Basic invalid')
81+
verify_client_authentication(self.clients, self.token_request_args, 'Basic invalid')

tests/pyop/test_provider.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
TEST_CLIENT_ID = 'client1'
2828
TEST_CLIENT_SECRET = 'secret'
29+
INVALID_TEST_CLIENT_ID = 'invalid_client'
30+
INVALID_TEST_CLIENT_SECRET = 'invalid_secret'
2931
TEST_REDIRECT_URI = 'https://client.example.com'
3032
ISSUER = 'https://provider.example.com'
3133
TEST_USER_ID = 'user1'
@@ -71,7 +73,14 @@ def inject_provider(request):
7173
'client_secret': TEST_CLIENT_SECRET,
7274
'token_endpoint_auth_method': 'client_secret_post',
7375
'post_logout_redirect_uris': ['https://client.example.com/post_logout']
74-
76+
},
77+
INVALID_TEST_CLIENT_ID: {
78+
'subject_type': 'pairwise',
79+
'redirect_uris': 'http://invalid.redirect.loc',
80+
'response_types': ['code'],
81+
'client_secret': INVALID_TEST_CLIENT_SECRET,
82+
'token_endpoint_auth_method': 'client_secret_post',
83+
'post_logout_redirect_uris': ['https://client.example.com/post_logout']
7584
}
7685
}
7786

@@ -337,6 +346,13 @@ def test_handle_token_request_reject_invalid_client_authentication(self):
337346
self.provider.handle_token_request(urlencode(self.authorization_code_exchange_request_args),
338347
extra_id_token_claims={'foo': 'bar'})
339348

349+
def test_handle_token_request_reject_code_not_issued_to_client(self):
350+
self.authorization_code_exchange_request_args['client_id'] = INVALID_TEST_CLIENT_ID
351+
self.authorization_code_exchange_request_args['client_secret'] = INVALID_TEST_CLIENT_SECRET
352+
self.authorization_code_exchange_request_args['code'] = self.create_authz_code()
353+
with pytest.raises(InvalidAuthorizationCode):
354+
self.provider.handle_token_request(urlencode(self.authorization_code_exchange_request_args))
355+
340356
def test_handle_token_request_reject_invalid_redirect_uri_in_exchange_request(self):
341357
self.authorization_code_exchange_request_args['redirect_uri'] = 'https://invalid.com'
342358
self.authorization_code_exchange_request_args['code'] = self.create_authz_code()

0 commit comments

Comments
 (0)