Repository navigation
Portal: let in people by Portal's own sign-in (private link, wallet, Google or company login) #2089
Description
Problem
Settings → Phone & devices → Portal (#746) can put the app on a public address, but the only ways through it are the access token and a paired device. Neither can be given to a named person for a limited time. The token is one secret that everyone shares, and only changing it takes it back from one person. A pairing has to be started by the owner and a code passed on, and the device then stays paired until it is revoked.
Portal v2.7.0 added sign-ins in front of the address (a private link that expires, an Ethereum wallet allow-list, Google or company login through OpenID Connect) and --auth-identity-proof, which lets an app behind Portal believe who signed in. The app cannot use any of it. It ignores Portal's identity headers, and a hand-run portal expose --auth … in front of the port only adds a second gate: a visitor passes Portal's sign-in and is then asked for the token anyway (or, with no token set, is taken for this computer, as discussed in #202).
Proposal
The change is already written and open as #2065. This is the issue its description offered to open, so the approach can be agreed before anyone reviews the code. In short:
- The stopped Portal group gets Who may open the address: the access token or a paired device (the default, unchanged), a private link, an Ethereum wallet, or Google or company login, each with the names, wallets or accounts it lets in. A choice is offered only when the installed
portal expose --helplists every flag it needs, so an older Portal gets the panel it has today. - Whoever Portal lets in is let in like a paired device that may drive, with no token asked, and Settings → Devices says who signed in.
- The app believes Portal's identity only on a request that carries a proof: a secret the app makes at each launch of a Portal that has a sign-in and hands to Portal's environment (
AUTH_IDENTITY_PROOF, never an argument), compared in constant time. Without the proof the identity headers are ignored and the request meets the token rules like any other. - The OIDC client secret is read from
OVIS_PORTAL_OIDC_CLIENT_SECRETand handed to Portal's environment alone: not typed in the panel, not in an argument, not inportal/state.json, not in an answer. - A private-link Portal offers Invite someone (a link for 1, 7, 30 or 90 days, shown once), and a browser Portal let in can sign out from Settings → Devices.
If all of that is more than you want to carry, it splits into parts that stand on their own, and I would rather ship the part you say yes to than the whole:
- The proof and the
portalway in, with the private link as the only sign-in. - The wallet and the Google or company login on top of that.
- Invite someone and sign-out.
Where
Access, pairing and devices; Phone, installed app and alerts.
Where it exists today
Not in herdr's TUI, which has no remote address. As far as I know none of the Codex and Claude apps, Paseo or T3 Code offers a public address with a sign-in of its own, so this does not meet the rule in CONTRIBUTING on those grounds, and I would rather say so than stretch it.
The nearest case is in this app. decideAccess already lets in a person whom a front door vouches for without a code: with tailscale serve, the Tailscale-User-Login header lets the owner's own devices in (via: "tailscale" in server/access.ts). This is the same idea for Portal's front door, with the proof added because Portal's headers can be sent by anything that reaches the port.
Outside coding-agent apps, Cloudflare Access does the same job for any internal app: allow rules by email and email domain, and a choice of login methods.
Workarounds and alternatives
- Today: give out the access token, or pair each person's browser with a code (the guide's current Portal section).
- A reverse proxy or identity-aware proxy you run yourself in front of the app. That works, but it is a second system to run, and the app still asks for its own token behind it.
- Documenting
portal expose --auth …only. It does not work without the app believing the identity, for the reason above.
Activity