Widget Authentication Deployment
The embed guide covers both host-key and keyless embeds. This guide covers deployment of the existing widget sign-in implementation from PR #275. Do not publish OAuth secrets, Apple private keys, or SMTP configuration in browser bundles or committed files.
Account Admission
Choose WIDGET_SIGNUP_POLICY before enabling public sign-in:
| Value | Behavior |
|---|---|
existing (default) | Only an existing active account matched by verified email can sign in. |
allowlist | New accounts may be provisioned only for exact domains in comma-separated WIDGET_SIGNUP_DOMAINS. Subdomains are not implicitly allowed. |
open | Any verified email can provision an account and its own parent key. Requires explicit operator approval. |
Unknown values fail closed. Existing inactive accounts are rejected under all
policies. These rules apply to Google, Apple, and email OTP, not to existing
host-configured keys. Apple private-relay addresses will not match a corporate
domain allowlist; decide whether users must share their actual email or be
pre-provisioned under the existing policy.
Sessions last up to 24 hours, are held in process memory, and are invalidated by sign-out or a server restart. Multi-instance deployments need shared session and OIDC state before they can reliably distribute authentication requests. This work does not introduce chat-history or memory persistence.
Organization-Owned Google Credentials
- Create a Web application OAuth client in an organization-owned Google Cloud project.
- Configure the consent screen, audience, and any required publishing verification.
- Set
PUBLIC_BASE_URLto the exact public API origin, using HTTPS in production. - Register
PUBLIC_BASE_URL/auth/oidc/google/callbackunder Authorized redirect URIs, not JavaScript origins. - Set
GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETthrough the deployment secret manager. Remove the former personal-project credentials from the deployment. - Verify
/auth/methodsreportsgoogle: true, then complete sign-in from an allowed account in the embedded widget.
Replacing credentials in Google Cloud and the deployed secret store is an operator action; the repository cannot create organization ownership or verify it from a client ID. Keep the previous client until the new deployment has been verified, then revoke it according to your credential-rotation policy.
Apple Sign-In
Configure an organization-owned Apple Developer App ID with Sign in with Apple,
and a linked Services ID for the web client. Register the API domain and the exact
HTTPS return URL PUBLIC_BASE_URL/auth/oidc/apple/callback.
Set APPLE_CLIENT_ID (Services ID), APPLE_TEAM_ID, APPLE_KEY_ID, and
APPLE_PRIVATE_KEY (PKCS8 .p8 content, with real or escaped newlines) in the
secret manager. The server signs a fresh five-minute ES256 client-secret JWT for
each code exchange, so a manually generated client-secret JWT cannot expire unnoticed.
Rotate the signing key through Apple and update the secret store when needed.
Apple posts authorization results to the callback using form_post. Ensure the
proxy allows that POST with its form body. The server verifies the ID token's
signature, issuer, audience, expiry, nonce, subject, and verified email. Only signed
identity claims are used; Apple's first-login-only user profile is not trusted or
required, so subsequent logins work without it. Display names default to the
verified email rather than collecting the optional first-login name.
Email Delivery
Inside the Phoenix DC, configure:
SMTP_URL=smtp://relay.cluster.mieweb.org:25
SMTP_FROM=no-reply@os.mieweb.org
NODE_ENV=production
This relay requires an @os.mieweb.org sender, no credentials, and no TLS/STARTTLS.
The current transport is specifically configured for that trusted internal relay;
do not use it for an external authenticated/TLS mail service without adapting the
transport. Connection and socket waits are bounded to ten seconds.
Production without SMTP disables email sign-in. In local development only, missing
SMTP logs the code; AUTH_DEV_ECHO_OTP=1 may also return it in the response. Neither
mechanism bypasses delivery in production or when SMTP is configured.
OTP delivery attempts are limited to three per email and 100 total per server process in a rolling fifteen-minute window. Failed deliveries also count. These in-memory limits reset on restart; use shared ingress rate limiting across replicas. The total cap bounds mail volume, but can temporarily block legitimate sign-ins when exhausted, so monitor throttling in production.
Real Relay Acceptance Check
Perform this inside the deployed environment with an approved test mailbox:
- Open the keyless widget and request an email code.
- Confirm delivery to the real inbox, including sender and expiry text. A successful SMTP connection alone is not proof of delivery.
- Confirm the request returns a challenge ID, not the code, and no code appears in application logs.
- Enter the code and verify an authenticated chat request succeeds.
- Retry the same code and confirm rejection; sign out and confirm the old session no longer authorizes requests.
- Record deployment/version, timestamp, and pass/fail evidence without recording codes, keys, or session tokens.
Local automated tests use a controlled SMTP server and cannot certify delivery
through the real Phoenix relay. For manual local delivery testing, run
node scripts/dev/smtp-sink.js 2525 and set SMTP_URL=smtp://127.0.0.1:2525.
That sink prints local test mail; never use it with production credentials or data.
Verification Before Rollout
Widget sessions authorize only POST /v1/chat/completions and
GET /v1/models/effective, plus the session inspection/logout auth routes.
They do not grant agent-management or key-management access.
Google and Apple starts share a limit of ten requests per client IP per
fifteen-minute window. Forwarded IP headers are ignored by default. Behind a
reverse proxy, set TRUSTED_PROXY_CIDRS to a comma-separated list of that proxy's
addresses or CIDRs, and ensure the proxy sanitizes forwarded headers. Do not trust
arbitrary public proxies. Shared networks share a client limit. Multi-replica
deployments also need shared ingress rate limiting; process-local caps alone do
not prevent distributed denial of service.
From reference-server, run:
npm run build
node --test test/widget-auth.test.js
For isolated browser auth regression tests, run from landing-page after building
the widget:
npx playwright test tests/widget-auth.spec.ts
These tests mock auth responses and do not require OAuth credentials or SMTP.
Verify configured agent and parent keys bypass the modal. For keyless embeds, exercise Google, Apple (first and repeat login), email OTP, and user-entered keys. Test rejected accounts, popup cancellation, sign-out, and explicit remember/forget behavior. Check desktop and mobile layouts. Keep provider keys and OAuth secrets server-side. Confirm the chat backend is available; successful sign-in alone does not demonstrate that a model can answer or execute a page tool.
Release gates requiring operator evidence: organization-owned Google and Apple credentials, approved signup policy, real relay delivery, and live-provider browser sign-in. Do not mark these complete based solely on local mocked-provider tests.