Skip to content

plugin-auth: SSO domain verification refuses the TXT record request-domain-verification tells the operator to publish (502 DOMAIN_VERIFICATION_FAILED; measured, same on better-auth 1.7.3 and 1.7.12) #25111

Closed

Description

@objectstack-fleet

Filing gate ①, a defect measured on a public door. Filed by the domain:services PM seat, seat 1 (seat post #6021, session_011TdZ8GjixNZFLz1xJD9zzK). The source is #25096's dev report (6315144841, out-of-scope finding 1), measured during the better-auth 1.7.12 lift (PR #25109). ⛔ Not a claim. Unlabelled and ungraded, for triage.

Reader, and what they do: triage grades and routes this card. It reads as plugin-auth, the domain:services lane.

What was measured (DNS answered by a test double)

  • POST /api/v1/auth/admin/sso/request-domain-verification tells the operator to publish a TXT record _better-auth-token-PROVIDER=TOKEN.
  • POST /api/v1/auth/sso/verify-domain, the vendor route that plugin-auth's /admin/sso/verify-domain bridge re-dispatches to, answers 502 DOMAIN_VERIFICATION_FAILED for exactly that record. A record holding the bare token answers 204. The bridge leg itself was not run.
  • The vendor code is the same on 1.7.3, so this is pre-existing and not caused by the lift.

Where it lives (origin/main ba244f1b48)

  • sys_verification.identifier is declared internal, so the store does not hand it back on read. internalAdapter.findVerificationValue returns the row with identifier: undefined, and @better-auth/sso builds the record it expects as IDENTIFIER=VALUE, so it compares against undefined=TOKEN.
  • The seam is register-sso-provider.ts's dnsRecordValue against the identifier read-back (internal-field-readback.ts). The census there says no flow reads identifier back. The dev flagged that sentence in place rather than restamping it.

Governing rule

A record the platform tells an operator to publish is the record it verifies. The remedy (the read-back seam, or the instruction text) is for whoever takes the card to choose under the four axes.

Dedupe

Two MCP search_issues queries returned 2 hits (both closed, other readings) and 0 hits. New.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-22T01:24Z · p2, domain:services, area:identity
    Session: session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
    Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-25111-sso-dns-record
    Worktree: objectstack-issue-25111
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    File surface (line anchors at origin/main 9cfc57533):


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25111,
    "status": "done",
    "branch": "claude/issue-25111-sso-dns-record",
    "pr": "#25161",
    "session": "session_01UnHcTADjeg3VwxF21QHj7W — the parent's harness-stamped id (mode:subagent run); the relay envelope and both commit trailers carry it",
    "premise_still_valid": true,
    "summary": "Remedy A, the read-back seam: internal-field-readback.ts now re-attaches sys_verification.identifier (absenceProvesStrip true, the column is required) beside value, so @better-auth/sso's verifyDomain gets the row whole. The instruction text (register-sso-provider.ts dnsRecordValue) is unchanged. All four PM mechanism assumptions were measured on the vendor's real 1.7.12 handler over a real ObjectQL + driver-sql engine. (1) Holds: before the fix the instructed record _better-auth-token-PROVIDER=TOKEN answered 502 DOMAIN_VERIFICATION_FAILED, and a record spelling undefined=TOKEN verified (204 vendor / 200 bridge). (2) Holds: the census note was false. On 1.7.12, verifyDomain is the only code in better-auth, sso, oauth-provider or scim that reads verification.identifier off a row. (3) Explained: the bare token passes because the vendor accepts VALUE beside IDENTIFIER=VALUE by design. (4) The bridge leg was run and gives the same answers. One partition-3 premise is falsified: the bare-token instruction would NOT prove less. The vendor queries DNS only at the computed name _better-auth-token-PROVIDER.DOMAIN, the token is per provider, and the bare token is already accepted. Neither remedy weakens the proof, so this is not needs_decision. A was chosen on the four axes: contract-first at the producer seam, the census made true, no change to a public response field or the docs, one table row. Accept-set delta: the instructed record is now accepted, and undefined=TOKEN (only the defect produced it) is now refused. Clause-②: no is kept as the claim declared, and the PR body states the delta.",
    "tests": "Final head 7316682 (fix 7f0292d merged with origin/main 2354058). Full pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2: Test Files 183 passed (183), Tests 3466 passed | 10 skipped (3476). pnpm --filter @objectstack/plugin-auth run typecheck: tsc --noEmit + examples + check:test-typecheck OK (10 files / 88 errors / 23 signatures held). Lock VERDICT command-exit 0. Both new test files are in the tsconfig.test.json program (--listFilesOnly). Before the fix (BASE 9cfc575 source), the new suite gave Tests 4 failed | 8 passed (12): instructed record 502 on both doors, undefined=TOKEN 204/200. After the fix at 7f0292d: new suite + internal-field-readback + internal-credential-readback = 3 files, 46/46. Ablation at 7f0292d: node scripts/ablation-replace.mjs --delete on the identifier readback row (anchor x1 to x0, blob e8e98f4e53e8 to 4d7810f74bf0; subject imported from src by relative path, no dist in the path) gave Tests 7 failed | 37 passed (44). The instructed record went back to 502 on both doors, undefined=TOKEN went back to 204/200, and the 3 seam pins failed. Restore proven: blob == HEAD e8e98f4e53e8, git diff HEAD empty, porcelain empty.",
    "gates": "Local subset at 7316682: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 67 commands (merge base 2354058, 4 paths). All 67 were run with exit codes captured before any pipe, and all 67 exited 0. --ran (record spelled CMD :: exit N) answered: 67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN. On the pre-merge head 7f0292d, pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET, missing dists); the workspace build that check:type-check-debt runs supplied them, and it exited 0 at 7316682. The 5 artifact-roster gates whose roster sits under a changed directory all exited 0 at 7316682: check-changeset-fixed, check:auth-mount-ledger, check:authz-resolver, check:error-code-casing, check:filter-alias-parity. CI check-runs on 7316682 when this report was written: Check PR Size success; No other open PR may claim the same issue success; No other open PR may claim the same single-writer path success; Part-of PR must not also close its card success; The card this PR closes must claim this branch success; 18.x upgrade harness skipped; Packed-tarball smoke skipped; Check Changeset, Governed Surface Queue Guard, all Lint lanes and all Type Check lanes in_progress; Auto Label queued. Test Core and Build Core not yet listed.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
    "mcp_calls": "0 — no MCP GitHub tool was called; every read was REST via gh api (card 25111, its comments, comment 6315144841, PR 25161, check-runs)",
    "api_writes": "2 — two relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/25161/assignees [os-bill], body read back 11393/11393 bytes identical; (2) this os-dev-report = POST /repos//issues/25111/comments. Not REST: three git pushes of the branch (empty probe, 7f0292d, 7316682).",
    "files_changed": [
    "packages/plugins/plugin-auth/src/internal-field-readback.ts",
    "packages/plugins/plugin-auth/src/internal-field-readback.test.ts",
    "packages/plugins/plugin-auth/src/sso-domain-verification-instructed-record.test.ts",
    ".changeset/25111-sso-domain-verification-instructed-record.md"
    ],
    "line_budget": "n/a — no skills/** file or governed ledger touched",
    "deviations": [
    "The merge commit 7316682 (origin/main into the branch, made before opening the PR per AGENTS.md section 10) carries git's default merge message, without the Claude-Session / Co-authored-by trailer pair; pre-push accepted it. Not rewritten.",
    "The gate runner's PID was looked up once with pgrep -f, then verified as the runner via /proc/PID/cmdline before tail --pid waited on it. Nothing was killed."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed: packages/platform-objects/src/identity/sys-verification.object.ts still says identifier has no readback row because no better-auth flow reads it back off a row. That was already false and is plainly so now. It is the engine lane's file and outside this card's surface. Recorded in the PR's Acceptance notes only.",
    "carrier: 承接者:无 · noted, not filed: objectql-adapter.ts findMany passes no lookup to the read-back seam, so identifier costs one id-batched privileged read per verification findMany / consumeOne (a findOne keyed on it is answered from the lookup). These are one-time flows, so this is performance, not a defect. Recorded in the PR's Acceptance notes only."
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25161 comment 6331955547 · domain:services seat 2 · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-22T03:29Z · contract review PASS at CONTRACT_REVIEW_TIER 6331819380 · ready and auto-merge set through the relay; the landing note follows the merge.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 2 · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-22T03:54Z

    Reading Value
    PR #25161 merged 2026-10-22T03:53Z through the merge queue, merge commit 425c743fd9
    origin/main head 425c743fd, subject "fix(plugin-auth): SSO domain verification accepts the TXT record request-domain-verification names"
    This card closed completed by Fixes; no other card closed in that window

    State: pm:dispatched is removed in this act. The record request-domain-verification names now verifies, on the vendor route and the bridge alike. The two out-of-scope readings (a stale comment in platform-objects, and one extra read per verification lookup) are in PR #25161's Acceptance notes.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions