Skip to content

Commit 75a5277

Browse files
first attempt at kinde instrumentation
1 parent 435dec2 commit 75a5277

6 files changed

Lines changed: 93 additions & 3 deletions

File tree

drift/core/drift_sdk.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,13 @@ def initialize(
241241
"Continuing with SDK initialization. Mock responses will not be available until CLI is running."
242242
)
243243

244+
# # TEMPORARY: Hardcoded time travel for testing
245+
# from drift.instrumentation.datetime import start_time_travel
246+
# # Unix timestamp: seconds + nanos/1e9 + 60 seconds
247+
# timestamp = 1768272255.864658
248+
# start_time_travel(timestamp)
249+
# logger.info(f"HARDCODED time travel to unix timestamp: {timestamp}")
250+
244251
install_hooks()
245252

246253
instance._init_auto_instrumentations()
@@ -466,6 +473,13 @@ def _init_auto_instrumentations(self) -> None:
466473
logger.debug("Socket instrumentation initialized (REPLAY mode - unpatched dependency detection)")
467474
except Exception as e:
468475
logger.debug(f"Socket instrumentation initialization failed: {e}")
476+
477+
try:
478+
from ..instrumentation.kinde import KindeInstrumentation
479+
_ = KindeInstrumentation(enabled=True)
480+
logger.debug("Kinde instrumentation initialized (REPLAY mode - auth token validation)")
481+
except Exception as e:
482+
logger.debug(f"Kinde instrumentation initialization failed: {e}")
469483

470484
def create_env_vars_snapshot(self) -> None:
471485
"""Create a span capturing all environment variables.

drift/core/mock_utils.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,6 @@ def _update_time_travel(
281281
mock_response: The mock response containing the timestamp
282282
replay_trace_id: The replay trace ID for this session
283283
"""
284-
if not replay_trace_id:
285-
return
286-
287284
try:
288285
from drift.instrumentation.datetime import start_time_travel
289286

drift/instrumentation/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
from .base import InstrumentationBase
44
from .django import DjangoInstrumentation
5+
from .kinde import KindeInstrumentation
56
from .registry import install_hooks, patch_instances_via_gc, register_patch
67
from .wsgi import WsgiInstrumentation
78

89
__all__ = [
910
"InstrumentationBase",
1011
"DjangoInstrumentation",
12+
"KindeInstrumentation",
1113
"WsgiInstrumentation",
1214
"register_patch",
1315
"install_hooks",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Kinde SDK instrumentation for bypassing authentication in replay mode."""
2+
3+
from .instrumentation import KindeInstrumentation
4+
5+
__all__ = ["KindeInstrumentation"]
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Instrumentation for Kinde SDK authentication.
2+
3+
Patches is_authenticated() to always return True in REPLAY mode,
4+
allowing tests to bypass authentication checks during replay.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import logging
10+
from typing import Any
11+
12+
from ..base import InstrumentationBase
13+
14+
logger = logging.getLogger(__name__)
15+
16+
17+
class KindeInstrumentation(InstrumentationBase):
18+
"""Instrumentation for the Kinde SDK authentication library.
19+
20+
Patches OAuth.is_authenticated() to:
21+
- Return True in REPLAY mode (bypass authentication)
22+
- Call the original method in RECORD and DISABLED modes
23+
24+
Since SmartOAuth and AsyncOAuth delegate to OAuth.is_authenticated(),
25+
patching OAuth covers all authentication entry points.
26+
"""
27+
28+
def __init__(self, enabled: bool = True) -> None:
29+
super().__init__(
30+
name="KindeInstrumentation",
31+
module_name="kinde_sdk.auth.oauth",
32+
supported_versions="*",
33+
enabled=enabled,
34+
)
35+
36+
def patch(self, module: Any) -> None:
37+
"""Patch the kinde_sdk.auth.oauth module.
38+
39+
Patches OAuth.is_authenticated() to return True in REPLAY mode.
40+
"""
41+
if not hasattr(module, "OAuth"):
42+
logger.warning("kinde_sdk.auth.oauth.OAuth not found, skipping instrumentation")
43+
return
44+
45+
original_is_authenticated = module.OAuth.is_authenticated
46+
47+
def patched_is_authenticated(oauth_self) -> bool:
48+
"""Patched is_authenticated method.
49+
50+
Args:
51+
oauth_self: OAuth instance
52+
53+
Returns:
54+
True in REPLAY mode, otherwise delegates to original method
55+
"""
56+
# Lazy imports to avoid circular dependency
57+
from ...core.drift_sdk import TuskDrift
58+
from ...core.types import TuskDriftMode
59+
60+
sdk = TuskDrift.get_instance()
61+
62+
# In REPLAY mode, always return True to bypass authentication
63+
if sdk.mode == TuskDriftMode.REPLAY:
64+
logger.debug("[KindeInstrumentation] REPLAY mode: returning True for is_authenticated")
65+
return True
66+
67+
# In RECORD or DISABLED mode, call the original method
68+
return original_is_authenticated(oauth_self)
69+
70+
module.OAuth.is_authenticated = patched_is_authenticated
71+
logger.info("kinde_sdk.auth.oauth.OAuth.is_authenticated instrumented")

drift/instrumentation/requests/instrumentation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ def _try_get_mock(
511511
input_value=input_value,
512512
kind=SpanKind.CLIENT,
513513
input_schema_merges=input_schema_merges,
514+
is_pre_app_start=not sdk.app_ready,
514515
)
515516

516517
if not mock_response_output or not mock_response_output.found:

0 commit comments

Comments
 (0)