Sign-in on the agent's page
read as.mdAn agent’s standalone page — its slug host, a
custom domain, or the first-party share page (its always-on
<app-id>.agents.… domain is the agent’s API address and serves no page)
— has no host page to hand it a token, so it signs visitors in itself. Which way it does
that follows from the app’s authentication mode (Users → Authentication
in the console, --auth-mode on the CLI):
| Authentication mode | On a custom domain | On a Guuey host (slug host / share page) |
|---|---|---|
| Anonymous | guests | guests |
| Guuey sign-in | Guuey sign-in — a full-page hop to Guuey’s login, back to the page | same |
| Bring your own auth | your identity endpoint if you set one; else your identity provider if you registered a client; else guests | your identity provider if you registered a client; else guests |
The rest of this page is the last row: how bring-your-own apps sign visitors in without a Guuey account and without a Guuey sign-in moment.
Two mechanisms, one identity
Section titled “Two mechanisms, one identity”Both bring-your-own paths end in the same place — a token the agent verifies against your issuer, resolving to the same end-user identity your widget embed and custom surface resolve. One history, one memory, one profile, whichever surface the person used.
- Your identity endpoint — silent. The page fetches a URL on your own site with the visitor’s session cookie and gets a token back; no redirect, no login screen if they are already signed in with you. It only works where that cookie is reachable: a custom domain on the same site as your session. Set it up →
- Your identity provider (OIDC redirect) — a full-page hop to your
IdP’s login page and back, on any host the page is served on,
including Guuey’s own. Register one client at your IdP; Guuey runs the
standard authorization-code flow (PKCE,
openidscope) and hands the page the ID token your IdP issued. This page covers it.
If you configure both, the endpoint wins on your custom domain (it is silent where it exists) and the redirect covers everywhere else.
How the token itself is verified — signature, issuer, audience, guests — is the same on every surface; that model is on The identity model.
Set up the redirect
Section titled “Set up the redirect”1. Register a client at your identity provider
Section titled “1. Register a client at your identity provider”Create an OIDC client (Auth0 “Regular Web App”, a Cognito app client,
an Okta web app — any provider that publishes
/.well-known/openid-configuration) with one redirect URI, the
Guuey login relay’s callback for your environment:
https://api.us-east-1.guuey.com/v1/auth/relay/completeThe console shows the exact value for your environment under
Users → Authentication → Sign-in on standalone pages, and
guuey apps get prints it once a client is registered. It is the same
URI for every app — Guuey tells the flows apart by a one-time state it
mints per sign-in.
Public client with PKCE is the default and works with most providers. If yours insists on a confidential client, it may issue a client secret; Guuey can hold it for you (sealed, never echoed) — see step 2.
2. Tell Guuey the client
Section titled “2. Tell Guuey the client”The client’s Client ID must be the app’s Audience. That is not a
Guuey rule but an OpenID one: the token the page receives is the ID
token, and an ID token’s aud claim is the client it was issued to.
The agent verifies every token against the app’s Audience — the same
check that already applies to tokens your widget embed forwards, which is
why the SDK page tells you
to forward the ID token, not the access token.
guuey apps update <appId> --auth-mode byo \ --issuer-url https://login.example.com \ --audience <client-id> \ --oidc-client-id <client-id># confidential client:# … --oidc-client-secret <secret> (sealed under KMS; pass an empty value to remove)# turn page sign-in off again:# guuey apps update <appId> --issuer-url … --audience … --clear-oidc-clientOr in the console: Users → Authentication → Bring Your Own Auth → Sign-in on standalone pages — the Client ID field shows the Audience as its placeholder; type it (or your client id) to register the client. Leaving the field empty keeps page sign-in off. (Client secrets are stored through the CLI; the console shows whether one is stored.)
Guuey refuses a client on a Guuey-owned issuer — the app’s own widget
issuer (guuey widget keys create) mints tokens server-to-server and has
no login page to send anyone to. This path is for your IdP.
3. That’s it
Section titled “3. That’s it”Visitors on any of the app’s standalone hosts see Sign in; the click
goes to your IdP’s login and comes back on the page’s root, signed in.
guuey apps get shows Page sign-in: OIDC redirect (client …, public client) and the public agent card carries oidcRelay: true.
What happens under the hood
Section titled “What happens under the hood”- Start — the page sends the visitor to
/v1/auth/relay/start?appId=…&returnTo=<its own URL>. Guuey checks thereturnToagainst the hosts the app’s page is served on (verified custom domains, the slug host, the share page — the app’s own hosts, nothing else), fetches your issuer’s discovery document, and redirects to itsauthorization_endpointwithresponse_type=code,scope=openid, PKCE (S256), astateand anonce. - Callback — your IdP sends the browser back to the relay’s callback with the code. Guuey checks the state (single-use, ten-minute lifetime, bound to the browser that started by an httpOnly cookie) and forwards the visitor to the page’s root with a ≤60-second, single-use hand-back code — never the IdP’s code, never a token.
- Exchange — the page redeems the hand-back code same-origin. Guuey
POSTs your
token_endpoint(client authentication as your discovery document allows), takes only the ID token, verifies it against your issuer’s published keys, the app’s Audience and the dance’snonce, and returns it to the page. The refresh token — if your IdP sent one — is discarded. - Chat — the page holds the ID token in memory and presents it as the bearer to the agent, exactly as an embed forwards your token. When it expires the page says Session expired — sign in again and the hop repeats; with a live IdP session that is one round trip and no prompt.
Guuey stores nothing durable: no session, no refresh token, no account for the visitor. The only rows are the two short-lived nonces above.
Requirements and limits
Section titled “Requirements and limits”- The issuer must publish
/.well-known/openid-configurationwhoseissueris your configured Issuer URL, withhttpsendpoints on public hosts. Providers that advertise PKCE methods must includeS256. - The issuer’s signing keys are read from
<issuer>/.well-known/jwks.json(Auth0, Cognito, Clerk and most providers serve them there). - Client ID = Audience (above). An app whose Audience is an API identifier rather than a client id cannot use page sign-in until it switches its embed to ID tokens.
- Client secrets are stored through the CLI only — the console shows whether one is stored. For a workspace-owned app, the CLI path is open to the app’s creator and to workspace API keys.
- Visitors signed in this way have no Guuey/Portal account; their data
lives under the identity your issuer asserts and is erased through the
app’s per-user erase (
guuey apps byo-user erase).
Troubleshooting
Section titled “Troubleshooting”- The page shows no Sign in button. The app is not
byo, or no client is registered, or the Client ID does not equal the Audience, or the issuer is Guuey-owned.guuey apps getprints the current binding;GET /v1/agents/<appId>should carryoidcRelay: true. /startanswersAPP_NOT_RELAYABLE. Same causes as above — the server refuses what the page would not have offered.DISCOVERY_ISSUER_MISMATCH/PKCE_S256_UNSUPPORTED/*_BLOCKED. Your discovery document names a differentissuerthan the Issuer URL you configured (trailing slash aside), does not listS256, or names an endpoint on a non-https / private host.- The visitor lands back on
?guuey_auth_error=…. Your IdP declined (access_denied, a cancelled login) — the page offers to try again. ID_TOKEN_INVALIDat the exchange. The ID token failed the same verification the agent runs: wrong audience (client ≠ Audience), wrong issuer, expired, or unsigned by the keys at<issuer>/.well-known/jwks.json.ID_TOKEN_MISSING. Your token endpoint answered without anid_token— an OAuth-only server, oropenidwas not granted.