dara-core 1.23.0__py3-none-any.whl → 1.23.0a1__py3-none-any.whl

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.
@@ -18,7 +18,6 @@ limitations under the License.
18
18
  from contextvars import ContextVar
19
19
  from datetime import datetime
20
20
 
21
- from pydantic import ConfigDict
22
21
  from typing_extensions import TypedDict
23
22
 
24
23
  from dara.core.base_definitions import DaraBaseModel as BaseModel
@@ -61,9 +60,6 @@ class UserData(BaseModel):
61
60
  identity_email: str | None = None
62
61
  groups: list[str] | None = []
63
62
 
64
- # allow extra for more flexibility in custom oidc configs
65
- model_config = ConfigDict(extra='allow')
66
-
67
63
  @classmethod
68
64
  def from_token_data(cls, token_data: TokenData):
69
65
  return cls(
@@ -65,7 +65,6 @@ class OIDCAuthConfig(BaseAuthConfig):
65
65
  - SSO_EXTRA_AUDIENCE - if set, extra audiences to verify against the ID token in addition to `sso_client_id`
66
66
  - SSO_SCOPES - space-separated list of scopes to request from the identity provider, defaults to `openid`
67
67
  - SSO_JWT_ALGO - algorithm to use for verifying IDP-provided JWTs, defaults to `ES256`
68
- - SSO_USE_USERINFO - if set to `true`, fetch additional claims from the userinfo endpoint when an access token is available
69
68
  """
70
69
 
71
70
  # NOTE: the config follows OIDC specification, but makes a few concessions
@@ -210,84 +209,29 @@ class OIDCAuthConfig(BaseAuthConfig):
210
209
  state = self.generate_state(redirect_to=body.redirect_to)
211
210
  return RedirectResponse(redirect_uri=self.get_authorization_url(state))
212
211
 
213
- async def fetch_userinfo(self, access_token: str) -> dict | None:
212
+ def extract_user_data_from_id_token(self, claims: IdTokenClaims) -> UserData:
214
213
  """
215
- Fetch user information from the OIDC userinfo endpoint.
216
-
217
- Per OpenID Connect Core 1.0 Section 5.3, the userinfo endpoint returns claims
218
- about the authenticated user. This is useful when the ID token doesn't contain
219
- all required claims.
220
-
221
- :param access_token: The access token to authenticate the request
222
- :return: Dictionary of userinfo claims, or None if the request fails
223
- """
224
- userinfo_endpoint = self.discovery.userinfo_endpoint
225
- if not userinfo_endpoint:
226
- dev_logger.warning('Userinfo endpoint not available in OIDC discovery')
227
- return None
228
-
229
- try:
230
- response = await self.client.get(
231
- userinfo_endpoint,
232
- headers={'Authorization': f'Bearer {access_token}'},
233
- timeout=10,
234
- )
235
- response.raise_for_status()
236
- return response.json()
237
- except httpx.HTTPStatusError as e:
238
- dev_logger.warning(
239
- f'Failed to fetch userinfo: HTTP {e.response.status_code}',
240
- )
241
- return None
242
- except httpx.RequestError as e:
243
- dev_logger.warning(f'Failed to fetch userinfo: {e}')
244
- return None
245
-
246
- def extract_user_data(self, claims: IdTokenClaims, userinfo: dict | None = None) -> UserData:
247
- """
248
- Extract user data from ID token claims and optional userinfo response.
214
+ Extract user data from ID token claims.
249
215
 
250
216
  Override this method in subclasses to handle provider-specific claim structures.
251
217
  The default implementation uses standard OIDC claims, with support for the
252
- non-standard 'identity' claim. When userinfo is provided and SSO_USE_USERINFO
253
- is enabled, userinfo claims take precedence over ID token claims.
218
+ non-standard 'identity' claim.
254
219
 
255
220
  :param claims: Decoded ID token claims
256
- :param userinfo: Optional userinfo response from the userinfo endpoint
257
221
  :return: UserData extracted from the claims
258
222
  """
259
- oidc_settings = get_oidc_settings()
260
-
261
- # When userinfo is provided and use_userinfo is enabled, prefer userinfo claims
262
- if userinfo and oidc_settings.use_userinfo:
263
- # userinfo 'sub' must match id_token 'sub' per OIDC spec
264
- identity_id = userinfo.get('sub') or claims.sub
265
- identity_email = userinfo.get('email') or claims.email
266
- identity_name = (
267
- userinfo.get('name')
268
- or userinfo.get('preferred_username')
269
- or userinfo.get('nickname')
270
- or (
271
- f'{userinfo.get("given_name", "")} {userinfo.get("family_name", "")}'.strip()
272
- if userinfo.get('given_name') or userinfo.get('family_name')
273
- else None
274
- )
275
- )
276
- groups = userinfo.get('groups') or claims.groups
223
+ # Check for non-standard 'identity' claim (Causalens IDP)
224
+ # This is a nested object with id, name, email fields
225
+ identity_claim = getattr(claims, 'identity', None)
226
+ if isinstance(identity_claim, dict):
227
+ identity_id = identity_claim.get('id') or claims.sub
228
+ identity_name = identity_claim.get('name')
229
+ identity_email = identity_claim.get('email') or claims.email
277
230
  else:
278
- # Check for non-standard 'identity' claim (Causalens IDP)
279
- # This is a nested object with id, name, email fields
280
- identity_claim = getattr(claims, 'identity', None)
281
- if isinstance(identity_claim, dict):
282
- identity_id = identity_claim.get('id') or claims.sub
283
- identity_name = identity_claim.get('name')
284
- identity_email = identity_claim.get('email') or claims.email
285
- else:
286
- # Standard OIDC: use 'sub' as the identity ID
287
- identity_id = claims.sub
288
- identity_email = claims.email
289
- identity_name = None
290
- groups = claims.groups
231
+ # Standard OIDC: use 'sub' as the identity ID
232
+ identity_id = claims.sub
233
+ identity_email = claims.email
234
+ identity_name = None
291
235
 
292
236
  # Fall back to standard claims for name if not set
293
237
  if not identity_name:
@@ -308,7 +252,7 @@ class OIDCAuthConfig(BaseAuthConfig):
308
252
  identity_id=identity_id,
309
253
  identity_name=identity_name,
310
254
  identity_email=identity_email,
311
- groups=groups,
255
+ groups=claims.groups,
312
256
  )
313
257
 
314
258
  def verify_token(self, token: str) -> TokenData:
@@ -347,7 +291,7 @@ class OIDCAuthConfig(BaseAuthConfig):
347
291
  claims = decode_id_token(token)
348
292
 
349
293
  # Extract user data (can be overridden for provider-specific claim structures)
350
- user_data = self.extract_user_data(claims)
294
+ user_data = self.extract_user_data_from_id_token(claims)
351
295
 
352
296
  # Verify user has access based on groups
353
297
  self.verify_user_access(user_data)
@@ -446,8 +390,6 @@ class OIDCAuthConfig(BaseAuthConfig):
446
390
  :return: Tuple of (new_session_token, new_refresh_token)
447
391
  :raises HTTPException: If the refresh fails
448
392
  """
449
- oidc_settings = get_oidc_settings()
450
-
451
393
  # Request new tokens from the IDP
452
394
  oidc_tokens = await get_token_from_idp(
453
395
  self,
@@ -464,13 +406,8 @@ class OIDCAuthConfig(BaseAuthConfig):
464
406
  # Decode and verify the new ID token
465
407
  claims = decode_id_token(oidc_tokens.id_token)
466
408
 
467
- # Fetch userinfo if enabled and we have an access token
468
- userinfo = None
469
- if oidc_settings.use_userinfo and oidc_tokens.access_token:
470
- userinfo = await self.fetch_userinfo(oidc_tokens.access_token)
471
-
472
409
  # Extract user data from claims
473
- user_data = self.extract_user_data(claims, userinfo=userinfo)
410
+ user_data = self.extract_user_data_from_id_token(claims)
474
411
 
475
412
  # Verify user still has access
476
413
  self.verify_user_access(user_data)
@@ -100,13 +100,8 @@ async def sso_callback(
100
100
  # Decode and verify the ID token
101
101
  claims = decode_id_token(oidc_tokens.id_token)
102
102
 
103
- # Fetch userinfo if enabled and we have an access token
104
- userinfo = None
105
- if oidc_settings.use_userinfo and oidc_tokens.access_token:
106
- userinfo = await auth_config.fetch_userinfo(oidc_tokens.access_token)
107
-
108
103
  # Extract user data from claims (handles both standard OIDC and Causalens identity claim)
109
- user_data = auth_config.extract_user_data(claims, userinfo=userinfo)
104
+ user_data = auth_config.extract_user_data_from_id_token(claims)
110
105
 
111
106
  # Verify user has access based on groups
112
107
  auth_config.verify_user_access(user_data)
@@ -24,8 +24,6 @@ class OIDCSettings(BaseSettings):
24
24
  verify_audience: bool = False
25
25
  extra_audience: list[str] | None = None
26
26
  allowed_identity_id: str | None = None
27
- use_userinfo: bool = False
28
- """If True, fetch additional claims from the userinfo endpoint when an access token is available."""
29
27
 
30
28
  model_config = SettingsConfigDict(env_file='.env', extra='allow', env_prefix='sso_')
31
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dara-core
3
- Version: 1.23.0
3
+ Version: 1.23.0a1
4
4
  Summary: Dara Framework Core
5
5
  Home-page: https://dara.causalens.com/
6
6
  License: Apache-2.0
@@ -21,10 +21,10 @@ Requires-Dist: cachetools (>=5.0.0)
21
21
  Requires-Dist: certifi (>=2024.7.4)
22
22
  Requires-Dist: click (>=8.1.3,<9.0.0)
23
23
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
24
- Requires-Dist: create-dara-app (==1.23.0)
24
+ Requires-Dist: create-dara-app (==1.23.0-alpha.1)
25
25
  Requires-Dist: croniter (>=6.0.0,<7.0.0)
26
26
  Requires-Dist: cryptography (>=42.0.4)
27
- Requires-Dist: dara-components (==1.23.0) ; extra == "all"
27
+ Requires-Dist: dara-components (==1.23.0-alpha.1) ; extra == "all"
28
28
  Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0)
29
29
  Requires-Dist: fastapi (>=0.115.0,<0.121.0)
30
30
  Requires-Dist: fastapi_vite_dara (==0.4.0)
@@ -55,7 +55,7 @@ Description-Content-Type: text/markdown
55
55
 
56
56
  # Dara Application Framework
57
57
 
58
- <img src="https://github.com/causalens/dara/blob/v1.23.0/img/dara_light.svg?raw=true">
58
+ <img src="https://github.com/causalens/dara/blob/v1.23.0-alpha.1/img/dara_light.svg?raw=true">
59
59
 
60
60
  ![Master tests](https://github.com/causalens/dara/actions/workflows/tests.yml/badge.svg?branch=master)
61
61
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -100,7 +100,7 @@ source .venv/bin/activate
100
100
  dara start
101
101
  ```
102
102
 
103
- ![Dara App](https://github.com/causalens/dara/blob/v1.23.0/img/components_gallery.png?raw=true)
103
+ ![Dara App](https://github.com/causalens/dara/blob/v1.23.0-alpha.1/img/components_gallery.png?raw=true)
104
104
 
105
105
  Note: `pip` installation uses [PEP 660](https://peps.python.org/pep-0660/) `pyproject.toml`-based editable installs which require `pip >= 21.3` and `setuptools >= 64.0.0`. You can upgrade both with:
106
106
 
@@ -117,9 +117,9 @@ Explore some of our favorite apps - a great way of getting started and getting t
117
117
 
118
118
  | Dara App | Description |
119
119
  | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
120
- | ![Large Language Model](https://github.com/causalens/dara/blob/v1.23.0/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
121
- | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.23.0/img/plot_interactivity.png?raw=true) | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
122
- | ![Graph Editor](https://github.com/causalens/dara/blob/v1.23.0/img/graph_viewer.png?raw=true) | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
120
+ | ![Large Language Model](https://github.com/causalens/dara/blob/v1.23.0-alpha.1/img/llm.png?raw=true) | Demonstrates how to use incorporate a LLM chat box into your decision app to understand model insights |
121
+ | ![Plot Interactivity](https://github.com/causalens/dara/blob/v1.23.0-alpha.1/img/plot_interactivity.png?raw=true) | Demonstrates how to enable the user to interact with plots, trigger actions based on clicks, mouse movements and other interactions with `Bokeh` or `Plotly` plots |
122
+ | ![Graph Editor](https://github.com/causalens/dara/blob/v1.23.0-alpha.1/img/graph_viewer.png?raw=true) | Demonstrates how to use the `CausalGraphViewer` component to display your graphs or networks, customising the displayed information through colors and tooltips, and updating the page based on user interaction. |
123
123
 
124
124
  Check out our [App Gallery](https://dara.causalens.com/gallery) for more inspiration!
125
125
 
@@ -146,9 +146,9 @@ And the supporting UI packages and tools.
146
146
  - `ui-utils` - miscellaneous utility functions
147
147
  - `ui-widgets` - widget components
148
148
 
149
- More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.23.0/CONTRIBUTING.md) file.
149
+ More information on the repository structure can be found in the [CONTRIBUTING.md](https://github.com/causalens/dara/blob/v1.23.0-alpha.1/CONTRIBUTING.md) file.
150
150
 
151
151
  ## License
152
152
 
153
- Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.23.0/LICENSE).
153
+ Dara is open-source and licensed under the [Apache 2.0 License](https://github.com/causalens/dara/blob/v1.23.0-alpha.1/LICENSE).
154
154
 
@@ -12,12 +12,12 @@ dara/core/actions.py,sha256=rC5Tu79AFNWMv0CJuchBnoy6pETIFh_1RTSqxrolArI,947
12
12
  dara/core/auth/__init__.py,sha256=HJoYIVPzbpwzN_RUHjGpSJj4o5TmHz9yFyZGiRiObCk,906
13
13
  dara/core/auth/base.py,sha256=NJmUJqA-W8AVKIQbX_0BoHoZqtU1Iz6cJ16RKdcdIyU,3642
14
14
  dara/core/auth/basic.py,sha256=sglIaogCslG2HlDMjFsaaJhOJeXUW-QQLTIYPaUPxAU,4927
15
- dara/core/auth/definitions.py,sha256=ZZMXJbMFY42Q5YaJCMwZT0bivnxq7TaIoug7uYqAGzE,3988
15
+ dara/core/auth/definitions.py,sha256=OzJshDLuut8MaM_pbfyQFkSlUuMNBWNszA57jRXF0Cg,3848
16
16
  dara/core/auth/oidc/__init__.py,sha256=UWdhFvDqLCoILaKVWbmrrJgiMgg9wlVZgCxRvf_HGHM,65
17
- dara/core/auth/oidc/config.py,sha256=XdiJSC2-9g8fqM-FkmU0c079VqnrApVv-MKwEBwk50s,23753
17
+ dara/core/auth/oidc/config.py,sha256=5rDxbr3wVZdjIz8USYNeUmKwKfeucHtLgfXwM3_weaE,20835
18
18
  dara/core/auth/oidc/definitions.py,sha256=KLlUl2Y7n6prX0JSAwPde6J43iMs6uUY07OG7kumRPw,17568
19
- dara/core/auth/oidc/routes.py,sha256=wlYp8VZP4C4BFHqJPPYHi3PdLd8SetdG2CtG3B4laBk,5579
20
- dara/core/auth/oidc/settings.py,sha256=wU6fmkqhTNzMNFUxizFaaavH2cKuXeqRIzMi3TpwLhA,2233
19
+ dara/core/auth/oidc/routes.py,sha256=uvNHYUieaXy9hu6MYe_5eZvflX5TmFaIv6EGpCRP-dw,5335
20
+ dara/core/auth/oidc/settings.py,sha256=k1CKDTUKSFoCEp1ASI_R5L_lsNkFzjfAymMMIMmI1AE,2097
21
21
  dara/core/auth/oidc/utils.py,sha256=IL17tStRNpkAQI3M17nrzGeb4xr47zSLPo4wT7LMwBQ,5145
22
22
  dara/core/auth/routes.py,sha256=dtOxpFotnt4XQ4spW3mbyM7ThYRvfIA_oRK5X5lyYHg,7256
23
23
  dara/core/auth/utils.py,sha256=_aHZ98qMJ0VLE9Zfvj5biPARhe973_eyTx2qxnufzRE,7290
@@ -134,8 +134,8 @@ dara/core/visual/themes/__init__.py,sha256=aM4mgoIYo2neBSw5FRzswsht7PUKjLthiHLmF
134
134
  dara/core/visual/themes/dark.py,sha256=QazCRDqh_SCOyQhdwMkH1wbHf301oL7gCFj91plbLww,2020
135
135
  dara/core/visual/themes/definitions.py,sha256=dtET2YUlwXkO6gJ23MqSb8gIq-LxJ343CWsgueWSifM,2787
136
136
  dara/core/visual/themes/light.py,sha256=dtHb6Q1HOb5r_AvJfe0vZajikVc-GnBEUrGsTcI5MHA,2022
137
- dara_core-1.23.0.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
138
- dara_core-1.23.0.dist-info/METADATA,sha256=b0Dh8uldnFW85wZNiC_kePWd9y5B83gmSQmingF7uMo,7524
139
- dara_core-1.23.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
140
- dara_core-1.23.0.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
141
- dara_core-1.23.0.dist-info/RECORD,,
137
+ dara_core-1.23.0a1.dist-info/LICENSE,sha256=r9u1w2RvpLMV6YjuXHIKXRBKzia3fx_roPwboGcLqCc,10944
138
+ dara_core-1.23.0a1.dist-info/METADATA,sha256=WH9TER9_ycRXWaU40zxhvD5teCWhUbExsBay21YHW_w,7598
139
+ dara_core-1.23.0a1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
140
+ dara_core-1.23.0a1.dist-info/entry_points.txt,sha256=nAT9o1kJCmTK1saDh29PFGFD6cbxDDDjTj31HDEDwfU,197
141
+ dara_core-1.23.0a1.dist-info/RECORD,,