Skip to content

fix(auth): accept formatted one-time pairing codes - #32145

Open
Sidreyas wants to merge 1 commit into
pingdotgg:mainfrom
Sidreyas:sidreyas/fix-4889-pairing-code-separators
Open

Sidreyas wants to merge 1 commit into
pingdotgg:mainfrom
Sidreyas:sidreyas/fix-4889-pairing-code-separators

Conversation

@Sidreyas

@Sidreyas Sidreyas commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Problem

One-time pairing codes typed with ASCII hyphens or spaces are rejected by the server with 401 auth_invalid, while the same code without separators works. This addresses the separator-rejection part of #4889. The case-only fix in #22089 is already merged; the separate Android false-timeout symptom is outside this change.

Change

Before the one-time pairing-link lookup, remove whitespace and ASCII hyphens only when the remaining value matches the existing generated-code alphabet and length, then use the existing uppercase canonical form. Exact desktop bootstrap matching happens first. The existing atomic consume, expiry, scope, and proof-key checks continue to apply to the canonical pairing code.

Extend the existing PairingGrantStore tests for formatted codes, invalid lengths/alphabet/separators, mixed raw/formatted concurrent consumption, proof-key and scope rejection, and exact desktop credentials.

Scope and approval

This is a small focused fix for an obvious input-formatting bug in the existing pairing capability. It changes the shared server credential boundary used by OAuth and browser-session pairing, so web, desktop, and mobile clients benefit without client or wire-contract changes. It also applies to remote connections because normalization happens on the destination server.

It does not change Android request timing or claim to close the entire issue. No competing open separator fix was found; #22089 explicitly covered case only.

Verification

Windows x64, Node 26.12.1, locked Effect 4.1.2, base 6577c6b6185167c0e502fa01f9e8e8f2dee6ff25.

  • Regression added before the source change: from apps/server, vp test run src/auth/PairingGrantStore.test.ts --maxWorkers 1 failed in the four formatted-code cases, with seven controls passing. Failure: UnknownBootstrapCredentialError for an issued valid code.
  • Fixed selected tests: from apps/server, vp test run src/auth/PairingGrantStore.test.ts src/provider/AgentContextBudget.test.ts --maxWorkers 1 passed all 13 tests in two files.
  • vp fmt --check apps/server/src/auth/PairingGrantStore.ts apps/server/src/auth/PairingGrantStore.test.ts and vp lint with the same two paths passed.
  • vp run knip:check:files and vp run knip:check:exports both passed.
  • vp run --filter ./apps/server generate --check passed.
  • vp run --cache --parallel --concurrency-limit 1 --filter @t3tools/evals --filter @t3tools/scripts --filter t3 typecheck passed all three affected packages.

vp run check:changed --concurrency 1 was attempted on this branch but blocked before its check stages: the ignored-input Git scan follows Windows pnpm junctions and hits the known traversal problem covered by #32023. The wrapper was stopped and is not claimed passed; its cache was not written. Every selected check was run individually with the same scope. Server tests used their owning package configuration because the root browser-project initialization selects port 62126, in this machine's Windows-excluded range; that separate problem is covered by #24007. No repository configuration or check was weakened.

Real network E2E

Ran the actual server CLI twice, before and after the fix, each with a fresh temporary data directory, isolated home and environment, and a reserved loopback port:

node apps/server/src/bin.ts serve --base-dir <fresh-directory> --host 127.0.0.1 --port <port> --no-browser --log-level debug
node apps/server/src/bin.ts auth pairing create --base-dir <same-directory> --json

The CLI-issued raw code authenticated an administrator through /oauth/token. The fixture then issued disposable read-only codes through /api/auth/pairing-token and exercised the actual HTTP handlers, persistence, authentication, and WebSocket upgrade. No provider prompt or live T3 state was used. Credentials were kept out of the transcript. Both owned server processes were stopped afterward.

Flow Before After
Lowercase code grouped with hyphens, OAuth 401 200 Bearer
Code grouped with spaces, OAuth 401 200 Bearer
Mixed whitespace/hyphens, OAuth 401 200 Bearer
Hyphenated code with real ES256 DPoP proofs 401 200 DPoP
Hyphenated code, browser-session cookie 401 200

Before, retrying each same issued code without separators succeeded, proving the credentials and transport were valid. After, all five formatted exchanges established authenticated sessions and returned 200 from /api/orchestration/shell; all three Bearer flows also minted WebSocket tickets and opened /ws. Reusing the original raw code returned 401 in every flow. Slash separators and an invalid alphabet remained rejected without spending a valid code.

This verifies the actual server pairing workflow over loopback HTTP. Native Android/iOS entry, Tailscale HTTPS transport, and the reported Android false timeout were not exercised; there is no changed visual surface.

Implemented with OpenAI GPT-6 via the Codex harness.

Copilot AI balanced review requested due to automatic review settings November 6, 2026 19:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 6, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 74b1089

Macroscope's review found this PR approvable — Pairing codes now accept hyphens and whitespace without changing the underlying secret. Expiration, single-use consumption, proof-key binding and scope checks remain unchanged.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The pairing grant store now accepts issued pairing codes with hyphens or whitespace. Tests cover formatted credentials, concurrent consumption, proof-key checks, and rejection of formatted desktop tokens.

Changes: Pairing code format

Layer / File(s) Summary
Normalize pairing codes and verify consumption
apps/server/src/auth/PairingGrantStore.ts, apps/server/src/auth/PairingGrantStore.test.ts
consume removes hyphens and whitespace before validating issued pairing codes. Tests check formatted credentials, concurrent consumption, proof-key handling, and desktop-token rejection.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Possibly related issues

  • pingdotgg/t3code#4889 reports 401 responses for formatted manual pairing codes. This change accepts valid issued codes with hyphens or whitespace.

Suggested reviewers: only21mil

Merge Risk: Minimal · up to 74b10

Formatted pairing codes can be accepted without changing exact desktop credential matching. No merge-blocking issue was established.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title clearly describes the change to accept formatted one-time pairing codes.
Description check Passed The description includes all required sections. It explains the problem, change, scope, verification results, and checks that were not completed.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 6577c6b and 74b1089.

Files selected for processing: 2

  • apps/server/src/auth/PairingGrantStore.test.ts
  • apps/server/src/auth/PairingGrantStore.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants