test_client_authentication.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. """Client authentication tests across all endpoints.
  2. Client authentication in OAuth2 serve two purposes, to authenticate
  3. confidential clients and to ensure public clients are in fact public. The
  4. latter is achieved with authenticate_client_id and the former with
  5. authenticate_client.
  6. We make sure authentication is done by requiring a client object to be set
  7. on the request object with a client_id parameter. The client_id attribute
  8. prevents this check from being circumvented with a client form parameter.
  9. """
  10. import json
  11. from unittest import mock
  12. from oauthlib.oauth2 import (
  13. BackendApplicationServer, LegacyApplicationServer, MobileApplicationServer,
  14. RequestValidator, WebApplicationServer,
  15. )
  16. from tests.unittest import TestCase
  17. from .test_utils import get_fragment_credentials
  18. class ClientAuthenticationTest(TestCase):
  19. def inspect_client(self, request, refresh_token=False):
  20. if not request.client or not request.client.client_id:
  21. raise ValueError()
  22. return 'abc'
  23. def setUp(self):
  24. self.validator = mock.MagicMock(spec=RequestValidator)
  25. self.validator.is_pkce_required.return_value = False
  26. self.validator.get_code_challenge.return_value = None
  27. self.validator.get_default_redirect_uri.return_value = 'http://i.b./path'
  28. self.web = WebApplicationServer(self.validator,
  29. token_generator=self.inspect_client)
  30. self.mobile = MobileApplicationServer(self.validator,
  31. token_generator=self.inspect_client)
  32. self.legacy = LegacyApplicationServer(self.validator,
  33. token_generator=self.inspect_client)
  34. self.backend = BackendApplicationServer(self.validator,
  35. token_generator=self.inspect_client)
  36. self.token_uri = 'http://example.com/path'
  37. self.auth_uri = 'http://example.com/path?client_id=abc&response_type=token'
  38. # should be base64 but no added value in this unittest
  39. self.basicauth_client_creds = {"Authorization": "john:doe"}
  40. self.basicauth_client_id = {"Authorization": "john:"}
  41. def set_client(self, request):
  42. request.client = mock.MagicMock()
  43. request.client.client_id = 'mocked'
  44. return True
  45. def set_client_id(self, client_id, request):
  46. request.client = mock.MagicMock()
  47. request.client.client_id = 'mocked'
  48. return True
  49. def basicauth_authenticate_client(self, request):
  50. assert "Authorization" in request.headers
  51. assert "john:doe" in request.headers["Authorization"]
  52. request.client = mock.MagicMock()
  53. request.client.client_id = 'mocked'
  54. return True
  55. def test_client_id_authentication(self):
  56. token_uri = 'http://example.com/path'
  57. # authorization code grant
  58. self.validator.authenticate_client.return_value = False
  59. self.validator.authenticate_client_id.return_value = False
  60. _, body, _ = self.web.create_token_response(token_uri,
  61. body='grant_type=authorization_code&code=mock')
  62. self.assertEqual(json.loads(body)['error'], 'invalid_client')
  63. self.validator.authenticate_client_id.return_value = True
  64. self.validator.authenticate_client.side_effect = self.set_client
  65. _, body, _ = self.web.create_token_response(token_uri,
  66. body='grant_type=authorization_code&code=mock')
  67. self.assertIn('access_token', json.loads(body))
  68. # implicit grant
  69. auth_uri = 'http://example.com/path?client_id=abc&response_type=token'
  70. self.assertRaises(ValueError, self.mobile.create_authorization_response,
  71. auth_uri, scopes=['random'])
  72. self.validator.validate_client_id.side_effect = self.set_client_id
  73. h, _, s = self.mobile.create_authorization_response(auth_uri, scopes=['random'])
  74. self.assertEqual(302, s)
  75. self.assertIn('Location', h)
  76. self.assertIn('access_token', get_fragment_credentials(h['Location']))
  77. def test_basicauth_web(self):
  78. self.validator.authenticate_client.side_effect = self.basicauth_authenticate_client
  79. _, body, _ = self.web.create_token_response(
  80. self.token_uri,
  81. body='grant_type=authorization_code&code=mock',
  82. headers=self.basicauth_client_creds
  83. )
  84. self.assertIn('access_token', json.loads(body))
  85. def test_basicauth_legacy(self):
  86. self.validator.authenticate_client.side_effect = self.basicauth_authenticate_client
  87. _, body, _ = self.legacy.create_token_response(
  88. self.token_uri,
  89. body='grant_type=password&username=abc&password=secret',
  90. headers=self.basicauth_client_creds
  91. )
  92. self.assertIn('access_token', json.loads(body))
  93. def test_basicauth_backend(self):
  94. self.validator.authenticate_client.side_effect = self.basicauth_authenticate_client
  95. _, body, _ = self.backend.create_token_response(
  96. self.token_uri,
  97. body='grant_type=client_credentials',
  98. headers=self.basicauth_client_creds
  99. )
  100. self.assertIn('access_token', json.loads(body))
  101. def test_basicauth_revoke(self):
  102. self.validator.authenticate_client.side_effect = self.basicauth_authenticate_client
  103. # legacy or any other uses the same RevocationEndpoint
  104. _, body, status = self.legacy.create_revocation_response(
  105. self.token_uri,
  106. body='token=foobar',
  107. headers=self.basicauth_client_creds
  108. )
  109. self.assertEqual(status, 200, body)
  110. def test_basicauth_introspect(self):
  111. self.validator.authenticate_client.side_effect = self.basicauth_authenticate_client
  112. # legacy or any other uses the same IntrospectEndpoint
  113. _, body, status = self.legacy.create_introspect_response(
  114. self.token_uri,
  115. body='token=foobar',
  116. headers=self.basicauth_client_creds
  117. )
  118. self.assertEqual(status, 200, body)
  119. def test_custom_authentication(self):
  120. token_uri = 'http://example.com/path'
  121. # authorization code grant
  122. self.assertRaises(NotImplementedError,
  123. self.web.create_token_response, token_uri,
  124. body='grant_type=authorization_code&code=mock')
  125. # password grant
  126. self.validator.authenticate_client.return_value = True
  127. self.assertRaises(NotImplementedError,
  128. self.legacy.create_token_response, token_uri,
  129. body='grant_type=password&username=abc&password=secret')
  130. # client credentials grant
  131. self.validator.authenticate_client.return_value = True
  132. self.assertRaises(NotImplementedError,
  133. self.backend.create_token_response, token_uri,
  134. body='grant_type=client_credentials')