oauthlib 3.3.0__tar.gz → 4.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. {oauthlib-3.3.0 → oauthlib-4.0.0}/CHANGELOG.rst +36 -2
  2. {oauthlib-3.3.0/oauthlib.egg-info → oauthlib-4.0.0}/PKG-INFO +13 -3
  3. {oauthlib-3.3.0 → oauthlib-4.0.0}/README.rst +3 -0
  4. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/__init__.py +1 -1
  5. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/common.py +1 -0
  6. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/access_token.py +2 -2
  7. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/base.py +1 -1
  8. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/request_token.py +1 -1
  9. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/errors.py +1 -1
  10. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/__init__.py +3 -2
  11. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/base.py +5 -22
  12. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/service_application.py +1 -1
  13. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/revocation.py +2 -9
  14. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +8 -27
  15. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/base.py +51 -0
  16. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py +2 -9
  17. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py +2 -16
  18. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py +2 -15
  19. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/parameters.py +12 -11
  20. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/request_validator.py +17 -6
  21. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/tokens.py +1 -1
  22. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/utils.py +1 -1
  23. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/grant_types/device_code.py +1 -15
  24. {oauthlib-3.3.0 → oauthlib-4.0.0/oauthlib.egg-info}/PKG-INFO +13 -3
  25. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/SOURCES.txt +0 -3
  26. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/top_level.txt +0 -1
  27. {oauthlib-3.3.0 → oauthlib-4.0.0}/setup.py +12 -4
  28. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_access_token.py +9 -9
  29. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_authorization.py +2 -2
  30. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_base.py +7 -7
  31. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_request_token.py +8 -8
  32. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_resource.py +2 -2
  33. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/endpoints/test_signature_only.py +4 -4
  34. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_client.py +9 -9
  35. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_base.py +2 -9
  36. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_web_application.py +22 -1
  37. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_base_endpoint.py +4 -4
  38. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py +1 -1
  39. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_error_responses.py +7 -7
  40. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_metadata.py +1 -1
  41. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_revocation_endpoint.py +0 -13
  42. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_scope_handling.py +2 -2
  43. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_authorization_code.py +37 -7
  44. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_client_credentials.py +2 -2
  45. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_implicit.py +2 -2
  46. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_refresh_token.py +17 -6
  47. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/grant_types/test_resource_owner_password.py +2 -2
  48. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_parameters.py +13 -0
  49. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_server.py +17 -17
  50. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/grant_types/test_device_code.py +32 -1
  51. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_claims_handling.py +5 -5
  52. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_openid_connect_params_handling.py +1 -1
  53. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_refresh_token.py +9 -2
  54. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_authorization_code.py +9 -9
  55. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_hybrid.py +1 -1
  56. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_implicit.py +5 -5
  57. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_refresh_token.py +3 -3
  58. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/test_server.py +7 -7
  59. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/test_common.py +16 -0
  60. oauthlib-3.3.0/examples/device_code_flow.py +0 -260
  61. oauthlib-3.3.0/examples/skeleton_oauth2_web_application_server.py +0 -114
  62. oauthlib-3.3.0/tests/openid/connect/core/grant_types/__init__.py +0 -0
  63. {oauthlib-3.3.0 → oauthlib-4.0.0}/LICENSE +0 -0
  64. {oauthlib-3.3.0 → oauthlib-4.0.0}/MANIFEST.in +0 -0
  65. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/__init__.py +0 -0
  66. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/__init__.py +0 -0
  67. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/__init__.py +0 -0
  68. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/authorization.py +0 -0
  69. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/pre_configured.py +0 -0
  70. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/resource.py +0 -0
  71. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/endpoints/signature_only.py +0 -0
  72. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/parameters.py +0 -0
  73. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/request_validator.py +0 -0
  74. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/signature.py +0 -0
  75. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth1/rfc5849/utils.py +0 -0
  76. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/__init__.py +0 -0
  77. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/__init__.py +0 -0
  78. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/backend_application.py +0 -0
  79. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/legacy_application.py +0 -0
  80. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/mobile_application.py +0 -0
  81. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/clients/web_application.py +0 -0
  82. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/__init__.py +0 -0
  83. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/authorization.py +0 -0
  84. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/base.py +0 -0
  85. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/introspect.py +0 -0
  86. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/metadata.py +0 -0
  87. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/pre_configured.py +0 -0
  88. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/resource.py +0 -0
  89. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/endpoints/token.py +0 -0
  90. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/errors.py +0 -0
  91. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/__init__.py +0 -0
  92. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc6749/grant_types/implicit.py +0 -0
  93. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/__init__.py +0 -0
  94. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/clients/__init__.py +0 -0
  95. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/clients/device.py +0 -0
  96. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/endpoints/__init__.py +0 -0
  97. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/endpoints/device_authorization.py +0 -0
  98. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/endpoints/pre_configured.py +0 -0
  99. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/errors.py +0 -0
  100. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/grant_types/__init__.py +0 -0
  101. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/oauth2/rfc8628/request_validator.py +0 -0
  102. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/__init__.py +0 -0
  103. {oauthlib-3.3.0/examples → oauthlib-4.0.0/oauthlib/openid/connect}/__init__.py +0 -0
  104. {oauthlib-3.3.0/oauthlib/openid/connect → oauthlib-4.0.0/oauthlib/openid/connect/core}/__init__.py +0 -0
  105. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/endpoints/__init__.py +0 -0
  106. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/endpoints/pre_configured.py +0 -0
  107. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/endpoints/userinfo.py +0 -0
  108. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/exceptions.py +0 -0
  109. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/__init__.py +0 -0
  110. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/authorization_code.py +0 -0
  111. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/base.py +0 -0
  112. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/dispatchers.py +0 -0
  113. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/hybrid.py +0 -0
  114. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/implicit.py +0 -0
  115. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/grant_types/refresh_token.py +0 -0
  116. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/request_validator.py +0 -0
  117. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/openid/connect/core/tokens.py +0 -0
  118. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/signals.py +0 -0
  119. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib/uri_validate.py +0 -0
  120. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/dependency_links.txt +0 -0
  121. {oauthlib-3.3.0 → oauthlib-4.0.0}/oauthlib.egg-info/requires.txt +0 -0
  122. {oauthlib-3.3.0 → oauthlib-4.0.0}/setup.cfg +0 -0
  123. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/__init__.py +0 -0
  124. {oauthlib-3.3.0/oauthlib/openid/connect/core → oauthlib-4.0.0/tests/oauth1}/__init__.py +0 -0
  125. {oauthlib-3.3.0/tests/oauth1 → oauthlib-4.0.0/tests/oauth1/rfc5849}/__init__.py +0 -0
  126. {oauthlib-3.3.0/tests/oauth1/rfc5849 → oauthlib-4.0.0/tests/oauth1/rfc5849/endpoints}/__init__.py +0 -0
  127. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_parameters.py +0 -0
  128. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_request_validator.py +0 -0
  129. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_signatures.py +0 -0
  130. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth1/rfc5849/test_utils.py +0 -0
  131. {oauthlib-3.3.0/tests/oauth1/rfc5849/endpoints → oauthlib-4.0.0/tests/oauth2}/__init__.py +0 -0
  132. {oauthlib-3.3.0/tests/oauth2 → oauthlib-4.0.0/tests/oauth2/rfc6749}/__init__.py +0 -0
  133. {oauthlib-3.3.0/tests/oauth2/rfc6749 → oauthlib-4.0.0/tests/oauth2/rfc6749/clients}/__init__.py +0 -0
  134. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_backend_application.py +0 -0
  135. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_legacy_application.py +0 -0
  136. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_mobile_application.py +0 -0
  137. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/clients/test_service_application.py +0 -0
  138. {oauthlib-3.3.0/tests/oauth2/rfc6749/clients → oauthlib-4.0.0/tests/oauth2/rfc6749/endpoints}/__init__.py +0 -0
  139. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_client_authentication.py +0 -0
  140. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_extra_credentials.py +0 -0
  141. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_introspect_endpoint.py +0 -0
  142. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py +0 -0
  143. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/endpoints/test_utils.py +0 -0
  144. {oauthlib-3.3.0/tests/oauth2/rfc6749/endpoints → oauthlib-4.0.0/tests/oauth2/rfc6749/grant_types}/__init__.py +0 -0
  145. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_request_validator.py +0 -0
  146. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_tokens.py +0 -0
  147. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc6749/test_utils.py +0 -0
  148. {oauthlib-3.3.0/tests/oauth2/rfc6749/grant_types → oauthlib-4.0.0/tests/oauth2/rfc8628}/__init__.py +0 -0
  149. {oauthlib-3.3.0/tests/oauth2/rfc8628 → oauthlib-4.0.0/tests/oauth2/rfc8628/clients}/__init__.py +0 -0
  150. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/clients/test_device.py +0 -0
  151. {oauthlib-3.3.0/tests/oauth2/rfc8628/clients → oauthlib-4.0.0/tests/oauth2/rfc8628/endpoints}/__init__.py +0 -0
  152. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/endpoints/test_device_application_server.py +0 -0
  153. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/endpoints/test_error_responses.py +0 -0
  154. {oauthlib-3.3.0/tests/oauth2/rfc8628/endpoints → oauthlib-4.0.0/tests/oauth2/rfc8628/grant_types}/__init__.py +0 -0
  155. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/oauth2/rfc8628/test_server.py +0 -0
  156. {oauthlib-3.3.0/tests/oauth2/rfc8628/grant_types → oauthlib-4.0.0/tests/openid}/__init__.py +0 -0
  157. {oauthlib-3.3.0/tests/openid → oauthlib-4.0.0/tests/openid/connect}/__init__.py +0 -0
  158. {oauthlib-3.3.0/tests/openid/connect → oauthlib-4.0.0/tests/openid/connect/core}/__init__.py +0 -0
  159. {oauthlib-3.3.0/tests/openid/connect/core → oauthlib-4.0.0/tests/openid/connect/core/endpoints}/__init__.py +0 -0
  160. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/endpoints/test_userinfo_endpoint.py +0 -0
  161. {oauthlib-3.3.0/tests/openid/connect/core/endpoints → oauthlib-4.0.0/tests/openid/connect/core/grant_types}/__init__.py +0 -0
  162. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_base.py +0 -0
  163. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/grant_types/test_dispatchers.py +0 -0
  164. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/test_request_validator.py +0 -0
  165. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/openid/connect/core/test_tokens.py +0 -0
  166. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/test_uri_validate.py +0 -0
  167. {oauthlib-3.3.0 → oauthlib-4.0.0}/tests/unittest/__init__.py +0 -0
@@ -1,6 +1,40 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 4.0.0 (2026-09-28):
5
+ ------------------
6
+ OAuth2.0 Provider:
7
+ * **Breaking**: #951: Removed JSONP support from token revocation endpoint.
8
+ JSONP has been superseded by CORS for cross-origin requests.
9
+ The ``enable_jsonp`` parameter has been removed from ``RevocationEndpoint``
10
+ and the ``callback`` parameter has been removed from
11
+ ``prepare_token_revocation_request``.
12
+ * **Breaking**: #919, #920: Fixed ``DeviceCodeGrant.validate_token_request``
13
+ trying to authenticate public clients.
14
+ Client authentication validation has been reorganized and is now shared
15
+ across ``AuthorizationCodeGrant``, ``DeviceCodeGrant``, ``RefreshTokenGrant``
16
+ and ``ResourceOwnerPasswordCredentialsGrant``: the ``grant_type`` parameter
17
+ is validated before client authentication, so requests missing
18
+ ``grant_type`` now return ``400 invalid_request`` instead of
19
+ ``401 invalid_client``.
20
+ * #963: Improved PKCE code comparison
21
+
22
+ Misc:
23
+ * #904: Stop installing ``examples`` into ``site-packages``.
24
+ * #930: Add devcontainer, Add Python3.14, Python3.14t.
25
+ * #931: Fix ruff checks about unused variables.
26
+ * #932: Dropped EOL Python 3.8 from CI.
27
+ * #934: Pre-commit hooks autoupdate.
28
+ * #938: Fix typos discovered by typos.
29
+ * Add OAuthLib Maintainer agent for automated issue/PR triage and release
30
+ management.
31
+
32
+ 3.3.1 (2025-06-19):
33
+ ------------------
34
+ OAuth2.0 Client:
35
+ * #906: fix regression of expires_in parsing when float in string.
36
+
37
+
4
38
  3.3.0 (2025-06-17):
5
39
  ------------------
6
40
  OAuth2.0 Provider:
@@ -95,7 +129,7 @@ General
95
129
  * #727: build isort integration
96
130
  * #734: python2 code removal
97
131
  * #735, #750: add python3.8 support
98
- * #749: bump minimum versions of pyjwt and cryptography
132
+ * #749: bump minimum versions of pyjwt and cryptography
99
133
 
100
134
  3.1.0 (2019-08-06)
101
135
  ------------------
@@ -407,7 +441,7 @@ Draft revocation endpoint features and numerous fixes including:
407
441
 
408
442
  * (OAuth 2 Provider) returned JSON headers no longer include charset.
409
443
 
410
- * (OAuth 2 Provider) validate_authorizatoin_request now also includes the
444
+ * (OAuth 2 Provider) validate_authorization_request now also includes the
411
445
  internal request object in the returned dictionary. Note that this is
412
446
  not meant to be relied upon heavily and its interface might change.
413
447
 
@@ -1,12 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oauthlib
3
- Version: 3.3.0
3
+ Version: 4.0.0
4
4
  Summary: A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
5
5
  Home-page: https://github.com/oauthlib/oauthlib
6
6
  Author: The OAuthlib Community
7
7
  Maintainer: Jonathan Huot
8
8
  Maintainer-email: jonathan.huot@gmail.com
9
9
  License: BSD-3-Clause
10
+ Project-URL: Changelog, https://github.com/oauthlib/oauthlib/blob/master/CHANGELOG.rst
11
+ Project-URL: Documentation, https://oauthlib.readthedocs.io/
12
+ Project-URL: Gitter, https://gitter.im/oauthlib/Lobby
13
+ Project-URL: Issues, https://github.com/oauthlib/oauthlib/issues
14
+ Project-URL: Source, https://github.com/oauthlib/oauthlib
15
+ Project-URL: Sponsor, https://github.com/sponsors/JonathanHuot
10
16
  Platform: any
11
17
  Classifier: Development Status :: 5 - Production/Stable
12
18
  Classifier: Environment :: Web Environment
@@ -16,18 +22,18 @@ Classifier: Operating System :: POSIX
16
22
  Classifier: Operating System :: POSIX :: Linux
17
23
  Classifier: Programming Language :: Python
18
24
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
25
  Classifier: Programming Language :: Python :: 3.9
21
26
  Classifier: Programming Language :: Python :: 3.10
22
27
  Classifier: Programming Language :: Python :: 3.11
23
28
  Classifier: Programming Language :: Python :: 3.12
24
29
  Classifier: Programming Language :: Python :: 3.13
30
+ Classifier: Programming Language :: Python :: 3.14
25
31
  Classifier: Programming Language :: Python :: 3 :: Only
26
32
  Classifier: Programming Language :: Python :: Implementation
27
33
  Classifier: Programming Language :: Python :: Implementation :: CPython
28
34
  Classifier: Programming Language :: Python :: Implementation :: PyPy
29
35
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
30
- Requires-Python: >=3.8
36
+ Requires-Python: >=3.9
31
37
  Description-Content-Type: text/x-rst
32
38
  License-File: LICENSE
33
39
  Provides-Extra: rsa
@@ -47,6 +53,7 @@ Dynamic: license-file
47
53
  Dynamic: maintainer
48
54
  Dynamic: maintainer-email
49
55
  Dynamic: platform
56
+ Dynamic: project-url
50
57
  Dynamic: provides-extra
51
58
  Dynamic: requires-python
52
59
  Dynamic: summary
@@ -57,6 +64,9 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
57
64
  *A generic, spec-compliant, thorough implementation of the OAuth request-signing
58
65
  logic for Python 3.8+*
59
66
 
67
+ .. image:: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue
68
+ :target: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/oauthlib/oauthlib
69
+ :alt: Open DevContainer in VSCode
60
70
  .. image:: https://github.com/oauthlib/oauthlib/actions/workflows/python-build.yml/badge.svg
61
71
  :target: https://github.com/oauthlib/oauthlib/actions
62
72
  :alt: GitHub Actions
@@ -4,6 +4,9 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
4
4
  *A generic, spec-compliant, thorough implementation of the OAuth request-signing
5
5
  logic for Python 3.8+*
6
6
 
7
+ .. image:: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue
8
+ :target: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/oauthlib/oauthlib
9
+ :alt: Open DevContainer in VSCode
7
10
  .. image:: https://github.com/oauthlib/oauthlib/actions/workflows/python-build.yml/badge.svg
8
11
  :target: https://github.com/oauthlib/oauthlib/actions
9
12
  :alt: GitHub Actions
@@ -12,7 +12,7 @@ import logging
12
12
  from logging import NullHandler
13
13
 
14
14
  __author__ = 'The OAuthlib Community'
15
- __version__ = '3.3.0'
15
+ __version__ = '4.0.0'
16
16
 
17
17
  logging.getLogger('oauthlib').addHandler(NullHandler())
18
18
 
@@ -371,6 +371,7 @@ class Request:
371
371
  "redirect_uri": None,
372
372
  "refresh_token": None,
373
373
  "request_token": None,
374
+ "resource": None,
374
375
  "response_type": None,
375
376
  "scope": None,
376
377
  "scopes": None,
@@ -102,7 +102,7 @@ class AccessTokenEndpoint(BaseEndpoint):
102
102
  resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
103
103
  try:
104
104
  request = self._create_request(uri, http_method, body, headers)
105
- valid, processed_request = self.validate_access_token_request(
105
+ valid, _processed_request = self.validate_access_token_request(
106
106
  request)
107
107
  if valid:
108
108
  token = self.create_access_token(request, credentials or {})
@@ -183,7 +183,7 @@ class AccessTokenEndpoint(BaseEndpoint):
183
183
  # .. _`Section 3.2`: https://tools.ietf.org/html/rfc5849#section-3.2
184
184
  #
185
185
  # Note that early exit would enable resource owner authorization
186
- # verifier enumertion.
186
+ # verifier enumeration.
187
187
  valid_verifier = self.request_validator.validate_verifier(
188
188
  request.client_key,
189
189
  request.resource_owner_key,
@@ -73,7 +73,7 @@ class BaseEndpoint:
73
73
  request = Request(uri, http_method, body, headers)
74
74
  else:
75
75
  request = Request(uri, http_method, '', headers)
76
- signature_type, params, oauth_params = (
76
+ _signature_type, params, oauth_params = (
77
77
  self._get_signature_type_and_params(request))
78
78
 
79
79
  # The server SHOULD return a 400 (Bad Request) status code when
@@ -97,7 +97,7 @@ class RequestTokenEndpoint(BaseEndpoint):
97
97
  resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
98
98
  try:
99
99
  request = self._create_request(uri, http_method, body, headers)
100
- valid, processed_request = self.validate_request_token_request(
100
+ valid, _processed_request = self.validate_request_token_request(
101
101
  request)
102
102
  if valid:
103
103
  token = self.create_request_token(request, credentials or {})
@@ -2,7 +2,7 @@
2
2
  oauthlib.oauth1.rfc5849.errors
3
3
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
4
 
5
- Error used both by OAuth 1 clients and provicers to represent the spec
5
+ Error used both by OAuth 1 clients and providers to represent the spec
6
6
  defined error responses for all four core grant types.
7
7
  """
8
8
  from oauthlib.common import add_params_to_uri, urlencode
@@ -66,5 +66,6 @@ from .rfc6749.request_validator import RequestValidator
66
66
  from .rfc6749.tokens import BearerToken, OAuth2Token
67
67
  from .rfc6749.utils import is_secure_transport
68
68
  from .rfc8628.clients import DeviceClient
69
- from oauthlib.oauth2.rfc8628.endpoints import DeviceAuthorizationEndpoint, DeviceApplicationServer
70
- from oauthlib.oauth2.rfc8628.grant_types import DeviceCodeGrant
69
+ from .rfc8628.endpoints import DeviceApplicationServer, DeviceAuthorizationEndpoint
70
+ from .rfc8628.errors import AccessDenied, AuthorizationPendingError, ExpiredTokenError, SlowDownError
71
+ from .rfc8628.grant_types import DeviceCodeGrant
@@ -320,7 +320,7 @@ class Client:
320
320
  return token_url, FORM_ENC_HEADERS, body
321
321
 
322
322
  def prepare_token_revocation_request(self, revocation_url, token,
323
- token_type_hint="access_token", body='', callback=None, **kwargs):
323
+ token_type_hint="access_token", body='', **kwargs):
324
324
  """Prepare a token revocation request.
325
325
 
326
326
  :param revocation_url: Provider token revocation endpoint URL.
@@ -329,14 +329,9 @@ class Client:
329
329
  ``"refresh_token"``. This is optional and if you wish to not pass it you
330
330
  must provide ``token_type_hint=None``.
331
331
  :param body:
332
- :param callback: A jsonp callback such as ``package.callback`` to be invoked
333
- upon receiving the response. Not that it should not include a () suffix.
334
332
  :param kwargs: Additional parameters to included in the request.
335
333
  :returns: The prepared request tuple with (url, headers, body).
336
334
 
337
- Note that JSONP request may use GET requests as the parameters will
338
- be added to the request URL query as opposed to the request body.
339
-
340
335
  An example of a revocation request
341
336
 
342
337
  .. code-block:: http
@@ -348,21 +343,6 @@ class Client:
348
343
 
349
344
  token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token
350
345
 
351
- An example of a jsonp revocation request
352
-
353
- .. code-block:: http
354
-
355
- GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
356
- Host: server.example.com
357
- Content-Type: application/x-www-form-urlencoded
358
- Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
359
-
360
- and an error response
361
-
362
- .. code-block:: javascript
363
-
364
- package.myCallback({"error":"unsupported_token_type"});
365
-
366
346
  Note that these requests usually require client credentials, client_id in
367
347
  the case for public clients and provider specific authentication
368
348
  credentials for confidential clients.
@@ -370,8 +350,11 @@ class Client:
370
350
  if not is_secure_transport(revocation_url):
371
351
  raise InsecureTransportError()
372
352
 
353
+ if 'callback' in kwargs:
354
+ raise TypeError("prepare_token_revocation_request() got an unexpected keyword argument 'callback'")
355
+
373
356
  return prepare_token_revocation_request(revocation_url, token,
374
- token_type_hint=token_type_hint, body=body, callback=callback,
357
+ token_type_hint=token_type_hint, body=body,
375
358
  **kwargs)
376
359
 
377
360
  def parse_request_body_response(self, body, scope=None, **kwargs):
@@ -17,7 +17,7 @@ from .base import Client
17
17
  class ServiceApplicationClient(Client):
18
18
  """A public client utilizing the JWT bearer grant.
19
19
 
20
- JWT bearer tokes can be used to request an access token when a client
20
+ JWT bearer tokens can be used to request an access token when a client
21
21
  wishes to utilize an existing trust relationship, expressed through the
22
22
  semantics of (and digital signature or keyed message digest calculated
23
23
  over) the JWT, without a direct user approval step at the authorization
@@ -27,13 +27,11 @@ class RevocationEndpoint(BaseEndpoint):
27
27
  valid_token_types = ('access_token', 'refresh_token')
28
28
  valid_request_methods = ('POST',)
29
29
 
30
- def __init__(self, request_validator, supported_token_types=None,
31
- enable_jsonp=False):
30
+ def __init__(self, request_validator, supported_token_types=None):
32
31
  BaseEndpoint.__init__(self)
33
32
  self.request_validator = request_validator
34
33
  self.supported_token_types = (
35
34
  supported_token_types or self.valid_token_types)
36
- self.enable_jsonp = enable_jsonp
37
35
 
38
36
  @catch_errors_and_unavailability
39
37
  def create_revocation_response(self, uri, http_method='POST', body=None,
@@ -69,18 +67,13 @@ class RevocationEndpoint(BaseEndpoint):
69
67
  except OAuth2Error as e:
70
68
  log.debug('Client error during validation of %r. %r.', request, e)
71
69
  response_body = e.json
72
- if self.enable_jsonp and request.callback:
73
- response_body = '{}({});'.format(request.callback, response_body)
74
70
  resp_headers.update(e.headers)
75
71
  return resp_headers, response_body, e.status_code
76
72
 
77
73
  self.request_validator.revoke_token(request.token,
78
74
  request.token_type_hint, request)
79
75
 
80
- response_body = ''
81
- if self.enable_jsonp and request.callback:
82
- response_body = request.callback + '();'
83
- return {}, response_body, 200
76
+ return {}, '', 200
84
77
 
85
78
  def validate_revocation_request(self, request):
86
79
  """Ensure the request is valid.
@@ -4,6 +4,7 @@ oauthlib.oauth2.rfc6749.grant_types
4
4
  """
5
5
  import base64
6
6
  import hashlib
7
+ import hmac
7
8
  import json
8
9
  import logging
9
10
 
@@ -39,14 +40,15 @@ def code_challenge_method_s256(verifier, challenge):
39
40
  return s;
40
41
  }
41
42
 
42
- In python urlsafe_b64encode is already replacing '+' and '/', but preserve
43
- the trailing '='. So we have to remove it.
43
+ Python's urlsafe_b64encode() replaces '+' and '/', but preserves the
44
+ trailing '=', so we have to remove it.
44
45
 
45
46
  .. _`Section 4.3`: https://tools.ietf.org/html/rfc7636#section-4.3
46
47
  """
47
- return base64.urlsafe_b64encode(
48
+ expected = base64.urlsafe_b64encode(
48
49
  hashlib.sha256(verifier.encode()).digest()
49
- ).decode().rstrip('=') == challenge
50
+ ).decode().rstrip('=')
51
+ return hmac.compare_digest(expected, challenge)
50
52
 
51
53
 
52
54
  def code_challenge_method_plain(verifier, challenge):
@@ -58,7 +60,7 @@ def code_challenge_method_plain(verifier, challenge):
58
60
 
59
61
  .. _`Section 4.3`: https://tools.ietf.org/html/rfc7636#section-4.3
60
62
  """
61
- return verifier == challenge
63
+ return hmac.compare_digest(verifier, challenge)
62
64
 
63
65
 
64
66
  class AuthorizationCodeGrant(GrantTypeBase):
@@ -452,28 +454,7 @@ class AuthorizationCodeGrant(GrantTypeBase):
452
454
  raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
453
455
  request=request)
454
456
 
455
- if self.request_validator.client_authentication_required(request):
456
- # If the client type is confidential or the client was issued client
457
- # credentials (or assigned other authentication requirements), the
458
- # client MUST authenticate with the authorization server as described
459
- # in Section 3.2.1.
460
- # https://tools.ietf.org/html/rfc6749#section-3.2.1
461
- if not self.request_validator.authenticate_client(request):
462
- log.debug('Client authentication failed, %r.', request)
463
- raise errors.InvalidClientError(request=request)
464
- elif not self.request_validator.authenticate_client_id(request.client_id, request):
465
- # REQUIRED, if the client is not authenticating with the
466
- # authorization server as described in Section 3.2.1.
467
- # https://tools.ietf.org/html/rfc6749#section-3.2.1
468
- log.debug('Client authentication failed, %r.', request)
469
- raise errors.InvalidClientError(request=request)
470
-
471
- if not hasattr(request.client, 'client_id'):
472
- raise NotImplementedError('Authenticate client must set the '
473
- 'request.client.client_id attribute '
474
- 'in authenticate_client.')
475
-
476
- request.client_id = request.client_id or request.client.client_id
457
+ self.validate_client_authentication(request)
477
458
 
478
459
  # Ensure client is authorized use of this grant type
479
460
  self.validate_grant_type(request)
@@ -175,6 +175,57 @@ class GrantTypeBase:
175
175
  request.scopes, request.client, request):
176
176
  raise errors.InvalidScopeError(request=request)
177
177
 
178
+ def validate_client_confidential(self, request):
179
+ # If the client type is confidential or the client was issued client
180
+ # credentials (or assigned other authentication requirements), the
181
+ # client MUST authenticate with the authorization server as described
182
+ # in Section 3.2.1.
183
+ # https://tools.ietf.org/html/rfc6749#section-3.2.1
184
+ log.debug('Authenticating confidential client, %r.', request)
185
+ if not self.request_validator.authenticate_client(request):
186
+ log.debug('Client authentication failed, %r.', request)
187
+ raise errors.InvalidClientError(request=request)
188
+
189
+ if request.client is None or \
190
+ not hasattr(request.client, 'client_id'):
191
+ raise NotImplementedError('Authenticate client must set the '
192
+ 'request.client.client_id attribute '
193
+ 'in authenticate_client.')
194
+
195
+ if not request.client_id:
196
+ request.client_id = request.client.client_id
197
+ elif request.client_id != request.client.client_id:
198
+ raise errors.ServerError('Discrepency found between client identifier')
199
+
200
+ def validate_client_public(self, request):
201
+ # REQUIRED, if the client is not authenticating with the
202
+ # authorization server as described in Section 3.2.1.
203
+ # https://tools.ietf.org/html/rfc6749#section-3.2.1
204
+ log.debug('Authenticating public client, %r.', request)
205
+ if not self.request_validator.authenticate_client_id(request.client_id, request):
206
+ log.debug('Client authentication failed, %r.', request)
207
+ raise errors.InvalidClientError(request=request)
208
+
209
+ if request.client is None or \
210
+ not hasattr(request.client, 'client_id'):
211
+ raise NotImplementedError('Authenticate client must set the '
212
+ 'request.client.client_id attribute '
213
+ 'in authenticate_client.')
214
+
215
+ if not request.client_id:
216
+ request.client_id = request.client.client_id
217
+ elif request.client_id != request.client.client_id:
218
+ raise errors.ServerError('Discrepency found between client identifier')
219
+
220
+ def validate_client_authentication(self, request):
221
+ """Raise on failed client authentication."""
222
+ # Handles confidential clients
223
+ if self.request_validator.client_authentication_required(request):
224
+ self.validate_client_confidential(request)
225
+ # Handles public clients
226
+ else:
227
+ self.validate_client_public(request)
228
+
178
229
  def prepare_authorization_response(self, request, token, headers, body, status):
179
230
  """Place token according to response mode.
180
231
 
@@ -103,18 +103,11 @@ class ClientCredentialsGrant(GrantTypeBase):
103
103
  raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param,
104
104
  request=request)
105
105
 
106
- log.debug('Authenticating client, %r.', request)
107
- if not self.request_validator.authenticate_client(request):
108
- log.debug('Client authentication failed, %r.', request)
109
- raise errors.InvalidClientError(request=request)
110
- elif not hasattr(request.client, 'client_id'):
111
- raise NotImplementedError('Authenticate client must set the '
112
- 'request.client.client_id attribute '
113
- 'in authenticate_client.')
106
+ self.validate_client_confidential(request)
107
+
114
108
  # Ensure client is authorized use of this grant type
115
109
  self.validate_grant_type(request)
116
110
 
117
- request.client_id = request.client_id or request.client.client_id
118
111
  log.debug('Authorizing access to client %r.', request.client_id)
119
112
  self.validate_scopes(request)
120
113
 
@@ -91,22 +91,8 @@ class RefreshTokenGrant(GrantTypeBase):
91
91
 
92
92
  # Because refresh tokens are typically long-lasting credentials used to
93
93
  # request additional access tokens, the refresh token is bound to the
94
- # client to which it was issued. If the client type is confidential or
95
- # the client was issued client credentials (or assigned other
96
- # authentication requirements), the client MUST authenticate with the
97
- # authorization server as described in Section 3.2.1.
98
- # https://tools.ietf.org/html/rfc6749#section-3.2.1
99
- if self.request_validator.client_authentication_required(request):
100
- log.debug('Authenticating client, %r.', request)
101
- if not self.request_validator.authenticate_client(request):
102
- log.debug('Invalid client (%r), denying access.', request)
103
- raise errors.InvalidClientError(request=request)
104
- # Ensure that request.client_id is set.
105
- if request.client_id is None and request.client is not None:
106
- request.client_id = request.client.client_id
107
- elif not self.request_validator.authenticate_client_id(request.client_id, request):
108
- log.debug('Client authentication failed, %r.', request)
109
- raise errors.InvalidClientError(request=request)
94
+ # client to which it was issued
95
+ self.validate_client_authentication(request)
110
96
 
111
97
  # Ensure client is authorized use of this grant type
112
98
  self.validate_grant_type(request)
@@ -85,14 +85,6 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
85
85
  """
86
86
  headers = self._get_default_headers()
87
87
  try:
88
- if self.request_validator.client_authentication_required(request):
89
- log.debug('Authenticating client, %r.', request)
90
- if not self.request_validator.authenticate_client(request):
91
- log.debug('Client authentication failed, %r.', request)
92
- raise errors.InvalidClientError(request=request)
93
- elif not self.request_validator.authenticate_client_id(request.client_id, request):
94
- log.debug('Client authentication failed, %r.', request)
95
- raise errors.InvalidClientError(request=request)
96
88
  log.debug('Validating access token request, %r.', request)
97
89
  self.validate_token_request(request)
98
90
  except errors.OAuth2Error as e:
@@ -175,23 +167,18 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
175
167
  if not request.grant_type == 'password':
176
168
  raise errors.UnsupportedGrantTypeError(request=request)
177
169
 
170
+ self.validate_client_authentication(request)
171
+
178
172
  log.debug('Validating username %s.', request.username)
179
173
  if not self.request_validator.validate_user(request.username,
180
174
  request.password, request.client, request):
181
175
  raise errors.InvalidGrantError(
182
176
  'Invalid credentials given.', request=request)
183
- elif not hasattr(request.client, 'client_id'):
184
- raise NotImplementedError(
185
- 'Validate user must set the '
186
- 'request.client.client_id attribute '
187
- 'in authenticate_client.')
188
177
  log.debug('Authorizing access to user %r.', request.user)
189
178
 
190
179
  # Ensure client is authorized use of this grant type
191
180
  self.validate_grant_type(request)
192
181
 
193
- if request.client:
194
- request.client_id = request.client_id or request.client.client_id
195
182
  self.validate_scopes(request)
196
183
 
197
184
  for validator in self.custom_validators.post_token:
@@ -172,7 +172,7 @@ def prepare_token_request(grant_type, body='', include_client_id=True, code_veri
172
172
 
173
173
 
174
174
  def prepare_token_revocation_request(url, token, token_type_hint="access_token",
175
- callback=None, body='', **kwargs):
175
+ body='', **kwargs):
176
176
  """Prepare a token revocation request.
177
177
 
178
178
  The client constructs the request by including the following parameters
@@ -216,17 +216,16 @@ def prepare_token_revocation_request(url, token, token_type_hint="access_token",
216
216
  if token_type_hint:
217
217
  params.append(('token_type_hint', token_type_hint))
218
218
 
219
+ if 'callback' in kwargs:
220
+ raise TypeError("prepare_token_revocation_request() got an unexpected keyword argument 'callback'")
221
+
219
222
  for k in kwargs:
220
223
  if kwargs[k]:
221
224
  params.append((str(k), kwargs[k]))
222
225
 
223
226
  headers = {'Content-Type': 'application/x-www-form-urlencoded'}
224
227
 
225
- if callback:
226
- params.append(('callback', callback))
227
- return add_params_to_uri(url, params), headers, body
228
- else:
229
- return url, headers, add_params_to_qs(body, params)
228
+ return url, headers, add_params_to_qs(body, params)
230
229
 
231
230
 
232
231
  def parse_authorization_code_response(uri, state=None):
@@ -339,7 +338,7 @@ def parse_implicit_response(uri, state=None, scope=None):
339
338
  if 'scope' in params:
340
339
  params['scope'] = scope_to_list(params['scope'])
341
340
 
342
- vin, vat, v_at = parse_expires(params)
341
+ vin, vat, _v_at = parse_expires(params)
343
342
  if vin:
344
343
  params['expires_in'] = vin
345
344
  elif 'expires_in' in params:
@@ -430,7 +429,7 @@ def parse_token_response(body, scope=None):
430
429
  if 'scope' in params:
431
430
  params['scope'] = scope_to_list(params['scope'])
432
431
 
433
- vin, vat, v_at = parse_expires(params)
432
+ vin, vat, _v_at = parse_expires(params)
434
433
  if vin:
435
434
  params['expires_in'] = vin
436
435
  elif 'expires_in' in params:
@@ -476,7 +475,7 @@ def parse_expires(params):
476
475
  """Parse `expires_in`, `expires_at` fields from params
477
476
 
478
477
  Parse following these rules:
479
- - `expires_in` must be either blank or a valid integer.
478
+ - `expires_in` must be either integer, float or None. If a float, it is converted into an integer.
480
479
  - `expires_at` is not in specification so it does its best to:
481
480
  - convert into a int, else
482
481
  - convert into a float, else
@@ -495,14 +494,16 @@ def parse_expires(params):
495
494
  if 'expires_in' in params:
496
495
  if isinstance(params.get('expires_in'), int):
497
496
  expires_in = params.get('expires_in')
497
+ elif isinstance(params.get('expires_in'), float):
498
+ expires_in = int(params.get('expires_in'))
498
499
  elif isinstance(params.get('expires_in'), str):
499
500
  try:
500
501
  # Attempt to convert to int
501
502
  expires_in = int(params.get('expires_in'))
502
503
  except ValueError:
503
- raise ValueError("expires_int must be an int")
504
+ raise ValueError("expires_in must be an int")
504
505
  elif params.get('expires_in') is not None:
505
- raise ValueError("expires_int must be an int")
506
+ raise ValueError("expires_in must be an int")
506
507
 
507
508
  if 'expires_at' in params:
508
509
  if isinstance(params.get('expires_at'), (float, int)):