oidcauthlib 3.0.5__tar.gz → 3.0.7__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 (137) hide show
  1. oidcauthlib-3.0.7/MANIFEST.in +1 -0
  2. {oidcauthlib-3.0.5/oidcauthlib.egg-info → oidcauthlib-3.0.7}/PKG-INFO +4 -14
  3. oidcauthlib-3.0.7/VERSION +1 -0
  4. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/auth_helper.py +2 -6
  5. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/auth_manager.py +34 -53
  6. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/oauth_mongo_cache.py +13 -35
  7. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/config/auth_config.py +1 -2
  8. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/config/auth_config_reader.py +24 -72
  9. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/dcr_client.py +3 -6
  10. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/dcr_manager.py +11 -21
  11. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/dcr_registration.py +3 -9
  12. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/fastapi_auth_manager.py +83 -34
  13. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/middleware/request_scope_middleware.py +4 -11
  14. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/middleware/token_reader_middleware.py +7 -21
  15. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/auth.py +2 -6
  16. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/cache_item.py +1 -3
  17. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/client_key_set.py +2 -6
  18. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/token.py +6 -26
  19. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/base_repository.py +2 -6
  20. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/memory/memory_repository.py +2 -8
  21. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/mongo/mongo_repository.py +14 -42
  22. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/repository_factory.py +2 -6
  23. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/routers/auth_router.py +9 -29
  24. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/token_reader.py +8 -26
  25. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/auth_server_metadata_discovery.py +1 -2
  26. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache.py +35 -92
  27. oidcauthlib-3.0.7/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache_result.py +9 -0
  28. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/well_known_configuration_manager.py +2 -6
  29. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/container/oidc_authlib_container_factory.py +5 -15
  30. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/mongo_gridfs_db.py +26 -77
  31. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/mongo_storage_factory.py +5 -13
  32. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/storage_factory_creator.py +2 -5
  33. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/environment/oidc_environment_variables.py +8 -24
  34. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/logging_response.py +2 -6
  35. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/mongo_url_utils.py +1 -3
  36. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/url_validator.py +33 -11
  37. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7/oidcauthlib.egg-info}/PKG-INFO +4 -14
  38. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/SOURCES.txt +2 -50
  39. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/top_level.txt +0 -1
  40. oidcauthlib-3.0.7/pyproject.toml +107 -0
  41. oidcauthlib-3.0.7/setup.cfg +4 -0
  42. oidcauthlib-3.0.5/MANIFEST.in +0 -6
  43. oidcauthlib-3.0.5/Makefile +0 -85
  44. oidcauthlib-3.0.5/VERSION +0 -1
  45. oidcauthlib-3.0.5/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache_result.py +0 -15
  46. oidcauthlib-3.0.5/oidcauthlib.egg-info/not-zip-safe +0 -1
  47. oidcauthlib-3.0.5/setup.cfg +0 -29
  48. oidcauthlib-3.0.5/setup.py +0 -82
  49. oidcauthlib-3.0.5/tests/__init__.py +0 -0
  50. oidcauthlib-3.0.5/tests/auth/__init__.py +0 -0
  51. oidcauthlib-3.0.5/tests/auth/cache/__init__.py +0 -0
  52. oidcauthlib-3.0.5/tests/auth/cache/test_oauth_memory_cache.py +0 -26
  53. oidcauthlib-3.0.5/tests/auth/config/__init__.py +0 -0
  54. oidcauthlib-3.0.5/tests/auth/config/test_auth_config.py +0 -72
  55. oidcauthlib-3.0.5/tests/auth/config/test_auth_config_reader.py +0 -91
  56. oidcauthlib-3.0.5/tests/auth/config/test_auth_config_reader_register.py +0 -200
  57. oidcauthlib-3.0.5/tests/auth/config/test_auth_config_reader_thread_safety.py +0 -127
  58. oidcauthlib-3.0.5/tests/auth/conftest.py +0 -14
  59. oidcauthlib-3.0.5/tests/auth/dcr/__init__.py +0 -0
  60. oidcauthlib-3.0.5/tests/auth/dcr/test_dcr_client.py +0 -129
  61. oidcauthlib-3.0.5/tests/auth/dcr/test_dcr_manager.py +0 -198
  62. oidcauthlib-3.0.5/tests/auth/middleware/__init__.py +0 -0
  63. oidcauthlib-3.0.5/tests/auth/middleware/test_request_scope_middleware.py +0 -188
  64. oidcauthlib-3.0.5/tests/auth/middleware/test_token_reader_middleware.py +0 -671
  65. oidcauthlib-3.0.5/tests/auth/models/__init__.py +0 -0
  66. oidcauthlib-3.0.5/tests/auth/models/test_auth.py +0 -43
  67. oidcauthlib-3.0.5/tests/auth/models/test_base_db_model.py +0 -70
  68. oidcauthlib-3.0.5/tests/auth/models/test_cache_item.py +0 -17
  69. oidcauthlib-3.0.5/tests/auth/models/test_token.py +0 -48
  70. oidcauthlib-3.0.5/tests/auth/repository/__init__.py +0 -0
  71. oidcauthlib-3.0.5/tests/auth/repository/memory/__init__.py +0 -0
  72. oidcauthlib-3.0.5/tests/auth/repository/memory/test_memory_repository.py +0 -156
  73. oidcauthlib-3.0.5/tests/auth/repository/mongo/__init__.py +0 -0
  74. oidcauthlib-3.0.5/tests/auth/repository/mongo/test_mongo_repository.py +0 -912
  75. oidcauthlib-3.0.5/tests/auth/repository/mongo/test_mongo_repository_real.py +0 -178
  76. oidcauthlib-3.0.5/tests/auth/test_auth_helper.py +0 -26
  77. oidcauthlib-3.0.5/tests/auth/test_auth_helper2.py +0 -33
  78. oidcauthlib-3.0.5/tests/auth/test_auth_manager.py +0 -514
  79. oidcauthlib-3.0.5/tests/auth/test_register_dynamic_provider.py +0 -348
  80. oidcauthlib-3.0.5/tests/auth/well_known_configuration/__init__.py +0 -0
  81. oidcauthlib-3.0.5/tests/auth/well_known_configuration/test_auth_server_metadata_discovery.py +0 -184
  82. oidcauthlib-3.0.5/tests/auth/well_known_configuration/test_well_known_configuration_cache.py +0 -393
  83. oidcauthlib-3.0.5/tests/auth/well_known_configuration/test_well_known_configuration_manager.py +0 -174
  84. oidcauthlib-3.0.5/tests/conftest.py +0 -130
  85. oidcauthlib-3.0.5/tests/storage/__init__.py +0 -0
  86. oidcauthlib-3.0.5/tests/storage/test_mongo_gridfs_db.py +0 -378
  87. oidcauthlib-3.0.5/tests/storage/test_mongo_gridfs_race_setup.py +0 -70
  88. oidcauthlib-3.0.5/tests/storage/test_mongo_store_factory.py +0 -516
  89. oidcauthlib-3.0.5/tests/storage/test_storage_factory_security.py +0 -498
  90. oidcauthlib-3.0.5/tests/utilities/__init__.py +0 -0
  91. oidcauthlib-3.0.5/tests/utilities/test_cached.py +0 -52
  92. oidcauthlib-3.0.5/tests/utilities/test_mongo_url_utils.py +0 -30
  93. oidcauthlib-3.0.5/tests/utilities/test_url_validator.py +0 -155
  94. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/LICENSE +0 -0
  95. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/README.md +0 -0
  96. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/__init__.py +0 -0
  97. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/__init__.py +0 -0
  98. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/__init__.py +0 -0
  99. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/oauth_cache.py +0 -0
  100. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/oauth_memory_cache.py +0 -0
  101. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/config/__init__.py +0 -0
  102. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/__init__.py +0 -0
  103. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/__init__.py +0 -0
  104. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_bearer_token_expired_exception.py +0 -0
  105. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_bearer_token_invalid_exception.py +0 -0
  106. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_bearer_token_missing_exception.py +0 -0
  107. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_needed_exception.py +0 -0
  108. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/middleware/__init__.py +0 -0
  109. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/__init__.py +0 -0
  110. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/base_db_model.py +0 -0
  111. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/__init__.py +0 -0
  112. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/memory/__init__.py +0 -0
  113. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/mongo/__init__.py +0 -0
  114. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/routers/__init__.py +0 -0
  115. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/__init__.py +0 -0
  116. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/auth_server_metadata.py +0 -0
  117. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/container/__init__.py +0 -0
  118. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/open_telemetry/__init__.py +0 -0
  119. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/open_telemetry/attribute_names.py +0 -0
  120. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/open_telemetry/span_names.py +0 -0
  121. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/py.typed +0 -0
  122. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/__init__.py +0 -0
  123. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/cache_to_collection_mapper.py +0 -0
  124. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/memory_storage_factory.py +0 -0
  125. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/mongo_gridfs_exception.py +0 -0
  126. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/storage/storage_factory.py +0 -0
  127. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/__init__.py +0 -0
  128. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/cached.py +0 -0
  129. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/environment/__init__.py +0 -0
  130. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/environment/abstract_environment_variables.py +0 -0
  131. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/__init__.py +0 -0
  132. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/log_levels.py +0 -0
  133. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/logging_transport.py +0 -0
  134. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/dependency_links.txt +0 -0
  135. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/requires.txt +0 -0
  136. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/tests/test_mongo_url_utils.py +0 -0
  137. {oidcauthlib-3.0.5 → oidcauthlib-3.0.7}/tests/test_simple.py +0 -0
@@ -0,0 +1 @@
1
+ include VERSION
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oidcauthlib
3
- Version: 3.0.5
3
+ Version: 3.0.7
4
4
  Summary: oidcauthlib
5
- Home-page: https://github.com/icanbwell/oidc-auth-lib
6
- Author: Imran Qureshi
7
- Author-email: imran.qureshi@bwell.com
5
+ Author-email: Imran Qureshi <imran.qureshi@bwell.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/icanbwell/oidc-auth-lib
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: Apache Software License
11
10
  Classifier: Operating System :: OS Independent
12
11
  Requires-Python: >=3.12
13
12
  Description-Content-Type: text/markdown
@@ -23,15 +22,6 @@ Requires-Dist: starlette>=0.49.1
23
22
  Requires-Dist: py-key-value-aio[memory,mongodb,pydantic,redis]>=0.4.4
24
23
  Requires-Dist: opentelemetry-api>=1.39.1
25
24
  Requires-Dist: simple-container>=1.0.2
26
- Dynamic: author
27
- Dynamic: author-email
28
- Dynamic: classifier
29
- Dynamic: description
30
- Dynamic: description-content-type
31
- Dynamic: home-page
32
25
  Dynamic: license-file
33
- Dynamic: requires-dist
34
- Dynamic: requires-python
35
- Dynamic: summary
36
26
 
37
27
  # oidcauthlib
@@ -0,0 +1 @@
1
+ 3.0.7
@@ -21,9 +21,7 @@ class AuthHelper:
21
21
  A base64url encoded string of the content.
22
22
  """
23
23
  json_content = json.dumps(content)
24
- encoded_content = base64.urlsafe_b64encode(json_content.encode("utf-8")).decode(
25
- "utf-8"
26
- )
24
+ encoded_content = base64.urlsafe_b64encode(json_content.encode("utf-8")).decode("utf-8")
27
25
  return encoded_content.rstrip("=")
28
26
 
29
27
  @staticmethod
@@ -56,9 +54,7 @@ class AuthHelper:
56
54
  logger.error(f"Failed to decode state (JSON error): {e}")
57
55
  raise ValueError("Invalid JSON in decoded state") from e
58
56
  if not isinstance(result, dict):
59
- logger.error(
60
- "Failed to decode state: Decoded state is not a dictionary"
61
- )
57
+ logger.error("Failed to decode state: Decoded state is not a dictionary")
62
58
  raise ValueError("Decoded state is not a dictionary")
63
59
  return result
64
60
  except Exception:
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import logging
2
3
  import os
3
4
  import time
@@ -33,6 +34,7 @@ from oidcauthlib.utilities.logger.log_levels import SRC_LOG_LEVELS
33
34
  from oidcauthlib.utilities.logger.logging_transport import (
34
35
  LoggingTransport,
35
36
  )
37
+ from oidcauthlib.utilities.url_validator import validate_url
36
38
 
37
39
  logger = logging.getLogger(__name__)
38
40
  logger.setLevel(SRC_LOG_LEVELS["AUTH"])
@@ -69,17 +71,13 @@ class AuthManager:
69
71
  if self.environment_variables is None:
70
72
  raise ValueError("environment_variables must not be None")
71
73
  if not isinstance(self.environment_variables, AbstractEnvironmentVariables):
72
- raise TypeError(
73
- "environment_variables must be an instance of EnvironmentVariables"
74
- )
74
+ raise TypeError("environment_variables must be an instance of EnvironmentVariables")
75
75
 
76
76
  self.auth_config_reader: AuthConfigReader = auth_config_reader
77
77
  if self.auth_config_reader is None:
78
78
  raise ValueError("auth_config_reader must not be None")
79
79
  if not isinstance(self.auth_config_reader, AuthConfigReader):
80
- raise TypeError(
81
- "auth_config_reader must be an instance of AuthConfigReader"
82
- )
80
+ raise TypeError("auth_config_reader must be an instance of AuthConfigReader")
83
81
 
84
82
  self.token_reader: TokenReader = token_reader
85
83
  if self.token_reader is None:
@@ -87,17 +85,11 @@ class AuthManager:
87
85
  if not isinstance(self.token_reader, TokenReader):
88
86
  raise TypeError("token_reader must be an instance of TokenReader")
89
87
 
90
- self.well_known_configuration_manager: WellKnownConfigurationManager = (
91
- well_known_configuration_manager
92
- )
88
+ self.well_known_configuration_manager: WellKnownConfigurationManager = well_known_configuration_manager
93
89
  if self.well_known_configuration_manager is None:
94
90
  raise ValueError("well_known_configuration_manager must not be None")
95
- if not isinstance(
96
- self.well_known_configuration_manager, WellKnownConfigurationManager
97
- ):
98
- raise TypeError(
99
- "well_known_configuration_manager must be an instance of WellKnownConfigurationManager"
100
- )
91
+ if not isinstance(self.well_known_configuration_manager, WellKnownConfigurationManager):
92
+ raise TypeError("well_known_configuration_manager must be an instance of WellKnownConfigurationManager")
101
93
 
102
94
  self._dcr_manager: DcrManager | None = dcr_manager
103
95
 
@@ -108,9 +100,7 @@ class AuthManager:
108
100
  else OAuthMongoCache(environment_variables=environment_variables)
109
101
  )
110
102
 
111
- logger.debug(
112
- f"Initializing AuthManager with cache type {type(self.cache)} cache id: {self.cache.id}"
113
- )
103
+ logger.debug(f"Initializing AuthManager with cache type {type(self.cache)} cache id: {self.cache.id}")
114
104
  # OIDC PKCE setup
115
105
  self.redirect_uri = os.getenv("AUTH_REDIRECT_URI")
116
106
  if self.redirect_uri is None:
@@ -118,17 +108,13 @@ class AuthManager:
118
108
  # https://docs.authlib.org/en/latest/client/frameworks.html#frameworks-clients
119
109
  self._oauth: OAuth = OAuth(cache=self.cache)
120
110
  self._registered_dynamic_providers: set[str] = set()
121
- self.auth_configs: List[AuthConfig] = (
122
- self.auth_config_reader.get_auth_configs_for_all_auth_providers()
123
- )
111
+ self.auth_configs: List[AuthConfig] = self.auth_config_reader.get_auth_configs_for_all_auth_providers()
124
112
 
125
113
  async def ensure_initialized_async(self) -> None:
126
114
  auth_config: AuthConfig
127
115
  for auth_config in self.auth_configs:
128
116
  if auth_config.well_known_uri:
129
- await self.well_known_configuration_manager.get_async(
130
- auth_config=auth_config
131
- )
117
+ await self.well_known_configuration_manager.get_async(auth_config=auth_config)
132
118
  await self.register_dynamic_provider(auth_config=auth_config)
133
119
 
134
120
  async def register_dynamic_provider(
@@ -161,9 +147,7 @@ class AuthManager:
161
147
  registration_url=auth_config.registration_url,
162
148
  )
163
149
  if dcr_result is None or not dcr_result.client_id:
164
- raise ValueError(
165
- f"DCR failed to obtain client_id for '{auth_config.auth_provider}'"
166
- )
150
+ raise ValueError(f"DCR failed to obtain client_id for '{auth_config.auth_provider}'")
167
151
  client_id = dcr_result.client_id
168
152
  client_secret = dcr_result.client_secret
169
153
  logger.info(
@@ -199,9 +183,12 @@ class AuthManager:
199
183
  }
200
184
 
201
185
  if auth_config.authorization_endpoint and auth_config.token_endpoint:
186
+ validate_url(auth_config.authorization_endpoint)
187
+ validate_url(auth_config.token_endpoint)
202
188
  register_kwargs["authorize_url"] = auth_config.authorization_endpoint
203
189
  register_kwargs["access_token_url"] = auth_config.token_endpoint
204
190
  elif auth_config.well_known_uri:
191
+ validate_url(auth_config.well_known_uri)
205
192
  register_kwargs["server_metadata_url"] = auth_config.well_known_uri
206
193
  else:
207
194
  raise ValueError(
@@ -274,12 +261,20 @@ class AuthManager:
274
261
  f" with state {state_content} and encoded state {state}"
275
262
  )
276
263
 
277
- rv: Dict[str, Any] = await client.create_authorization_url(
278
- redirect_uri=redirect_uri, state=state
279
- )
264
+ rv: Dict[str, Any] = await client.create_authorization_url(redirect_uri=redirect_uri, state=state)
280
265
  logger.debug(f"Authorization URL created: {rv}")
281
- # request is only needed if we are using the session to store the state
282
- await client.save_authorize_data(request=None, redirect_uri=redirect_uri, **rv)
266
+ # Save OAuth state data (code_verifier, nonce, redirect_uri) to our
267
+ # own cache rather than relying on authlib's save_authorize_data which
268
+ # requires request.session (SessionMiddleware). This allows the
269
+ # callback to retrieve the data without SessionMiddleware.
270
+ state_data: Dict[str, Any] = {"redirect_uri": redirect_uri}
271
+ if "code_verifier" in rv:
272
+ state_data["code_verifier"] = rv["code_verifier"]
273
+ if "nonce" in rv:
274
+ state_data["nonce"] = rv["nonce"]
275
+ cache_key = f"_state_{auth_provider}_{state}"
276
+ await self.cache.set(cache_key, json.dumps({"data": state_data}))
277
+ logger.debug(f"Saved OAuth state to cache key={cache_key} data={state_data}")
283
278
  return cast(str, rv["url"])
284
279
 
285
280
  async def create_oauth_client(self, *, name: str) -> StarletteOAuth2App:
@@ -323,17 +318,11 @@ class AuthManager:
323
318
  resp.raise_for_status()
324
319
  token_url = resp.json().get("token_endpoint")
325
320
  except Exception as e:
326
- raise AuthorizationNeededException(
327
- message=f"Failed to discover token endpoint: {e}"
328
- )
321
+ raise AuthorizationNeededException(message=f"Failed to discover token endpoint: {e}")
329
322
  if not token_url and auth_config.issuer:
330
- token_url = (
331
- auth_config.issuer.rstrip("/") + "/protocol/openid-connect/token"
332
- )
323
+ token_url = auth_config.issuer.rstrip("/") + "/protocol/openid-connect/token"
333
324
  if not token_url:
334
- raise AuthorizationNeededException(
335
- message="No token endpoint found in AuthConfig."
336
- )
325
+ raise AuthorizationNeededException(message="No token endpoint found in AuthConfig.")
337
326
 
338
327
  # Prepare OAuth2 client
339
328
  client_id = auth_config.client_id
@@ -365,9 +354,7 @@ class AuthManager:
365
354
 
366
355
  return access_token
367
356
 
368
- def get_auth_config_for_auth_provider(
369
- self, *, auth_provider: str
370
- ) -> AuthConfig | None:
357
+ def get_auth_config_for_auth_provider(self, *, auth_provider: str) -> AuthConfig | None:
371
358
  if not auth_provider:
372
359
  raise ValueError("auth_provider must not be empty")
373
360
  for auth_config in self.auth_configs:
@@ -376,9 +363,7 @@ class AuthManager:
376
363
  return None
377
364
 
378
365
  @staticmethod
379
- def wait_till_well_known_configuration_available(
380
- *, auth_config: AuthConfig, timeout_seconds: int = 30
381
- ) -> None:
366
+ def wait_till_well_known_configuration_available(*, auth_config: AuthConfig, timeout_seconds: int = 30) -> None:
382
367
  """
383
368
  Wait until the well-known configuration is available for the given AuthConfig.
384
369
 
@@ -402,9 +387,7 @@ class AuthManager:
402
387
  resp = client.get(auth_config.well_known_uri)
403
388
  resp.raise_for_status()
404
389
  # Successfully fetched the configuration
405
- logger.info(
406
- f"Well-known configuration is now available at {auth_config.well_known_uri}"
407
- )
390
+ logger.info(f"Well-known configuration is now available at {auth_config.well_known_uri}")
408
391
  return
409
392
  except Exception as e:
410
393
  elapsed_time = time.time() - start_time
@@ -412,7 +395,5 @@ class AuthManager:
412
395
  raise TimeoutError(
413
396
  f"Timed out waiting for well-known configuration at {auth_config.well_known_uri}"
414
397
  ) from e
415
- logger.debug(
416
- f"Well-known configuration not yet available, retrying... ({elapsed_time:.1f}s elapsed)"
417
- )
398
+ logger.debug(f"Well-known configuration not yet available, retrying... ({elapsed_time:.1f}s elapsed)")
418
399
  time.sleep(2) # Wait before retrying
@@ -43,30 +43,20 @@ class OAuthMongoCache(OAuthCache):
43
43
 
44
44
  """
45
45
  self.id_ = uuid.uuid4()
46
- self.repository: AsyncBaseRepository[CacheItem] = (
47
- RepositoryFactory.get_repository(
48
- repository_type=environment_variables.oauth_cache,
49
- environment_variables=environment_variables,
50
- )
51
- )
52
- collection_name: str | None = (
53
- environment_variables.mongo_db_auth_cache_collection_name
46
+ self.repository: AsyncBaseRepository[CacheItem] = RepositoryFactory.get_repository(
47
+ repository_type=environment_variables.oauth_cache,
48
+ environment_variables=environment_variables,
54
49
  )
50
+ collection_name: str | None = environment_variables.mongo_db_auth_cache_collection_name
55
51
  if collection_name is None:
56
- raise ValueError(
57
- "MONGO_DB_AUTH_CACHE_COLLECTION_NAME environment variable must be set"
58
- )
52
+ raise ValueError("MONGO_DB_AUTH_CACHE_COLLECTION_NAME environment variable must be set")
59
53
  self.collection_name: str = collection_name
60
54
 
61
55
  self.environment_variables: AbstractEnvironmentVariables = environment_variables
62
56
  if self.environment_variables is None:
63
- raise ValueError(
64
- "OAuthMongoCache requires an EnvironmentVariables instance."
65
- )
57
+ raise ValueError("OAuthMongoCache requires an EnvironmentVariables instance.")
66
58
  if not isinstance(self.environment_variables, AbstractEnvironmentVariables):
67
- raise TypeError(
68
- "environment_variables must be an instance of EnvironmentVariables"
69
- )
59
+ raise TypeError("environment_variables must be an instance of EnvironmentVariables")
70
60
 
71
61
  @property
72
62
  @override
@@ -92,9 +82,7 @@ class OAuthMongoCache(OAuthCache):
92
82
  "key": key,
93
83
  },
94
84
  )
95
- disable_delete: bool | None = (
96
- self.environment_variables.mongo_db_cache_disable_delete
97
- )
85
+ disable_delete: bool | None = self.environment_variables.mongo_db_cache_disable_delete
98
86
  if cache_item is not None and cache_item.id is not None:
99
87
  # delete the cache item if it exists
100
88
  logger.debug(f" ====== Deleting {cache_item.id} =====")
@@ -131,9 +119,7 @@ class OAuthMongoCache(OAuthCache):
131
119
  "key": key,
132
120
  },
133
121
  )
134
- logger.debug(
135
- f" ====== For key {key} found {cache_item} default {default} ====="
136
- )
122
+ logger.debug(f" ====== For key {key} found {cache_item} default {default} =====")
137
123
  return cache_item.value if cache_item is not None else default
138
124
 
139
125
  @override
@@ -166,21 +152,13 @@ class OAuthMongoCache(OAuthCache):
166
152
  model_class=CacheItem,
167
153
  )
168
154
  if updated_cache_item is None:
169
- raise ValueError(
170
- f"Failed to update cache item with ID: {existing_cache_item_id} for key: {key}"
171
- )
172
- logger.debug(
173
- f"Cache item updated with ID: {updated_cache_item.id} for key: {key} with value: {value}.\n"
174
- )
155
+ raise ValueError(f"Failed to update cache item with ID: {existing_cache_item_id} for key: {key}")
156
+ logger.debug(f"Cache item updated with ID: {updated_cache_item.id} for key: {key} with value: {value}.\n")
175
157
  else:
176
158
  logger.debug(f" ====== Creating new cache item {key}: {value} =====")
177
- cache_item = CacheItem(
178
- key=key, value=value, created=datetime.now(timezone.utc)
179
- )
159
+ cache_item = CacheItem(key=key, value=value, created=datetime.now(timezone.utc))
180
160
  new_object_id = await self.repository.insert(
181
161
  collection_name=self.collection_name,
182
162
  model=cache_item,
183
163
  )
184
- logger.debug(
185
- f"New cache item created with ID: {new_object_id}: {cache_item}"
186
- )
164
+ logger.debug(f"New cache item created with ID: {new_object_id}: {cache_item}")
@@ -77,7 +77,6 @@ class AuthConfig(BaseModel):
77
77
  def _require_client_id_or_registration_url(self) -> Self:
78
78
  if not self.client_id and not self.registration_url:
79
79
  raise ValueError(
80
- f"AuthConfig for '{self.auth_provider}' must have either "
81
- f"client_id or registration_url (for DCR)"
80
+ f"AuthConfig for '{self.auth_provider}' must have either client_id or registration_url (for DCR)"
82
81
  )
83
82
  return self
@@ -28,19 +28,13 @@ class AuthConfigReader:
28
28
  logger.debug("Initializing AuthConfigReader")
29
29
  self.environment_variables: AbstractEnvironmentVariables = environment_variables
30
30
  if self.environment_variables is None:
31
- logger.error(
32
- "AuthConfigReader initialization failed: environment_variables is None"
33
- )
34
- raise ValueError(
35
- "AuthConfigReader requires an EnvironmentVariables instance."
36
- )
31
+ logger.error("AuthConfigReader initialization failed: environment_variables is None")
32
+ raise ValueError("AuthConfigReader requires an EnvironmentVariables instance.")
37
33
  if not isinstance(self.environment_variables, AbstractEnvironmentVariables):
38
34
  logger.error(
39
35
  f"AuthConfigReader initialization failed: environment_variables is not an instance of AbstractEnvironmentVariables, got {type(environment_variables)}"
40
36
  )
41
- raise TypeError(
42
- "environment_variables must be an instance of EnvironmentVariables"
43
- )
37
+ raise TypeError("environment_variables must be an instance of EnvironmentVariables")
44
38
  self._auth_configs: list[AuthConfig] | None = None
45
39
  # lock to protect first-time initialization of _auth_configs across threads
46
40
  self._lock: threading.Lock = threading.Lock()
@@ -83,9 +77,7 @@ class AuthConfigReader:
83
77
  )
84
78
  if auth_config is not None:
85
79
  auth_configs.append(auth_config)
86
- logger.debug(
87
- f"Successfully loaded config for provider: {auth_provider}"
88
- )
80
+ logger.debug(f"Successfully loaded config for provider: {auth_provider}")
89
81
  else:
90
82
  logger.warning(f"No config found for provider: {auth_provider}")
91
83
  # Assign atomically while still under lock
@@ -124,23 +116,17 @@ class AuthConfigReader:
124
116
  return
125
117
  with self._lock:
126
118
  if self._auth_configs is None:
127
- auth_providers: list[str] | None = (
128
- self.environment_variables.auth_providers
129
- )
119
+ auth_providers: list[str] | None = self.environment_variables.auth_providers
130
120
  if auth_providers is None:
131
121
  auth_providers = []
132
122
  env_configs: list[AuthConfig] = []
133
123
  for auth_provider in auth_providers:
134
- auth_config = self.read_config_for_auth_provider(
135
- auth_provider=auth_provider
136
- )
124
+ auth_config = self.read_config_for_auth_provider(auth_provider=auth_provider)
137
125
  if auth_config is not None:
138
126
  env_configs.append(auth_config)
139
127
  self._auth_configs = env_configs
140
128
 
141
- existing_names: set[str] = {
142
- c.auth_provider.lower() for c in self._auth_configs
143
- }
129
+ existing_names: set[str] = {c.auth_provider.lower() for c in self._auth_configs}
144
130
  for config in configs:
145
131
  if config.auth_provider.lower() not in existing_names:
146
132
  self._auth_configs.append(config)
@@ -175,37 +161,23 @@ class AuthConfigReader:
175
161
  logger.debug(f"Standardized auth provider name to: {auth_provider_upper}")
176
162
  # read client_id and client_secret from the environment variables
177
163
  auth_client_id: str | None = os.getenv(f"AUTH_CLIENT_ID_{auth_provider_upper}")
178
- auth_client_secret: str | None = os.getenv(
179
- f"AUTH_CLIENT_SECRET_{auth_provider_upper}"
180
- )
164
+ auth_client_secret: str | None = os.getenv(f"AUTH_CLIENT_SECRET_{auth_provider_upper}")
181
165
  if auth_client_secret:
182
166
  logger.debug(f"Found client secret for provider: {auth_provider_upper}")
183
167
  else:
184
168
  logger.debug(f"No client secret found for provider: {auth_provider_upper}")
185
- auth_well_known_uri: str | None = os.getenv(
186
- f"AUTH_WELL_KNOWN_URI_{auth_provider_upper}"
187
- )
169
+ auth_well_known_uri: str | None = os.getenv(f"AUTH_WELL_KNOWN_URI_{auth_provider_upper}")
188
170
  issuer: str | None = os.getenv(f"AUTH_ISSUER_{auth_provider_upper}")
189
- logger.debug(
190
- f"Issuer for {auth_provider_upper}: {issuer if issuer else 'not set'}"
191
- )
171
+ logger.debug(f"Issuer for {auth_provider_upper}: {issuer if issuer else 'not set'}")
192
172
  audience: str | None = os.getenv(f"AUTH_AUDIENCE_{auth_provider_upper}")
193
173
  if audience is None:
194
- logger.error(
195
- f"AUTH_AUDIENCE_{auth_provider_upper} environment variable is not set"
196
- )
197
- raise ValueError(
198
- f"AUTH_AUDIENCE_{auth_provider_upper} environment variable must be set"
199
- )
200
- friendly_name: str | None = os.getenv(
201
- f"AUTH_FRIENDLY_NAME_{auth_provider_upper}"
202
- )
174
+ logger.error(f"AUTH_AUDIENCE_{auth_provider_upper} environment variable is not set")
175
+ raise ValueError(f"AUTH_AUDIENCE_{auth_provider_upper} environment variable must be set")
176
+ friendly_name: str | None = os.getenv(f"AUTH_FRIENDLY_NAME_{auth_provider_upper}")
203
177
  if not friendly_name:
204
178
  # if no friendly name is set, use the auth_provider as the friendly name
205
179
  friendly_name = auth_provider
206
- logger.debug(
207
- f"No friendly name set, using auth provider name: {friendly_name}"
208
- )
180
+ logger.debug(f"No friendly name set, using auth provider name: {friendly_name}")
209
181
  else:
210
182
  logger.debug(f"Friendly name for {auth_provider}: {friendly_name}")
211
183
 
@@ -214,33 +186,21 @@ class AuthConfigReader:
214
186
  scope = "openid profile email"
215
187
 
216
188
  logger.info(f"Successfully read config for auth provider: {auth_provider}")
217
- extra_info_text: str | None = os.getenv(
218
- f"AUTH_EXTRA_INFO_{auth_provider_upper}"
219
- )
189
+ extra_info_text: str | None = os.getenv(f"AUTH_EXTRA_INFO_{auth_provider_upper}")
220
190
  extra_info_dict: dict[str, Any] | None
221
191
  if extra_info_text:
222
192
  try:
223
193
  extra_info_raw: object = json.loads(extra_info_text)
224
194
  except json.JSONDecodeError as exc:
225
- raise ValueError(
226
- f"Invalid JSON in AUTH_EXTRA_INFO_{auth_provider_upper}: {exc.msg}"
227
- ) from exc
195
+ raise ValueError(f"Invalid JSON in AUTH_EXTRA_INFO_{auth_provider_upper}: {exc.msg}") from exc
228
196
  if not isinstance(extra_info_raw, dict):
229
- raise ValueError(
230
- f"AUTH_EXTRA_INFO_{auth_provider_upper} must be a JSON object"
231
- )
197
+ raise ValueError(f"AUTH_EXTRA_INFO_{auth_provider_upper} must be a JSON object")
232
198
  extra_info_dict = {str(key): value for key, value in extra_info_raw.items()}
233
199
  else:
234
200
  extra_info_dict = None
235
- authorization_endpoint: str | None = os.getenv(
236
- f"AUTH_AUTHORIZATION_ENDPOINT_{auth_provider_upper}"
237
- )
238
- token_endpoint: str | None = os.getenv(
239
- f"AUTH_TOKEN_ENDPOINT_{auth_provider_upper}"
240
- )
241
- registration_url: str | None = os.getenv(
242
- f"AUTH_REGISTRATION_URL_{auth_provider_upper}"
243
- )
201
+ authorization_endpoint: str | None = os.getenv(f"AUTH_AUTHORIZATION_ENDPOINT_{auth_provider_upper}")
202
+ token_endpoint: str | None = os.getenv(f"AUTH_TOKEN_ENDPOINT_{auth_provider_upper}")
203
+ registration_url: str | None = os.getenv(f"AUTH_REGISTRATION_URL_{auth_provider_upper}")
244
204
 
245
205
  return AuthConfig(
246
206
  auth_provider=auth_provider,
@@ -268,15 +228,11 @@ class AuthConfigReader:
268
228
  str: The audience for the specified auth provider.
269
229
  """
270
230
  logger.debug(f"Getting audience for provider: {auth_provider}")
271
- auth_config: AuthConfig | None = self.get_config_for_auth_provider(
272
- auth_provider=auth_provider
273
- )
231
+ auth_config: AuthConfig | None = self.get_config_for_auth_provider(auth_provider=auth_provider)
274
232
  if auth_config is None:
275
233
  logger.error(f"AuthConfig for provider {auth_provider} not found")
276
234
  raise ValueError(f"AuthConfig for audience {auth_provider} not found.")
277
- logger.debug(
278
- f"Found audience for provider {auth_provider}: {auth_config.audience}"
279
- )
235
+ logger.debug(f"Found audience for provider {auth_provider}: {auth_config.audience}")
280
236
  return auth_config.audience
281
237
 
282
238
  def get_provider_for_audience(self, *, audience: str) -> str | None:
@@ -293,9 +249,7 @@ class AuthConfigReader:
293
249
  auth_configs: list[AuthConfig] = self.get_auth_configs_for_all_auth_providers()
294
250
  for auth_config in auth_configs:
295
251
  if auth_config.audience == audience:
296
- logger.debug(
297
- f"Found provider for audience {audience}: {auth_config.auth_provider}"
298
- )
252
+ logger.debug(f"Found provider for audience {audience}: {auth_config.auth_provider}")
299
253
  return auth_config.auth_provider
300
254
  logger.warning(f"No provider found for audience: {audience}")
301
255
  return None
@@ -314,9 +268,7 @@ class AuthConfigReader:
314
268
  auth_configs: list[AuthConfig] = self.get_auth_configs_for_all_auth_providers()
315
269
  for auth_config in auth_configs:
316
270
  if auth_config.client_id == client_id:
317
- logger.debug(
318
- f"Found provider for client_id {client_id}: {auth_config.auth_provider}"
319
- )
271
+ logger.debug(f"Found provider for client_id {client_id}: {auth_config.auth_provider}")
320
272
  return auth_config.auth_provider
321
273
  logger.warning(f"No provider found for client_id: {client_id}")
322
274
  return None
@@ -73,8 +73,7 @@ class DcrClient:
73
73
  e.response.text[:500],
74
74
  )
75
75
  raise ValueError(
76
- f"DCR registration failed at '{registration_url}' "
77
- f"with status {e.response.status_code}"
76
+ f"DCR registration failed at '{registration_url}' with status {e.response.status_code}"
78
77
  ) from e
79
78
  dcr_response: dict[str, Any] = response.json()
80
79
 
@@ -86,13 +85,11 @@ class DcrClient:
86
85
  response_keys,
87
86
  )
88
87
  raise ValueError(
89
- f"DCR response from '{registration_url}' missing 'client_id'. "
90
- f"Response keys: {response_keys}"
88
+ f"DCR response from '{registration_url}' missing 'client_id'. Response keys: {response_keys}"
91
89
  )
92
90
 
93
91
  logger.info(
94
- "DCR: Registration successful at '%s' — client_id=%s, "
95
- "has_secret=%s, expires_at=%s",
92
+ "DCR: Registration successful at '%s' — client_id=%s, has_secret=%s, expires_at=%s",
96
93
  registration_url,
97
94
  dcr_response["client_id"],
98
95
  "client_secret" in dcr_response,
@@ -32,11 +32,9 @@ class DcrManager:
32
32
  ) -> None:
33
33
  self._collection_name = collection_name
34
34
  self._redirect_uri = redirect_uri
35
- self._repository: AsyncBaseRepository[DcrRegistration] = (
36
- RepositoryFactory.get_repository(
37
- repository_type=environment_variables.oauth_cache,
38
- environment_variables=environment_variables,
39
- )
35
+ self._repository: AsyncBaseRepository[DcrRegistration] = RepositoryFactory.get_repository(
36
+ repository_type=environment_variables.oauth_cache,
37
+ environment_variables=environment_variables,
40
38
  )
41
39
  self._dcr_client = dcr_client or DcrClient()
42
40
 
@@ -53,8 +51,7 @@ class DcrManager:
53
51
  ) -> DcrRegistration | None:
54
52
  if client_id:
55
53
  logger.info(
56
- "DCR: Skipping registration for '%s' — client_id already "
57
- "provided (client_id=%s)",
54
+ "DCR: Skipping registration for '%s' — client_id already provided (client_id=%s)",
58
55
  auth_provider,
59
56
  client_id,
60
57
  )
@@ -62,13 +59,11 @@ class DcrManager:
62
59
 
63
60
  if not registration_url:
64
61
  logger.error(
65
- "DCR: No registration_url and no client_id for '%s' — "
66
- "cannot resolve credentials",
62
+ "DCR: No registration_url and no client_id for '%s' — cannot resolve credentials",
67
63
  auth_provider,
68
64
  )
69
65
  raise ValueError(
70
- f"registration_url is required for DCR when client_id is not "
71
- f"provided (auth_provider='{auth_provider}')"
66
+ f"registration_url is required for DCR when client_id is not provided (auth_provider='{auth_provider}')"
72
67
  )
73
68
 
74
69
  validate_url(registration_url)
@@ -86,8 +81,7 @@ class DcrManager:
86
81
 
87
82
  if cached and not self._is_expired(cached):
88
83
  logger.info(
89
- "DCR: Using cached credentials for '%s' — client_id=%s, "
90
- "expires_at=%s",
84
+ "DCR: Using cached credentials for '%s' — client_id=%s, expires_at=%s",
91
85
  auth_provider,
92
86
  cached.client_id,
93
87
  cached.client_secret_expires_at or "never",
@@ -96,16 +90,14 @@ class DcrManager:
96
90
 
97
91
  if cached and self._is_expired(cached):
98
92
  logger.info(
99
- "DCR: Cached credentials for '%s' are expired "
100
- "(client_id=%s, expired_at=%s) — re-registering",
93
+ "DCR: Cached credentials for '%s' are expired (client_id=%s, expired_at=%s) — re-registering",
101
94
  auth_provider,
102
95
  cached.client_id,
103
96
  cached.client_secret_expires_at,
104
97
  )
105
98
  else:
106
99
  logger.info(
107
- "DCR: No cached credentials found for '%s' — performing "
108
- "new registration at '%s'",
100
+ "DCR: No cached credentials found for '%s' — performing new registration at '%s'",
109
101
  auth_provider,
110
102
  registration_url,
111
103
  )
@@ -138,8 +130,7 @@ class DcrManager:
138
130
  registration_url: str,
139
131
  ) -> DcrRegistration | None:
140
132
  logger.debug(
141
- "DCR: Querying cache for auth_provider='%s', "
142
- "registration_url='%s', collection='%s'",
133
+ "DCR: Querying cache for auth_provider='%s', registration_url='%s', collection='%s'",
143
134
  auth_provider,
144
135
  registration_url,
145
136
  self._collection_name,
@@ -154,8 +145,7 @@ class DcrManager:
154
145
  )
155
146
  if results:
156
147
  logger.debug(
157
- "DCR: Cache hit for '%s' — found %d result(s), "
158
- "client_id=%s",
148
+ "DCR: Cache hit for '%s' — found %d result(s), client_id=%s",
159
149
  auth_provider,
160
150
  len(results),
161
151
  results[0].client_id,