feat(auth): Ever ID token route answers email_unverified; "Login with Ever ID" button
PR
- Have you followed the contributing guidelines?
- Have you explained what your changes do, and why they add value?
⚠️ develop → stage only. This changes the shared gauzy-api image (also the Ever Teams API). Production
(main/master, app.gauzy.co / api.gauzy.co / api.ever.team) is the owner's call; Ever ID stays off in Gauzy prod.
Why
On 2026-10-17 at 20:27Z / 20:29Z a person who had just created their Ever ID tried "Ever ID" on app.ever.team and got
AccessDenied. The API (POST /api/auth/zitadel/token) answered a bare 401. Checked: the token's audience was right
(the Teams web client is the only ZITADEL_ALLOWED_AUDIENCES entry); the Ever ID account was minutes old with an
unverified e-mail, and ZitadelSigninService.decide() returns email_unverified first. The token route answered
that with the same bare 401 it uses for a forged token, and logged nothing above debug, so neither the client nor the
operator could tell why.
What
Token route (packages/plugins/auth-zitadel)
- A valid ID token whose e-mail is not verified →
401 { code: 'email_unverified' }. Every refusal of the token itself
(audience, issuer, signature, expiry) stays a bare 401, so the route is no oracle for token validity; the
email_verifiedclaim is in the token the caller already holds. - The OIDC error code of a refused token is logged at
warn(wasdebug). Codes only, never the token. - Ever Teams already handles both answers (ever-co/ever-teams#4645 shows "Verify your Ever ID e-mail"); it does not
depend on this change.
"Login with Ever ID" button (packages/ui-auth, ngx-social-links: login, magic-login, register)
- Per the Ever brand kit (
ever-co/platformpackages/brand, PR #37): the kit's rotating star (one turn every 9 s,
no spin withprefers-reduced-motion), "Login with", and the kit's "ever® ID" wordmark. The form's full width,
40 px (the login/register primary button is ~40 px too), radius 8, padding 0 16px, 14/20 px at 500. Light themes:
#0A0A0A/ white text (hover#262626); dark themes (gauzy-dark, dark, cosmic, material-dark):#EDEDED/#0A0A0A
(hover#FFF); focus ring 2 px (#231645/#CC66FF). Same button as Ever Teams (ever-co/ever-teams#4645). - New
LOGIN_PAGE.LOGIN_WITH_EVER_ID(Login with {{ everId }}) in 13 languages; the label component splits it at the
wordmark so word order is kept (German "Mit Ever ID anmelden").SIGN_IN_WITH_EVER_IDstays.
Tests
zitadel-token.e2e.spec.ts: the unverified case now expectscode: 'email_unverified'; the other refusals expect no
code; a token for another audience withemail_verified: falsestill gets a bare 401.social-links.component.spec.ts: word-order split for the new label, fallback when the translation is missing.- ⚠️ Not run locally (no Gauzy install on this workstation) and Gauzy CI does not run jest: please run
yarn nx test plugin-auth-zitadel/yarn nx test ui-author rely on the reviewer. The code change is small
(exception body, log level, a template branch).
Verify on stage
app-stage / apistage (Ever ID on there): login and register pages show the full-width "Login with Ever ID" button in
light and dark; an Ever ID with an unverified e-mail gets 401 {"code":"email_unverified"} from
POST /api/auth/zitadel/token.
🤖 Generated with Claude Code