Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/druks/accounts/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from contextvars import ContextVar

# The request's authenticated account, stamped by the auth gate — Workflow.start
# reads it so a browser-origin run attributes itself without route ceremony.
current_account_id: ContextVar[str | None] = ContextVar("current_account_id", default=None)
5 changes: 3 additions & 2 deletions backend/druks/accounts/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from fastapi import HTTPException, Request

from druks.accounts import sessions
from druks.accounts.context import current_account_id
from druks.accounts.exceptions import InvalidPatError
from druks.accounts.models import Account, PersonalAccessToken

Expand All @@ -22,7 +23,7 @@ async def current_session_account(request: Request) -> Account:
management, so a token can never manage tokens."""
account = await resolve_session_account(request)
if account:
sessions.current_account_id.set(account.id)
current_account_id.set(account.id)
return account
raise HTTPException(status_code=401, detail="Sign in to use this API.")

Expand All @@ -45,7 +46,7 @@ async def current_account(request: Request) -> Account:
detail=str(error),
headers={"WWW-Authenticate": f'{_BEARER_CHALLENGE}, error="invalid_token"'},
) from error
sessions.current_account_id.set(pat.account_id)
current_account_id.set(pat.account_id)
return pat.account
raise HTTPException(
status_code=401,
Expand Down
5 changes: 0 additions & 5 deletions backend/druks/accounts/sessions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import secrets
from contextvars import ContextVar

from druks.redis import get_client

Expand All @@ -8,10 +7,6 @@
SESSION_COOKIE = "druks_session"
SESSION_TTL_SECONDS = 30 * 24 * 3600

# The request's signed-in account, stamped by the session gate — Workflow.start
# reads it so a browser-origin run attributes itself without route ceremony.
current_account_id: ContextVar[str | None] = ContextVar("current_account_id", default=None)


async def mint_session(account_id: str) -> str:
token = secrets.token_urlsafe(32)
Expand Down
4 changes: 2 additions & 2 deletions backend/druks/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pydantic import BaseModel, ConfigDict, Field, create_model
from uuid_utils import uuid7

from druks.accounts.sessions import current_account_id
from druks.accounts.context import current_account_id
from druks.durable.activity import get_run_phase, set_run_phase
from druks.durable.engine import _step_engine, register_schedule, run_queue, step_session
from druks.durable.enums import AgentCallStatus, RunState
Expand Down Expand Up @@ -636,7 +636,7 @@ async def start(
# subject is required (no default) so a run can't silently lose its
# timeline by omission — pass subject=None explicitly for a background run.
if account_id is None:
# Browser-origin starts inherit the session gate's account;
# Browser-origin starts inherit the request's authenticated account;
# dispatchers that know better pass account_id explicitly.
account_id = current_account_id.get()
if subject is not None:
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,8 @@ def configure_app_for_test(


async def _test_account():
from druks.accounts.context import current_account_id
from druks.accounts.models import Account
from druks.accounts.sessions import current_account_id

account = Account.get_or_create("op@example.com")
current_account_id.set(account.id)
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/test_api_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def client(extension_settings: Settings):


def _account_id() -> str:
# The suite's session gate stands in op@example.com (conftest override).
# The suite's auth gate stands in op@example.com (conftest override).
from druks.accounts.models import Account

return Account.get_or_create("op@example.com").id
Expand Down
4 changes: 2 additions & 2 deletions backend/tests/test_durable_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,9 @@ async def test_attribution_rides_the_run_and_survives_resume(rt):


async def test_browser_origin_start_inherits_the_ambient_account(rt):
# The session gate stamps the request's account into a contextvar;
# The auth gate stamps the request's account into a contextvar;
# start() reads it when no explicit account_id is passed.
from druks.accounts.sessions import current_account_id
from druks.accounts.context import current_account_id

account_id = _account_id(rt.engine, "ambient@example.com")
token = current_account_id.set(account_id)
Expand Down