You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
External IdP sign-in from the shell (ASWebAuthenticationSession / Custom Tabs) for providers that refuse embedded WebViews #148
Sign-in happens in the Console inside the WebView. An IdP host listed in EXPO_PUBLIC_TRUSTED_HOSTS can load there, and the session cookie lands in the WebView. Some providers refuse embedded WebViews: Google returns disallowed_useragent, and enterprise IdPs may enforce the same. Today those sign-ins open in the system browser, where the resulting cookie never reaches the WebView.
Ask
Design and implement the flow:
The Console starts the social / SSO sign-in.
The shell opens it in ASWebAuthenticationSession / Custom Tabs (expo-web-browseropenAuthSessionAsync).
The server redirects to an app-scheme callback carrying a one-time code.
The shell hands the code to the Console.
The Console exchanges the code for a WebView session.
This needs a server endpoint for the one-time code. Check the existing auth plugin routes first, and file the objectstack-ai/objectstack side as its own card if a route is missing. The code must be single-use, short-lived and bound to the initiating session (PKCE-style), so an intercepted callback cannot be replayed.
Acceptance
Google sign-in works on iOS and Android device builds.
Replaying a callback fails.
docs/HYBRID-SHELL.md (Session and auth) is updated.
Note for triage: this touches a security boundary (session transfer). The flow shape belongs in front of the maintainer before dispatch.
docs/HYBRID-SHELL.md 〈Session and auth〉:"Providers that refuse embedded WebViews (Google) need the ASWebAuthenticationSession flow — tracked as follow-up work"。
Ruling: batch #1 item 4 · letter B · maintainer 「同意」 2026-11-03T02:26Z
The maintainer accepted the batch's recommendations in the session session_01QSeKYnW1Xf6HDSWz1eGAqv. The presentation (item 4) put B as the recommendation, and the reply 「同意」 applies to it.
Option B (the system auth session plus a one-time code exchange) reuses the platform auth library's existing plugin (better-auth one-time-token or Expo plugin) rather than a hand-written endpoint.
Premise of this ruling, falsifiable: better-auth 1.7.12, as installed in objectstack packages/plugins/plugin-auth, ships a plugin that issues a single-use, short-lived code a native app can exchange for a session.
The seat first dispatches a read-only measurement of that premise.
If it holds: an objectstack card enables the plugin (server lane), and this card waits on it with Blocked-by:.
⛔ If it does not hold, report the fork back to the decision box. Do not fall back to C quietly. A is the stated fallback, and it needs its own confirmation.
Effort for B: 1 read-only measurement, then 1 objectstack enable card and 1 mobile PR.
This is a session-transfer flow, so the maintainer merges the PR.
Premise of the ruling measured, and it holds. better-auth@1.7.12 ships ./plugins/one-time-token (/one-time-token/generate, /one-time-token/verify, default 3-minute expiry, hashed storage available). It is not enabled in objectstack plugin-auth today.
Platform card filed: objectstack-ai/objectstack#29714 (server lane; the maintainer merges, because the PR must close callback interception). This card moves pm:queue → pm:blocked with Blocked-by: objectstack-ai/objectstack#29714. It resumes once the plugin ships in an installable platform release.
Claim: maintainer direct order (not a PM loop round), 2026-11-03T16:50Z
Session: Claude desktop session of the maintainer; order verbatim 「开始做 #148」, after 「148 要看看车道有没有认领」
Account: hotlong
Branch: claude/issue-148-system-auth-session
Worktree: mobile-148
File surface: lib/shell/, components/ or app/ for the console screen, app.config.ts (the reverse-domain scheme), docs/HYBRID-SHELL.md, locales/, tests, one changeset
Clause-②: no
Metadata-protocol: none
Thread-read: 6593927768
Premise: holds. @objectstack/plugin-auth@18.3.0 (npm, 2026-11-03T15:11Z) contains the hand-off route, so Blocked-by objectstack-ai/objectstack#29714 has shipped.
Serial constraints cleared: none in flight on these files; #182 / PR #189 (os-justin) touches locales, so new locale keys rebase over it.
Lane read before this claim: no assignee, no branch, no PR, and pm:blocked on this card. Lane seat #160 holds #182 only.
pm:blocked stays on: this is a session-transfer flow, and the flow shape goes to the maintainer, who merges. A readout of the remaining prerequisites follows on this card.
Readout after the claim: the shell half is built (draft PR follows). Three platform pieces are still missing, so Google sign-in cannot work end to end yet.
Blocker status. #29714 shipped in 18.3.0 (npm, 2026-11-03T15:11Z), but that release alone does not make the flow reachable:
Hosted environments pass no scheme, and registerRoutes: false means the route is not mounted
Read from source, not measured
huayan.objectos.ai is on 18.2.0 and answers 404 on both routes.
Flow shape, for the maintainer (this card asks for it before work lands):
The WebView asks for a provider that refuses it. The existing check blocks the load and returns to /login.
The shell asks anonymously whether this server registered the app (GET .../handoff: 401 yes, 400 or 404 no). If not, today's notice stays and no browser opens.
It keeps a PKCE verifier in memory and opens <server>/_console/login?redirect=<hand-off with the S256 challenge> in the auth session, ephemeral on iOS. The user chooses the provider again there.
After the sign-in the Console navigates to the hand-off, which answers 302 ai.objectos.mobile://auth/callback?token=T.
The shell injects a script into the WebView that POSTs T.verifier to /one-time-token/verify, only if location.origin is the server's. The cookie lands in the WebView, and the page moves to the Console or to /login?error=handoff_failed.
Three choices in that shape the card did not specify:
The shell redeems by injected script, not through the Console. The card's step 5 says the Console exchanges the code, and the lane charter says the shell does not call platform APIs. The server's docblock allows either. I chose the script because it needs no new bridge method and keeps the verifier out of URLs. The request is same-origin from the page, and the shell's native side holds no cookie or credential. The alternative is a bridge method plus a Console consumer card.
The user picks the provider twice, once in the WebView and again in the auth session. The provider's URL cannot be reused: its OAuth state cookie lives in the WebView.
A new URL scheme ai.objectos.mobile, because TrustedAppSchemeSchema refuses objectos. That and expo-crypto are native changes, so this needs a new binary.
Known gap on Android. Custom Tabs share Chrome's cookies and have no ephemeral mode through expo-web-browser, so an old Chrome session reaches the hand-off and gets a 401. That is rule 4 on objectstack-ai/objectstack#29971.
Not verified yet: the auth session on a simulator, Android, and Google itself. The server half is verified only by its own tests so far.
The shell half ran end to end on an iOS simulator, with a local stand-in for objectstack-ai/objectstack#29971. Results are in #190. Google itself, Android and a real device are still open, and the three Blocked-by cards still stand.
Activity
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
决策:谷歌这类拒绝内嵌浏览器的登录,怎么把登录态交回 App?
一句话问题: 用谷歌等账号登录时,对方拒绝在 App 内嵌页面里登录。今天这类用户根本登不进 App。要修,就得设计一条把登录结果从系统浏览器安全交回 App 的通道。卡面自己也注明:流程形状需先呈维护者。
Governing text:
docs/HYBRID-SHELL.md〈Session and auth〉:"Providers that refuse embedded WebViews (Google) need theASWebAuthenticationSessionflow — tracked as follow-up work"。前提(带 re-check):
better-auth1.7.12。今天它没有启用一次性码类插件(oneTimeToken)或 Expo 移动端插件,只常开bearer()。re-check(objectstack):git grep -nE "oneTimeToken|@better-auth/expo" origin/main -- packages/plugins/plugin-auth,本席读数为 0 命中。Blocked-by:。选项:
ASWebAuthenticationSession/ Custom Tabs)+ 平台新增一次性码端点(单次、短时、PKCE 式绑定发起会话),Console 用码换 WebView 会话better-auth)的现成一次性码或移动端插件做码交换,不自研端点业务含义: A 是自己造一把一次性钥匙。B 是用锁匠现成的钥匙。C 是先关这扇门。
四轴(业务立场):
docs/DEPLOYMENT.md的审核也看登录可用性。拉动中等,不急。推荐:B,以「认证库有现成移动端流程」为前提。
better-auth1.7.12 的插件清单。裁后执行: 席位派一个只读读数 dev 实测认证库插件。按结果在 objectstack 立平台端点卡(server 车道),mobile 本卡挂
pm:blocked加Blocked-by:指向它。平台端点进可安装的发行版后,再派本卡。Analysis-tier: default (seat tier)
objectstack-fleet commentedon Nov 3, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Ruling: batch #1 item 4 · letter B · maintainer 「同意」 2026-11-03T02:26Z
The maintainer accepted the batch's recommendations in the session
session_01QSeKYnW1Xf6HDSWz1eGAqv. The presentation (item 4) put B as the recommendation, and the reply 「同意」 applies to it.Option B (the system auth session plus a one-time code exchange) reuses the platform auth library's existing plugin (better-auth one-time-token or Expo plugin) rather than a hand-written endpoint.
Premise of this ruling, falsifiable: better-auth 1.7.12, as installed in objectstack
packages/plugins/plugin-auth, ships a plugin that issues a single-use, short-lived code a native app can exchange for a session.Blocked-by:.Effort for B: 1 read-only measurement, then 1 objectstack enable card and 1 mobile PR.
This is a session-transfer flow, so the maintainer merges the PR.
objectstack-fleet commentedon Nov 3, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Premise of the ruling measured, and it holds.
better-auth@1.7.12ships./plugins/one-time-token(/one-time-token/generate,/one-time-token/verify, default 3-minute expiry, hashed storage available). It is not enabled in objectstackplugin-authtoday.Platform card filed: objectstack-ai/objectstack#29714 (server lane; the maintainer merges, because the PR must close callback interception). This card moves
pm:queue→pm:blockedwithBlocked-by: objectstack-ai/objectstack#29714. It resumes once the plugin ships in an installable platform release.hotlong commentedon Nov 3, 2026
Claim: maintainer direct order (not a PM loop round), 2026-11-03T16:50Z
Session: Claude desktop session of the maintainer; order verbatim 「开始做 #148」, after 「148 要看看车道有没有认领」
Account:
hotlongBranch:
claude/issue-148-system-auth-sessionWorktree:
mobile-148File surface:
lib/shell/,components/orapp/for the console screen,app.config.ts(the reverse-domain scheme),docs/HYBRID-SHELL.md,locales/, tests, one changesetClause-②: no
Metadata-protocol: none
Thread-read: 6593927768
Premise: holds.
@objectstack/plugin-auth@18.3.0(npm, 2026-11-03T15:11Z) contains the hand-off route, so Blocked-by objectstack-ai/objectstack#29714 has shipped.Serial constraints cleared: none in flight on these files; #182 / PR #189 (
os-justin) touches locales, so new locale keys rebase over it.Lane read before this claim: no assignee, no branch, no PR, and
pm:blockedon this card. Lane seat #160 holds #182 only.pm:blockedstays on: this is a session-transfer flow, and the flow shape goes to the maintainer, who merges. A readout of the remaining prerequisites follows on this card.hotlong commentedon Nov 3, 2026
Readout after the claim: the shell half is built (draft PR follows). Three platform pieces are still missing, so Google sign-in cannot work end to end yet.
Blocker status. #29714 shipped in 18.3.0 (npm, 2026-11-03T15:11Z), but that release alone does not make the flow reachable:
redirectlands on 页面不存在os servehas no setting fortrustedAppSchemesregisterRoutes: falsemeans the route is not mountedhuayan.objectos.aiis on 18.2.0 and answers 404 on both routes.Flow shape, for the maintainer (this card asks for it before work lands):
/login.GET .../handoff:401yes,400or404no). If not, today's notice stays and no browser opens.<server>/_console/login?redirect=<hand-off with the S256 challenge>in the auth session, ephemeral on iOS. The user chooses the provider again there.302 ai.objectos.mobile://auth/callback?token=T.T.verifierto/one-time-token/verify, only iflocation.originis the server's. The cookie lands in the WebView, and the page moves to the Console or to/login?error=handoff_failed.Three choices in that shape the card did not specify:
ai.objectos.mobile, becauseTrustedAppSchemeSchemarefusesobjectos. That andexpo-cryptoare native changes, so this needs a new binary.Known gap on Android. Custom Tabs share Chrome's cookies and have no ephemeral mode through
expo-web-browser, so an old Chrome session reaches the hand-off and gets a401. That is rule 4 on objectstack-ai/objectstack#29971.Not verified yet: the auth session on a simulator, Android, and Google itself. The server half is verified only by its own tests so far.
hotlong commentedon Nov 3, 2026
Draft PR: #190.
hotlong commentedon Nov 3, 2026
The shell half ran end to end on an iOS simulator, with a local stand-in for objectstack-ai/objectstack#29971. Results are in #190. Google itself, Android and a real device are still open, and the three
Blocked-bycards still stand.