Sign in with any OpenID Connect issuer (SSO) on the Gateway
Step-by-step guide: Continue with SSO on the Gateway's sign-in page from any OpenID Connect issuer (Okta, Entra, Google Workspace, Authentik…), everyapp signin add oidc, the check.
Puts Continue with SSO (or a label the person picks, such as
Continue with Okta) on the Gateway's sign-in page, beside the password,
through the company's identity provider: Okta, Keycloak, Auth0, Authentik,
Google Workspace as an issuer, and the like. At the end the person has a
web-application client at their identity provider, its settings on the
Gateway, and has signed in through it once.
pnpm everyapp signin show oidc --json names this guide as setup, and the
CLI prints Guide: https://everyapp.com/docs/agent/setup/signin-oidc.md.
Who gets in doesn't change: the issuer only says who someone is, and only
an ID token with email_verified: true counts. A member signs in;
someone with a pending invitation for that exact address joins with the
invited role; everyone else is refused ("You don't have an account on this
gateway.") and nothing is created. The rules:
docs/runbooks/sign-in-providers.md, "Who gets in".
Not this guide: Cloudflare Access for SaaS (also an OIDC issuer, but with
its own CLOUDFLARE_ACCESS_OIDC_* settings; access.md and
docs/runbooks/cloudflare-access.md). Microsoft Entra ID: prefer
signin-microsoft.md, which handles Entra's
verified-email rules; Entra's ID tokens carry no email_verified claim
(not among its optional claims), so here every sign-in would be refused.
the issuer URL, https://…, which serves /.well-known/openid-configuration
OIDC_SIGNIN_CLIENT_ID
required
--client-id <id>
the client's ID; not a secret
OIDC_SIGNIN_CLIENT_SECRET
required
--client-secret-from-env <VAR>
the client's secret
OIDC_SIGNIN_LABEL
optional
--label <text>
the button text after "Continue with" (default SSO; 40 characters at most)
Scopes asked for: openid email profile. The email is the ID token's,
else the userinfo endpoint's (for the same sub only), and counts only
with email_verified: true. ID tokens must be signed with RS256 (every
major issuer's default) and carry the sign-in's nonce.
Whatever the identity provider charges; the Gateway adds nothing. Most
include OIDC web clients on every plan (Okta, Auth0 and Authentik plans
not checked here: unverified).
pnpm everyapp status --json: data.installed and data.claimed true;
data.gatewayUrl gives <gateway host>. Not installed or no owner:
gateway.md first. data.access.on true: see Verify 4.
pnpm everyapp signin show oidc --json (changes nothing; Unknown command signin: git pull in the every-app folder). data.statusoff: this guide applies; needs setup: data.missing says what is
left (step 4); on: ask before replacing. data.callbackUrl is
https://<gateway host>/api/auth/callback/oidc.
Ask which identity provider, and the label they want on the button.
Person: in the identity provider's admin console, create an OpenID
Connect client: type web application (confidential, with a secret),
grant authorization code, sign-in redirect URI the exact URL from
step 1, scopes openid email profile. Assign it to the people or groups
who should sign in (Okta and Authentik refuse unassigned users). Note
the issuer URL the console shows. Usual forms (check each with step 3):
Okta https://<org>.okta.com or https://<org>.okta.com/oauth2/default;
Keycloak https://<host>/realms/<realm>; Auth0
https://<tenant>.<region>.auth0.com/ (with the slash); Authentik
https://<host>/application/o/<slug>/; Google Workspace
https://accounts.google.com (a Web application client as in
signin-google.md, Internal audience). The page
shows the client ID and secret; keep it open.
You: check the issuer, nothing secret in it:
curl -s https://<issuer host and path>/.well-known/openid-configuration | jq -r .issuer
(an issuer ending in /: drop that slash before /.well-known). The
answer must equal the issuer URL exactly, trailing slash included; that
exact string is --issuer. HTML, nothing, or another string: the
wrong URL (Okta's org server and its default server are two issuers).
Person: in a second terminal in the every-app folder:
pnpm everyapp signin add oidc
It asks for the issuer and the client ID (shown as typed), the client
secret (hidden: pasting works, nothing is echoed) and the label (shown
as typed; Enter leaves the default, SSO), and writes them in one new Gateway Worker
version, live on the next request. They tell you when it's done.
Another way: in that terminal, the hidden read (bash or zsh:
read -rs OIDC_SIGNIN_CLIENT_SECRET && export OIDC_SIGNIN_CLIENT_SECRET;
fish: read -s -x OIDC_SIGNIN_CLIENT_SECRET), paste, Enter, then
pnpm everyapp signin add oidc --issuer <issuer> --client-id <client ID> --label Okta --client-secret-from-env OIDC_SIGNIN_CLIENT_SECRET --json
and unset OIDC_SIGNIN_CLIENT_SECRET (fish: set -e OIDC_SIGNIN_CLIENT_SECRET).
pnpm everyapp signin list --json: the oidc entry has status: "on"
and missing empty.
The person's address at the identity provider must be a member's or
have a pending invitation: pnpm everyapp workspace invite <email>.
Person: private window, https://<gateway host>/sign-in →
Continue with <label> → the identity provider's login → the
Gateway, signed in. The issuer's discovery document is fetched at this
moment (and cached for an hour), not when the Gateway starts.
Behind Cloudflare Access: nothing else changes, but the identity
provider's address must be the one Access knows the person by.
Logs (the setup rules' Verify 4): signin.config_problem names a malformed
setting (an issuer that isn't an https URL, a label too long).
Rerun: pnpm everyapp signin add oidc any time; values given replace,
settings not given are kept (Enter at a prompt keeps it).
Rotate: a new secret at the provider, then pnpm everyapp signin add oidc
with Enter at the other prompts, or the hidden read and
pnpm everyapp signin add oidc --client-secret-from-env OIDC_SIGNIN_CLIENT_SECRET --json.
Then revoke the old secret there.
Back to the default label: pnpm everyapp secret unset OIDC_SIGNIN_LABEL --json
(--yes only when the person said so).
Remove, only when asked: pnpm everyapp signin remove oidc --json --yes
(--yes only when they said so). Accounts made with it stay. Then
delete the client at the provider.