Backend example (Node.js)
A complete working backend for the embedded window: it exchanges the code that came from the
browser for a token and verifies the id_token. A step-by-step breakdown is in the
quick start; here is one ready module you can copy and run.
What runs where
Section titled “What runs where”- In the browser:
window.FidmosID.login(...)from the widget. The window starts the flow itself and returns{ code, state, codeVerifier }inonSuccess, and the page passes them to this backend. - On the backend (this code): the code-for-token exchange and
id_tokenverification. This is whereclient_secretlives; the backend getscode_verifierfrom the browser and sends it to/oauth/token.
Install
Section titled “Install”npm install openid-client express
You need Node.js 20+ (the module uses the built-in fetch and ESM).
Configuration
Section titled “Configuration”// config.js
export const ISSUER = 'https://id.fidmos.com';
export const CLIENT_ID = process.env.FIDMOS_CLIENT_ID;
export const CLIENT_SECRET = process.env.FIDMOS_CLIENT_SECRET; // backend only!
export const REDIRECT_URI = 'https://partner.example/callback'; // the same one registered
Sign-in module
Section titled “Sign-in module”// fidmos-id.js
import * as client from 'openid-client';
import { ISSUER, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI } from './config.js';
// Do discovery once at startup: endpoint addresses and verification keys are taken
// from /.well-known/openid-configuration, not hardcoded.
const config = await client.discovery(new URL(ISSUER), CLIENT_ID, CLIENT_SECRET);
// Finish sign-in: exchange the code for a token and verify id_token.
// code and codeVerifier came from the widget's onSuccess via your page.
export async function finishLogin({ code, codeVerifier }) {
// Put only the code in the callback URL — openid-client reads the response params from it.
const callbackUrl = new URL(REDIRECT_URI);
callbackUrl.searchParams.set('code', code);
// Code-for-token exchange + id_token verification: signature by JWKS from discovery,
// iss = issuer, aud = our client_id, exp lifetime. PKCE is confirmed by the verifier from the browser.
const tokens = await client.authorizationCodeGrant(config, callbackUrl, {
pkceCodeVerifier: codeVerifier,
idTokenExpected: true,
});
// sub is the pairwise user identifier: stable for your client.
const { sub, email, email_verified: emailVerified } = tokens.claims();
return { sub, email, emailVerified, accessToken: tokens.access_token };
}
Wiring it up (Express)
Section titled “Wiring it up (Express)”// server.js
import express from 'express';
import { finishLogin } from './fidmos-id.js';
const app = express();
app.use(express.json());
// the widget's onSuccess sent { code, state, codeVerifier }; the page passes them here.
app.post('/login/callback', async (req, res) => {
try {
const { sub } = await finishLogin(req.body);
// Identify the user by sub and start your own session (express-session, a JWT cookie, etc.).
res.json({ ok: true, sub });
} catch (err) {
res.status(400).json({ ok: false });
}
});
app.listen(3000);
What’s next
Section titled “What’s next”- Fetch the name and avatar — user profile (the same claims are in the
already-verified
id_token). - The full widget contract — login widget.
- Classic OAuth2 without the widget — redirect login.