oidcauthlib 3.0.6__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.6/oidcauthlib.egg-info → oidcauthlib-3.0.7}/PKG-INFO +4 -14
  3. oidcauthlib-3.0.7/VERSION +1 -0
  4. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/auth_helper.py +2 -6
  5. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/auth_manager.py +30 -53
  6. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/oauth_mongo_cache.py +13 -35
  7. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/config/auth_config.py +1 -2
  8. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/config/auth_config_reader.py +24 -72
  9. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/dcr_client.py +3 -6
  10. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/dcr_manager.py +9 -17
  11. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/dcr_registration.py +3 -9
  12. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/fastapi_auth_manager.py +83 -34
  13. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/middleware/request_scope_middleware.py +4 -11
  14. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/middleware/token_reader_middleware.py +7 -21
  15. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/auth.py +2 -6
  16. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/cache_item.py +1 -3
  17. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/client_key_set.py +2 -6
  18. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/token.py +6 -26
  19. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/base_repository.py +2 -6
  20. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/memory/memory_repository.py +2 -8
  21. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/mongo/mongo_repository.py +14 -42
  22. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/repository_factory.py +2 -6
  23. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/routers/auth_router.py +9 -29
  24. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/token_reader.py +8 -26
  25. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/auth_server_metadata_discovery.py +1 -2
  26. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache.py +32 -92
  27. oidcauthlib-3.0.7/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache_result.py +9 -0
  28. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/well_known_configuration_manager.py +2 -6
  29. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/container/oidc_authlib_container_factory.py +5 -15
  30. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/mongo_gridfs_db.py +26 -77
  31. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/mongo_storage_factory.py +5 -13
  32. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/storage_factory_creator.py +2 -5
  33. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/environment/oidc_environment_variables.py +8 -24
  34. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/logging_response.py +2 -6
  35. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/mongo_url_utils.py +1 -3
  36. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7/oidcauthlib.egg-info}/PKG-INFO +4 -14
  37. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/SOURCES.txt +2 -50
  38. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/top_level.txt +0 -1
  39. oidcauthlib-3.0.7/pyproject.toml +107 -0
  40. oidcauthlib-3.0.7/setup.cfg +4 -0
  41. oidcauthlib-3.0.6/MANIFEST.in +0 -6
  42. oidcauthlib-3.0.6/Makefile +0 -85
  43. oidcauthlib-3.0.6/VERSION +0 -1
  44. oidcauthlib-3.0.6/oidcauthlib/auth/well_known_configuration/well_known_configuration_cache_result.py +0 -15
  45. oidcauthlib-3.0.6/oidcauthlib.egg-info/not-zip-safe +0 -1
  46. oidcauthlib-3.0.6/setup.cfg +0 -29
  47. oidcauthlib-3.0.6/setup.py +0 -82
  48. oidcauthlib-3.0.6/tests/__init__.py +0 -0
  49. oidcauthlib-3.0.6/tests/auth/__init__.py +0 -0
  50. oidcauthlib-3.0.6/tests/auth/cache/__init__.py +0 -0
  51. oidcauthlib-3.0.6/tests/auth/cache/test_oauth_memory_cache.py +0 -26
  52. oidcauthlib-3.0.6/tests/auth/config/__init__.py +0 -0
  53. oidcauthlib-3.0.6/tests/auth/config/test_auth_config.py +0 -72
  54. oidcauthlib-3.0.6/tests/auth/config/test_auth_config_reader.py +0 -91
  55. oidcauthlib-3.0.6/tests/auth/config/test_auth_config_reader_register.py +0 -200
  56. oidcauthlib-3.0.6/tests/auth/config/test_auth_config_reader_thread_safety.py +0 -127
  57. oidcauthlib-3.0.6/tests/auth/conftest.py +0 -14
  58. oidcauthlib-3.0.6/tests/auth/dcr/__init__.py +0 -0
  59. oidcauthlib-3.0.6/tests/auth/dcr/test_dcr_client.py +0 -129
  60. oidcauthlib-3.0.6/tests/auth/dcr/test_dcr_manager.py +0 -198
  61. oidcauthlib-3.0.6/tests/auth/middleware/__init__.py +0 -0
  62. oidcauthlib-3.0.6/tests/auth/middleware/test_request_scope_middleware.py +0 -188
  63. oidcauthlib-3.0.6/tests/auth/middleware/test_token_reader_middleware.py +0 -671
  64. oidcauthlib-3.0.6/tests/auth/models/__init__.py +0 -0
  65. oidcauthlib-3.0.6/tests/auth/models/test_auth.py +0 -43
  66. oidcauthlib-3.0.6/tests/auth/models/test_base_db_model.py +0 -70
  67. oidcauthlib-3.0.6/tests/auth/models/test_cache_item.py +0 -17
  68. oidcauthlib-3.0.6/tests/auth/models/test_token.py +0 -48
  69. oidcauthlib-3.0.6/tests/auth/repository/__init__.py +0 -0
  70. oidcauthlib-3.0.6/tests/auth/repository/memory/__init__.py +0 -0
  71. oidcauthlib-3.0.6/tests/auth/repository/memory/test_memory_repository.py +0 -156
  72. oidcauthlib-3.0.6/tests/auth/repository/mongo/__init__.py +0 -0
  73. oidcauthlib-3.0.6/tests/auth/repository/mongo/test_mongo_repository.py +0 -912
  74. oidcauthlib-3.0.6/tests/auth/repository/mongo/test_mongo_repository_real.py +0 -178
  75. oidcauthlib-3.0.6/tests/auth/test_auth_helper.py +0 -26
  76. oidcauthlib-3.0.6/tests/auth/test_auth_helper2.py +0 -33
  77. oidcauthlib-3.0.6/tests/auth/test_auth_manager.py +0 -523
  78. oidcauthlib-3.0.6/tests/auth/test_register_dynamic_provider.py +0 -357
  79. oidcauthlib-3.0.6/tests/auth/well_known_configuration/__init__.py +0 -0
  80. oidcauthlib-3.0.6/tests/auth/well_known_configuration/test_auth_server_metadata_discovery.py +0 -184
  81. oidcauthlib-3.0.6/tests/auth/well_known_configuration/test_well_known_configuration_cache.py +0 -405
  82. oidcauthlib-3.0.6/tests/auth/well_known_configuration/test_well_known_configuration_manager.py +0 -174
  83. oidcauthlib-3.0.6/tests/conftest.py +0 -130
  84. oidcauthlib-3.0.6/tests/storage/__init__.py +0 -0
  85. oidcauthlib-3.0.6/tests/storage/test_mongo_gridfs_db.py +0 -378
  86. oidcauthlib-3.0.6/tests/storage/test_mongo_gridfs_race_setup.py +0 -70
  87. oidcauthlib-3.0.6/tests/storage/test_mongo_store_factory.py +0 -516
  88. oidcauthlib-3.0.6/tests/storage/test_storage_factory_security.py +0 -498
  89. oidcauthlib-3.0.6/tests/utilities/__init__.py +0 -0
  90. oidcauthlib-3.0.6/tests/utilities/test_cached.py +0 -52
  91. oidcauthlib-3.0.6/tests/utilities/test_mongo_url_utils.py +0 -30
  92. oidcauthlib-3.0.6/tests/utilities/test_url_validator.py +0 -182
  93. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/LICENSE +0 -0
  94. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/README.md +0 -0
  95. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/__init__.py +0 -0
  96. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/__init__.py +0 -0
  97. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/__init__.py +0 -0
  98. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/oauth_cache.py +0 -0
  99. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/cache/oauth_memory_cache.py +0 -0
  100. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/config/__init__.py +0 -0
  101. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/dcr/__init__.py +0 -0
  102. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/__init__.py +0 -0
  103. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_bearer_token_expired_exception.py +0 -0
  104. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_bearer_token_invalid_exception.py +0 -0
  105. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_bearer_token_missing_exception.py +0 -0
  106. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/exceptions/authorization_needed_exception.py +0 -0
  107. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/middleware/__init__.py +0 -0
  108. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/__init__.py +0 -0
  109. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/models/base_db_model.py +0 -0
  110. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/__init__.py +0 -0
  111. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/memory/__init__.py +0 -0
  112. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/repository/mongo/__init__.py +0 -0
  113. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/routers/__init__.py +0 -0
  114. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/__init__.py +0 -0
  115. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/auth/well_known_configuration/auth_server_metadata.py +0 -0
  116. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/container/__init__.py +0 -0
  117. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/open_telemetry/__init__.py +0 -0
  118. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/open_telemetry/attribute_names.py +0 -0
  119. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/open_telemetry/span_names.py +0 -0
  120. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/py.typed +0 -0
  121. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/__init__.py +0 -0
  122. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/cache_to_collection_mapper.py +0 -0
  123. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/memory_storage_factory.py +0 -0
  124. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/mongo_gridfs_exception.py +0 -0
  125. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/storage/storage_factory.py +0 -0
  126. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/__init__.py +0 -0
  127. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/cached.py +0 -0
  128. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/environment/__init__.py +0 -0
  129. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/environment/abstract_environment_variables.py +0 -0
  130. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/__init__.py +0 -0
  131. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/log_levels.py +0 -0
  132. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/logger/logging_transport.py +0 -0
  133. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib/utilities/url_validator.py +0 -0
  134. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/dependency_links.txt +0 -0
  135. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/oidcauthlib.egg-info/requires.txt +0 -0
  136. {oidcauthlib-3.0.6 → oidcauthlib-3.0.7}/tests/test_mongo_url_utils.py +0 -0
  137. {oidcauthlib-3.0.6 → 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.6
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
@@ -70,17 +71,13 @@ class AuthManager:
70
71
  if self.environment_variables is None:
71
72
  raise ValueError("environment_variables must not be None")
72
73
  if not isinstance(self.environment_variables, AbstractEnvironmentVariables):
73
- raise TypeError(
74
- "environment_variables must be an instance of EnvironmentVariables"
75
- )
74
+ raise TypeError("environment_variables must be an instance of EnvironmentVariables")
76
75
 
77
76
  self.auth_config_reader: AuthConfigReader = auth_config_reader
78
77
  if self.auth_config_reader is None:
79
78
  raise ValueError("auth_config_reader must not be None")
80
79
  if not isinstance(self.auth_config_reader, AuthConfigReader):
81
- raise TypeError(
82
- "auth_config_reader must be an instance of AuthConfigReader"
83
- )
80
+ raise TypeError("auth_config_reader must be an instance of AuthConfigReader")
84
81
 
85
82
  self.token_reader: TokenReader = token_reader
86
83
  if self.token_reader is None:
@@ -88,17 +85,11 @@ class AuthManager:
88
85
  if not isinstance(self.token_reader, TokenReader):
89
86
  raise TypeError("token_reader must be an instance of TokenReader")
90
87
 
91
- self.well_known_configuration_manager: WellKnownConfigurationManager = (
92
- well_known_configuration_manager
93
- )
88
+ self.well_known_configuration_manager: WellKnownConfigurationManager = well_known_configuration_manager
94
89
  if self.well_known_configuration_manager is None:
95
90
  raise ValueError("well_known_configuration_manager must not be None")
96
- if not isinstance(
97
- self.well_known_configuration_manager, WellKnownConfigurationManager
98
- ):
99
- raise TypeError(
100
- "well_known_configuration_manager must be an instance of WellKnownConfigurationManager"
101
- )
91
+ if not isinstance(self.well_known_configuration_manager, WellKnownConfigurationManager):
92
+ raise TypeError("well_known_configuration_manager must be an instance of WellKnownConfigurationManager")
102
93
 
103
94
  self._dcr_manager: DcrManager | None = dcr_manager
104
95
 
@@ -109,9 +100,7 @@ class AuthManager:
109
100
  else OAuthMongoCache(environment_variables=environment_variables)
110
101
  )
111
102
 
112
- logger.debug(
113
- f"Initializing AuthManager with cache type {type(self.cache)} cache id: {self.cache.id}"
114
- )
103
+ logger.debug(f"Initializing AuthManager with cache type {type(self.cache)} cache id: {self.cache.id}")
115
104
  # OIDC PKCE setup
116
105
  self.redirect_uri = os.getenv("AUTH_REDIRECT_URI")
117
106
  if self.redirect_uri is None:
@@ -119,17 +108,13 @@ class AuthManager:
119
108
  # https://docs.authlib.org/en/latest/client/frameworks.html#frameworks-clients
120
109
  self._oauth: OAuth = OAuth(cache=self.cache)
121
110
  self._registered_dynamic_providers: set[str] = set()
122
- self.auth_configs: List[AuthConfig] = (
123
- self.auth_config_reader.get_auth_configs_for_all_auth_providers()
124
- )
111
+ self.auth_configs: List[AuthConfig] = self.auth_config_reader.get_auth_configs_for_all_auth_providers()
125
112
 
126
113
  async def ensure_initialized_async(self) -> None:
127
114
  auth_config: AuthConfig
128
115
  for auth_config in self.auth_configs:
129
116
  if auth_config.well_known_uri:
130
- await self.well_known_configuration_manager.get_async(
131
- auth_config=auth_config
132
- )
117
+ await self.well_known_configuration_manager.get_async(auth_config=auth_config)
133
118
  await self.register_dynamic_provider(auth_config=auth_config)
134
119
 
135
120
  async def register_dynamic_provider(
@@ -162,9 +147,7 @@ class AuthManager:
162
147
  registration_url=auth_config.registration_url,
163
148
  )
164
149
  if dcr_result is None or not dcr_result.client_id:
165
- raise ValueError(
166
- f"DCR failed to obtain client_id for '{auth_config.auth_provider}'"
167
- )
150
+ raise ValueError(f"DCR failed to obtain client_id for '{auth_config.auth_provider}'")
168
151
  client_id = dcr_result.client_id
169
152
  client_secret = dcr_result.client_secret
170
153
  logger.info(
@@ -278,12 +261,20 @@ class AuthManager:
278
261
  f" with state {state_content} and encoded state {state}"
279
262
  )
280
263
 
281
- rv: Dict[str, Any] = await client.create_authorization_url(
282
- redirect_uri=redirect_uri, state=state
283
- )
264
+ rv: Dict[str, Any] = await client.create_authorization_url(redirect_uri=redirect_uri, state=state)
284
265
  logger.debug(f"Authorization URL created: {rv}")
285
- # request is only needed if we are using the session to store the state
286
- 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}")
287
278
  return cast(str, rv["url"])
288
279
 
289
280
  async def create_oauth_client(self, *, name: str) -> StarletteOAuth2App:
@@ -327,17 +318,11 @@ class AuthManager:
327
318
  resp.raise_for_status()
328
319
  token_url = resp.json().get("token_endpoint")
329
320
  except Exception as e:
330
- raise AuthorizationNeededException(
331
- message=f"Failed to discover token endpoint: {e}"
332
- )
321
+ raise AuthorizationNeededException(message=f"Failed to discover token endpoint: {e}")
333
322
  if not token_url and auth_config.issuer:
334
- token_url = (
335
- auth_config.issuer.rstrip("/") + "/protocol/openid-connect/token"
336
- )
323
+ token_url = auth_config.issuer.rstrip("/") + "/protocol/openid-connect/token"
337
324
  if not token_url:
338
- raise AuthorizationNeededException(
339
- message="No token endpoint found in AuthConfig."
340
- )
325
+ raise AuthorizationNeededException(message="No token endpoint found in AuthConfig.")
341
326
 
342
327
  # Prepare OAuth2 client
343
328
  client_id = auth_config.client_id
@@ -369,9 +354,7 @@ class AuthManager:
369
354
 
370
355
  return access_token
371
356
 
372
- def get_auth_config_for_auth_provider(
373
- self, *, auth_provider: str
374
- ) -> AuthConfig | None:
357
+ def get_auth_config_for_auth_provider(self, *, auth_provider: str) -> AuthConfig | None:
375
358
  if not auth_provider:
376
359
  raise ValueError("auth_provider must not be empty")
377
360
  for auth_config in self.auth_configs:
@@ -380,9 +363,7 @@ class AuthManager:
380
363
  return None
381
364
 
382
365
  @staticmethod
383
- def wait_till_well_known_configuration_available(
384
- *, auth_config: AuthConfig, timeout_seconds: int = 30
385
- ) -> None:
366
+ def wait_till_well_known_configuration_available(*, auth_config: AuthConfig, timeout_seconds: int = 30) -> None:
386
367
  """
387
368
  Wait until the well-known configuration is available for the given AuthConfig.
388
369
 
@@ -406,9 +387,7 @@ class AuthManager:
406
387
  resp = client.get(auth_config.well_known_uri)
407
388
  resp.raise_for_status()
408
389
  # Successfully fetched the configuration
409
- logger.info(
410
- f"Well-known configuration is now available at {auth_config.well_known_uri}"
411
- )
390
+ logger.info(f"Well-known configuration is now available at {auth_config.well_known_uri}")
412
391
  return
413
392
  except Exception as e:
414
393
  elapsed_time = time.time() - start_time
@@ -416,7 +395,5 @@ class AuthManager:
416
395
  raise TimeoutError(
417
396
  f"Timed out waiting for well-known configuration at {auth_config.well_known_uri}"
418
397
  ) from e
419
- logger.debug(
420
- f"Well-known configuration not yet available, retrying... ({elapsed_time:.1f}s elapsed)"
421
- )
398
+ logger.debug(f"Well-known configuration not yet available, retrying... ({elapsed_time:.1f}s elapsed)")
422
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)
@@ -95,16 +90,14 @@ class DcrManager:
95
90
 
96
91
  if cached and self._is_expired(cached):
97
92
  logger.info(
98
- "DCR: Cached credentials for '%s' are expired "
99
- "(client_id=%s, expired_at=%s) — re-registering",
93
+ "DCR: Cached credentials for '%s' are expired (client_id=%s, expired_at=%s) — re-registering",
100
94
  auth_provider,
101
95
  cached.client_id,
102
96
  cached.client_secret_expires_at,
103
97
  )
104
98
  else:
105
99
  logger.info(
106
- "DCR: No cached credentials found for '%s' — performing "
107
- "new registration at '%s'",
100
+ "DCR: No cached credentials found for '%s' — performing new registration at '%s'",
108
101
  auth_provider,
109
102
  registration_url,
110
103
  )
@@ -137,8 +130,7 @@ class DcrManager:
137
130
  registration_url: str,
138
131
  ) -> DcrRegistration | None:
139
132
  logger.debug(
140
- "DCR: Querying cache for auth_provider='%s', "
141
- "registration_url='%s', collection='%s'",
133
+ "DCR: Querying cache for auth_provider='%s', registration_url='%s', collection='%s'",
142
134
  auth_provider,
143
135
  registration_url,
144
136
  self._collection_name,
@@ -10,19 +10,13 @@ class DcrRegistration(BaseDbModel):
10
10
  """Persisted DCR credentials in MongoDB."""
11
11
 
12
12
  created: datetime = Field(description="When the registration was created.")
13
- updated: Optional[datetime] = Field(
14
- default=None, description="When the registration was last updated."
15
- )
13
+ updated: Optional[datetime] = Field(default=None, description="When the registration was last updated.")
16
14
  auth_provider: str = Field(description="The normalized auth provider key.")
17
15
  registration_url: str = Field(description="The DCR endpoint URL.")
18
16
  client_id: str = Field(description="The client_id from DCR.")
19
- client_secret: Optional[str] = Field(
20
- default=None, description="The client_secret from DCR (if any)."
21
- )
17
+ client_secret: Optional[str] = Field(default=None, description="The client_secret from DCR (if any).")
22
18
  client_secret_expires_at: int = Field(
23
19
  default=0,
24
20
  description="Unix timestamp when client_secret expires. 0 = no expiry.",
25
21
  )
26
- registration_response: dict[str, Any] = Field(
27
- default_factory=dict, description="The full DCR response."
28
- )
22
+ registration_response: dict[str, Any] = Field(default_factory=dict, description="The full DCR response.")