Skip to content

feat(client): adopt gateway bearer sessions - #437

Open
kartojal wants to merge 5 commits into
feat/authenticated-service-clientfrom
feat/dev-749-gateway-session
Open

kartojal wants to merge 5 commits into
feat/authenticated-service-clientfrom
feat/dev-749-gateway-session

Conversation

@kartojal

@kartojal kartojal commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Adopts gateway bearer sessions for prediction clients: challenge/login, restoration, automatic L2 migration, shared HTTP/WebSocket renewal and logout.

createSecureClient accepts plain saved bearer or legacy L2 credential objects. L2 credentials are migrated without a new wallet signature; rejected migration rejects creation without calling the persistence callback. Login, migration, secureClient.credentials and onCredentialsChanged expose only the plain { accessToken, refreshToken } pair.

login owns the challenge/signature flow and returns credentials. Client creation resolves credentials before constructing the client, replacing the internal beginAuthentication generator. Supplied signer-only bearer credentials skip wallet discovery and deployment, allowing authenticated account requests before wallet setup. Wallet-dependent operations reject clearly when the session has no wallet. Fresh authentication retains Deposit Wallet discovery and deployment.

Order ownership comes from the session trading identity. Direct API-key creation, derivation and management actions are removed. Business-action routing and platform-key adapters remain separate stack layers.

Client creation now owns newly issued login/migration sessions until setup succeeds. Identity, construction, readiness, wallet-check and deployment failures attempt logout and close local resources while preserving the original error. New-session refresh notifications are deferred until successful creation; restored bearer sessions retain their existing ownership and persistence behavior. Local cleanup rejections are observed immediately while logout is pending.

Stack: #442 → #481 → #437 → #463 → #438 → #441.

Restacked on 2026-11-02 with the current retry/transport ancestry and #463 Gateway routing corrections. Existing plain bearer credentials, automatic migration and signer-only restoration are retained.

Combined stack validation on 2026-11-02: build, lint, workspace typecheck, browser bundle check, 415 bindings tests and 1,159 client tests passed (1,574 total, no type errors). Failure-path coverage uses controlled responses. Live production checks additionally passed for an existing Deposit Wallet: signed login, automatic saved-L2 migration without signing, authenticated order/trade/notification/trading-status/balance-allowance reads, bearer restoration/refresh, and logout with rejected refresh reuse. No trading, transfers, approvals or wallet deployment were performed.

Gateway heartbeat compatibility remains blocked as documented in #463. Human approval and outstanding review threads have not been resolved automatically.


Note

High Risk
This is a breaking authentication migration touching session lifecycle, token persistence, logout/revocation, and trading identity—errors can strand users or leave orphaned sessions.

Overview
Replaces L2 API-key authentication for prediction clients with gateway bearer sessions (accessToken / refreshToken). createSecureClient now logs in via challenge/signature, migrates saved L2 credentials without a new signature, or restores bearer tokens (including signer-only sessions that skip wallet discovery). secureClient.credentials, login, migrateCredentials, and onCredentialsChanged expose only the plain bearer pair; direct API-key create/derive/list/delete/revoke actions are removed.

Adds AuthSession for shared proactive refresh, single-flight renewal, HTTP expired_access_token replay, and /v1/auth/logout on endAuthentication. Secure HTTP and websocket paths send Authorization: Bearer; CLOB user, realtime, and RFQ sockets authenticate with access tokens only (legacy L2 socket shape remains where needed). Order posting uses the session’s clob_api_key claim instead of credentials.key. Failed creation revokes newly issued sessions and defers persistence callbacks until setup succeeds; docs/authentication-migration.md documents upgrade steps.

Reviewed by Cursor Bugbot for commit 6626705. Bugbot is set up for automated code reviews on this repo. Configure here.

@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch from 3728947 to 599f25c Compare October 20, 2026 14:32
@kartojal
kartojal changed the base branch from main to feat/auth-refresh-callback October 20, 2026 14:33
@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch from 599f25c to a548456 Compare October 20, 2026 16:16
@kartojal
kartojal added this pull request to stack #443 October 20, 2026 16:27
@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch 3 times, most recently from efd471d to f74c9e2 Compare October 21, 2026 08:52
@kartojal
kartojal marked this pull request as ready for review October 21, 2026 08:56

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@kartojal kartojal mentioned this pull request Oct 21, 2026
@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch from 22f45e9 to 7bc8688 Compare October 22, 2026 11:16
@kartojal
kartojal removed this pull request from stack #443 October 22, 2026 14:42
@kartojal
kartojal added this pull request to stack #464 October 22, 2026 14:42
@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch from ce85def to 771af35 Compare October 23, 2026 12:08

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@kartojal
kartojal removed this pull request from stack #464 October 26, 2026 15:46
@kartojal
kartojal changed the base branch from feat/auth-refresh-callback to feat/authenticated-service-client October 26, 2026 15:47
@kartojal
kartojal added this pull request to stack #482 October 26, 2026 15:47

Copy link
Copy Markdown
Collaborator

Should createSecureClient automatically migrate supplied L2 credentials to bearer credentials, as described in the SDK guide? The idea was to keep the same restore call and save secureClient.credentials afterward. Here it looks like we return an L2 client instead. Is this an oversight in this PR, or is automatic migration coming later in the stack?

cesarenaldi commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Can we keep the public credential shapes free of a type discriminator? Existing L2 credentials are accepted for migration compatibility. We do not intend to expose L2 credentials as a long-term output of the new authentication flow.

// Accepted saved input shapes, no added metadata.
type Credentials = ApiKeyCreds | BearerCredentials;

type BearerCredentials = {
  accessToken: string;
  refreshToken: string;
};

// The new flow exposes the plain bearer pair in both places.
const credentials: BearerCredentials = secureClient.credentials;

onCredentialsChanged: (credentials: BearerCredentials) =>
  saveCredentials(credentials);

If a discriminator helps internal branching, the parsing schema can produce a separate internal tagged representation:

type ParsedCredentials =
  | { type: 'l2'; credentials: ApiKeyCreds }
  | { type: 'bearer'; credentials: BearerCredentials };

Keep that tag out of public credential inputs, secureClient.credentials, and onCredentialsChanged.

cesarenaldi commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

I noticed login returns credentials. The Notion web integration showed it returning a SecureClient, but I think the approach here is better: login and migration return credentials, and the UI explicitly saves them before creating the client. I’ve updated the docs to match.

const credentials = await publicClient.login({
  signer,
  verificationToken,
});

await saveCredentials(credentials);

const secureClient = await createSecureClient({
  signer,
  credentials,
  onCredentialsChanged: saveCredentials,
});

const account = await secureClient.predictions.get('/internal/users/me');

Could we evolve the existing internal beginAuthentication flow into login, rather than fit this flow into what beginAuthentication currently does? login can own the challenge/signature flow and return credentials; createSecureClient can use supplied credentials or call login when needed, then construct the client.

Supplied signer-only bearer credentials need to work without wallet discovery or deployment during client creation. Currently, beginAuthentication rejects a missing wallet claim, preventing the UI from fetching /me before wallet setup. The public credential shape should also stay untagged, as discussed in the earlier comment.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d356e0d. Configure here.

Comment thread packages/client/src/clients.ts Resolved
@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch from d356e0d to ed7c73d Compare November 2, 2026 14:23
@kartojal
kartojal force-pushed the feat/dev-749-gateway-session branch from 2f15809 to 6626705 Compare November 3, 2026 17:52
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.

2 participants