Skip to content
FidmosDocumentation

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.

  • In the browser: window.FidmosID.login(...) from the widget. The window starts the flow itself and returns { code, state, codeVerifier } in onSuccess, and the page passes them to this backend.
  • On the backend (this code): the code-for-token exchange and id_token verification. This is where client_secret lives; the backend gets code_verifier from the browser and sends it to /oauth/token.
npm install openid-client express

You need Node.js 20+ (the module uses the built-in fetch and ESM).

// 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
// 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 };
}
// 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);
  • 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.