Skip to content

feat(messaging): external auth backend + internal-token auth path (PR-5c)#26

Merged
govindkavaturi-art merged 1 commit intomainfrom
feat/dock-readiness-5c-external-auth
May 2, 2026
Merged

feat(messaging): external auth backend + internal-token auth path (PR-5c)#26
govindkavaturi-art merged 1 commit intomainfrom
feat/dock-readiness-5c-external-auth

Conversation

@govindkavaturi-art
Copy link
Copy Markdown
Member

tl;dr

PR-5c of the Dock-readiness PRD. Lets self-host integrators bring their own user identity system and authenticate cueapi-core via a shared service-to-service token.

What lands

Surface What
Config New `EXTERNAL_AUTH_BACKEND` flag + `INTERNAL_AUTH_TOKEN` value (in `app/config.py`)
Auth path New internal-token branch in `app/auth.py:get_current_user`. Constant-time bearer compare; caller declares acting user via `X-On-Behalf-Of: <user_uuid>`
New endpoint `PUT /v1/internal/users/{user_id}` — idempotent upsert. Auth: only `INTERNAL_AUTH_TOKEN`. Conditionally mounted only when flag is on
Tests `tests/test_dock_readiness_external_auth.py` — 3 classes, 9 cases covering default-off / flag-on / legacy-paths-still-work

How Dock will use this

```
EXTERNAL_AUTH_BACKEND=true
INTERNAL_AUTH_TOKEN= # python -c "import secrets; print(secrets.token_urlsafe(48))"
DISABLE_DEVICE_CODE=true # from PR-5d
DISABLE_CUE_PRIMITIVE=true # from PR-5d
DISABLE_QUOTA_ENFORCEMENT=true # from PR-5d
```

Then Dock-side flow:

  1. User signs up on Dock (Dock's identity system mints the User UUID)
  2. Dock cloud calls `PUT https://cue.dock.svc/v1/internal/users/{user_id}\` with `Authorization: Bearer <INTERNAL_AUTH_TOKEN>` and the user's email + slug
  3. cueapi-core upserts the row in its `users` table
  4. Dock cloud calls cueapi-core endpoints via `Authorization: Bearer <INTERNAL_AUTH_TOKEN>` + `X-On-Behalf-Of: <user_id>` header to act as that user

Backward compatibility

The new internal-token path is additive. Per-user API-key auth (`cue_sk_*`) and JWT session auth remain available. Self-hosters can run mixed traffic during migration. Pinned by `TestLegacyPathsStillWork`.

Tests

```
tests/test_dock_readiness_external_auth.py::TestExternalAuthDefaultOff (2 cases)
tests/test_dock_readiness_external_auth.py::TestExternalAuthFlagOn (6 cases)
tests/test_dock_readiness_external_auth.py::TestLegacyPathsStillWork (1 case)
```

Test plan

  • CI `test` + `sdk-integration` + `parity-check` green
  • After merge: hosted cueapi prod still authenticates via cue_sk_ keys (default-off path preserved)
  • Dock can vendor + flip flags + upsert their first user

🤖 Generated with Claude Code

…-5c)

PR-5c of the Dock-readiness PRD
(https://trydock.ai/dock/prd/cueapi-port). Lets self-host integrators
(Dock first; future others) bring their own user identity system and
authenticate cueapi-core via a shared service-to-service token
instead of per-user API keys.

What lands
----------

* New env vars in ``app/config.py``:
  - ``EXTERNAL_AUTH_BACKEND`` (bool, default False) — flag that
    activates the new auth + endpoint surface.
  - ``INTERNAL_AUTH_TOKEN`` (str, required when flag is True) —
    the shared bearer token. Recommended: ``secrets.token_urlsafe(48)``.

* New auth path in ``app/auth.py:get_current_user``:
  When the flag is on AND the request's bearer matches
  INTERNAL_AUTH_TOKEN (constant-time HMAC compare), the request is
  treated as service-to-service. Caller declares acting user via
  ``X-On-Behalf-Of: <user_uuid>`` header. New helper
  ``_auth_via_internal_token`` resolves the header to a User row.

* New endpoint ``PUT /v1/internal/users/{user_id}`` in
  ``app/routers/internal_users.py``:
  Idempotent upsert keyed by UUID. Required body: ``email``, ``slug``.
  Optional: plan, all four limit fields. First call creates with
  defaults; subsequent calls update. Auth: only requests bearing
  INTERNAL_AUTH_TOKEN (constant-time compare). Conditionally mounted
  in ``app/main.py`` only when EXTERNAL_AUTH_BACKEND=True — default
  deployments don't expose ``/v1/internal/*`` at all.

Per Dock's PRD §"Open question 2" the EXTERNAL_AUTH_BACKEND flag and
DISABLE_DEVICE_CODE flag (PR-5d) are independent. PR-5c only adds the
token + endpoint paths; integrators who want to ALSO strip the email-
magic-link signup flow set DISABLE_DEVICE_CODE=True separately.
The two flags compose without conflict.

Important property: the new internal-token path is ADDITIVE, not
replacing. Per-user API-key auth (cue_sk_*) and JWT session auth
remain available alongside it. Self-hosters can run mixed traffic
during migration.

Tests
-----

tests/test_dock_readiness_external_auth.py (3 classes, 9 cases):

* TestExternalAuthDefaultOff:
  - internal/users route absent when flag off
  - internal-token auth unreachable when flag off (request with
    matching token still falls through to per-user lookup → 401)

* TestExternalAuthFlagOn:
  - internal/users mounts when flag on
  - upsert creates a new user, idempotent re-call updates without
    clobbering fields the caller didn't pass
  - upsert with wrong token → 401 invalid_internal_token
  - internal-token + valid X-On-Behalf-Of → request authenticates
  - internal-token without X-On-Behalf-Of → 400 with explicit code
  - internal-token + nonexistent user UUID → 404 user_not_found
    (integrator must upsert first)

* TestLegacyPathsStillWork:
  - per-user cue_sk_* path still 401s with invalid_api_key (not
    some internal-token-related error) when flag is on, proving
    legacy auth is still active

Tests use the same ``_patch_settings`` + ``_reimport_main`` pattern
from PR-5d so the FastAPI app object re-evaluates router mounts.
Settings restored on exit; no test pollutes neighboring tests.

What's NOT in this PR
---------------------

* PR-5a Multi-shell same-agent claims (separate, schema change)
* PR-5b Pluggable cross-user authz hook (separate)
* PR-5d Operational packaging knobs (already shipped — DISABLE_*)

Each PR-5 sub-feature is independent and can land in any order.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

Parity check

This PR modifies files tracked in parity-manifest.json:

  • app/auth.py
  • app/config.py
  • app/main.py

Please confirm one of the following in a reply or PR description update:

  1. The equivalent change has been applied to the private cueapi monorepo. Link the PR.
  2. This change is OSS-only and does not need porting. Briefly explain why (e.g. "fixes a bug that only exists in the OSS build").
  3. A follow-up issue has been filed to port the reverse direction. Link the issue.

This is a soft check — it does not block merge. The goal is visibility, not friction. See HOSTED_ONLY.md for the open-core policy.

@govindkavaturi-art govindkavaturi-art enabled auto-merge (squash) May 2, 2026 07:44
@govindkavaturi-art govindkavaturi-art merged commit 7c105fb into main May 2, 2026
5 checks passed
@govindkavaturi-art govindkavaturi-art deleted the feat/dock-readiness-5c-external-auth branch May 2, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant