Skip to content
Early preview — you found Guuey before launch · official launch soon

Sign-in on the agent's page

read as .md

An 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.

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, openid scope) 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.

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/complete

The 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.

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.

Terminal window
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-client

Or 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.

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.

  1. Start — the page sends the visitor to /v1/auth/relay/start?appId=…&returnTo=<its own URL>. Guuey checks the returnTo against 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 its authorization_endpoint with response_type=code, scope=openid, PKCE (S256), a state and a nonce.
  2. 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.
  3. 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’s nonce, and returns it to the page. The refresh token — if your IdP sent one — is discarded.
  4. 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.

  • The issuer must publish /.well-known/openid-configuration whose issuer is your configured Issuer URL, with https endpoints on public hosts. Providers that advertise PKCE methods must include S256.
  • 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).
  • 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 get prints the current binding; GET /v1/agents/<appId> should carry oidcRelay: true.
  • /start answers APP_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 different issuer than the Issuer URL you configured (trailing slash aside), does not list S256, 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_INVALID at 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 an id_token — an OAuth-only server, or openid was not granted.