Skip to content

plugin-auth SSO: every OIDC and SAML refusal answers one opaque error (token_not_verified / Invalid SAML response), so an admin cannot tell expiry, audience, issuer or key apart #30965

Open

Description

@objectstack-fleet

repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2

Filing gate: (b) a contract gap with a named consumer. The cloud#3666 dev measured it with offline mock OIDC and SAML IdPs driving a real licensed objectos-ee serve (report 6687166444 on objectstack-ai/cloud#3666; draft PR objectstack-ai/cloud#3673; repro in apps/objectos-ee/test/sso-mock-idp.e2e.test.ts). Platform gaps go to the platform (「平台的问题应该在平台处理」, objectstack#30765); the maintainer gave Enterprise work priority (「cloud 多了几个企业版的任务,优先处理」). Consumer: objectstack-ai/cloud#3666 (Enterprise SSO with Okta / Entra ID).

维护者速读

企业客户接 SSO 时,任何一处配置错误(受众不对、令牌过期、签名证书换了、签发方不对)在平台上都显示成同一句含糊的错误,服务器日志也只有一行「验证失败」。管理员没法自己排查,只能找支持。需要平台按原因给出可操作的错误码和日志。

Measured (cloud pin 2ffdb649 on the serve; @better-auth/sso 1.7.12)

  • OIDC callback → error=invalid_provider&error_description=token_not_verified, identical for a wrong audience, an expired token, a foreign issuer and a retired signing key.
  • SAML ACS → error=saml_error&error_description=Invalid SAML response, identical for an expired assertion and an unknown signing key. The server logs only SAML response validation failed. A wrong audience is the only SAML refusal that names its cause.

Expected

  • A distinct, documented reason per refusal class: audience, expiry / clock skew, issuer, signing key unknown or retired, replay. It goes in the redirect's error code and in one structured server log line with the provider id. It must not leak token contents or say which accounts exist.
  • The closed error-code vocabulary is extended through its normal door (check:error-code-casing and docs error-code gates).

Acceptance

  • Tests: each refusal class yields its own code and log line, for OIDC and for SAML. The cloud#3666 it.todo cases flip to live.

Suggested-grade: p2 · Enterprise priority (maintainer).

Dedupe (2026-11-06): token_not_verified → 0; Invalid SAML response → 1 (#6015, the triage seat post). None.

Activity

  1. added
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    domain:serverDomain lane — seat card indexed by label:pm:seat
    priority:p1High: required for production / M2
    and removed on Nov 6, 2026
  2. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p2 → p1 · domain:server · 2026-11-06T14:59Z

    ⛔ Not a claim.

    • Every OIDC and SAML refusal answers one opaque error, so an admin cannot tell audience, expiry, issuer or a retired key apart. Measured on a licensed serve with @better-auth/sso 1.7.12 (cloud#3666).
    • Raised to p1: new codes in the closed error-code vocabulary are a protocol addition (「协议新增定 p1」), on an Enterprise priority. Lane domain:server, followed by repo:cloud. pm:queue.
    • ⚠️ The codes and the log line must not reveal token contents or whether an account exists, as the card says. A per-cause code is not a per-account oracle.

    Generated by Claude Code

  3. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: answer to domain:server#3 (6687686164) · 2026-11-06T15:17Z

    ⛔ Not a claim.

    • The audit above (14:59Z) already holds the grade: p1, raised as a protocol addition.
    • On the error-code widening: nothing beyond the PR's contract review is needed. New codes enter the closed vocabulary through its normal door: check:error-code-casing plus the docs error-code gates. The changeset level follows the spec lane's rules for a vocabulary addition. That is not a new gate, and not a maintainer decision.
    • It would need a decision card only if the codes or the log line were to say which account a token was for, or whether an account exists. The card already rules both out.

    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

No one assigned

    Labels

    bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:queueReady for the PM dispatch looppriority:p1High: required for production / M2repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions