A minimal, framework-agnostic example showing how to add “Sign in with MyEQ” to any web app — OpenID Connect, Authorization Code flow with PKCE.
- Renders the branded Sign in with MyEQ button (themable, light/dark, several shapes & sizes).
- Runs the full OpenID Connect login: redirect → Authorization Code + PKCE → callback → tokens.
- Displays the signed-in user’s profile claims, with a Sign out action.
- Ships an interactive button customizer so you can preview every variant and copy the markup.
No backend, no client secret — it runs entirely in the browser and deploys as static files. Vanilla TypeScript on purpose: the same approach drops into React, Vue, Svelte or plain HTML.
MyEQ is a private identity provider — credentials are not self-service.
There is no public sign-up: your client_id, allowed scopes and redirect URIs
are provisioned by Europe Qualité administrators.
To connect your application, email support@europequalitegroup.com with:
- your application name and the origin(s) it will run from;
- the exact redirect URI(s) (e.g.
https://yourapp.example.com/callback.html); - the scopes you need (at minimum
openid; typicallyprofile,email).
You’ll receive a client_id (and, for confidential server-side clients, a
secret) to drop into your configuration. Redirect URIs are matched exactly
(no wildcards), so list every origin up front.
Prerequisite — a
client_idissued by Europe Qualité (see Getting access). Usehttp://localhost:5173/callback.htmlas a redirect URI for local development.
cp .env.example .env # then set your client_id / authority
npm install
npm run dev # http://localhost:5173Build the static site:
npm run build && npm run previewSet these in your .env (all prefixed with VITE_ so Vite exposes them):
| Variable | Description |
|---|---|
VITE_OIDC_AUTHORITY |
Issuer URL — https://auth.europequalitegroup.com |
VITE_OIDC_CLIENT_ID |
Your registered client_id |
VITE_OIDC_REDIRECT_URI |
Must match a registered redirect URI |
VITE_OIDC_SCOPE |
Space-separated scopes (openid is mandatory) |
Every endpoint is discovered from
${VITE_OIDC_AUTHORITY}/.well-known/openid-configuration.
1. The button — copy src/signin-button.css and the
markup below. It is plain HTML/CSS with no dependencies:
<button class="eq-signin eq-signin--light eq-signin--md eq-signin--rounded">
<span class="eq-signin__logo"><!-- inline EQ mark SVG --></span>
<span class="eq-signin__label">Sign in with MyEQ</span>
</button>2. The OIDC client — see src/auth.ts. The essentials:
import { UserManager, WebStorageStateStore } from 'oidc-client-ts'
const userManager = new UserManager({
authority: 'https://auth.europequalitegroup.com',
client_id: 'my-demo-rp',
redirect_uri: window.location.origin + '/callback.html',
response_type: 'code', // PKCE is enabled automatically
scope: 'openid profile email',
// Keep tokens in sessionStorage (cleared on tab close), not the default localStorage
userStore: new WebStorageStateStore({ store: window.sessionStorage }),
})
button.onclick = () => userManager.signinRedirect()On your redirect page, finish the flow (src/callback.ts):
await userManager.signinRedirectCallback()Per-stack, end-to-end guides (discovery, PKCE, login, tokens, refresh, logout):
| Stack | Guide |
|---|---|
Vue 3 (oidc-client-ts) |
guides/vue.md |
React / Next.js (react-oidc-context) |
guides/react.md |
| Node API as a resource server | guides/node-resource-server.md |
PHP (league/oauth2-client) |
guides/php.md |
| Mobile — iOS / Android / React Native (AppAuth) | guides/mobile.md |
See guides/ for the shared provider reference (endpoints, scopes, claims, token formats).
The button is driven entirely by CSS — combine class modifiers, and override the
--eq-signin-* custom properties to retheme it. Run the demo and use the
“Customize the button” panel to preview combinations and copy the markup.
| Group | Options |
|---|---|
| Theme | --light · --dark · --auto (follows the OS) |
| Size | --sm · --md · --lg |
| Shape | --rounded · --pill |
| Layout | --block (full width) · --icon (mark only, drop the label) |
/* Re-theme via custom properties */
.eq-signin {
--eq-signin-accent: #e42528; /* focus ring */
--eq-signin-radius: 10px; /* corner radius (rounded shape) */
}- PKCE (S256) is mandatory for every MyEQ client and handled automatically.
- Tokens are kept in
sessionStorage(cleared when the tab closes). Browser storage is readable by any script on the page, so for production or long-lived sessions prefer a server-side (confidential) client or a BFF with HttpOnly cookies. - The redirect callback only returns to same-origin paths (no open redirect).
- Always register exact redirect URIs and serve over HTTPS in production.
Both pages ship a strict CSP that locks scripts/styles/images to the same origin
and restricts connect-src to the MyEQ IdP — the primary containment against
XSS token exfiltration. Two delivery mechanisms, by design:
- In-document
<meta>(inindex.html/callback.html): a fallback for static hosts that can't set response headers (e.g. GitHub Pages). - Response headers (
public/_headers, copied todist/): the real mechanism — and the only one that delivers framing protection.frame-ancestors,X-Frame-Optionsand HSTS are ignored in a<meta>tag, so always serve them as headers in production. The_headersformat is read by Netlify and Cloudflare Pages; for nginx / Cloud Run set the equivalent headers in your server config.
If you point the app at your own IdP, add its origin to connect-src in both
the <meta> tags and public/_headers, otherwise discovery and token calls are
blocked. The dev server relaxes connect-src/style-src automatically for HMR
(see vite.config.ts); the production build stays strict.
index.html Home page: demo + button customizer
callback.html OAuth redirect page
src/
auth.ts UserManager configuration (adapt this)
callback.ts Redirect callback handler
main.ts Home page logic (render signed-in / signed-out)
playground.ts Interactive button customizer
signin-button.css Framework-agnostic button styles
styles.css Demo page styles
MIT © Europe Qualité
