WamISSO Documentation

JavaScript Integration

Integrate WamISSO into a Node.js application using Express and the native fetch API.

Before you start: Complete developer registration and create an OAuth client in the developer portal to get your client_id and client_secret.

Install dependencies

npm install express express-session dotenv

Environment variables (.env)

SSO_BASE_URL=https://wamisso.rendovations.com
SSO_CLIENT_ID=your-client-id
SSO_CLIENT_SECRET=your-client-secret
SSO_REDIRECT_URI=http://localhost:3000/callback
SESSION_SECRET=change-me-to-a-long-random-string

Complete Express example

import 'dotenv/config';
import crypto from 'node:crypto';
import express from 'express';
import session from 'express-session';

const app = express();
const {
  SSO_BASE_URL,
  SSO_CLIENT_ID,
  SSO_CLIENT_SECRET,
  SSO_REDIRECT_URI,
  SESSION_SECRET,
} = process.env;

app.use(session({
  secret: SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
}));

app.get('/login', (req, res) => {
  const state = crypto.randomBytes(16).toString('hex');
  req.session.oauthState = state;

  const params = new URLSearchParams({
    client_id: SSO_CLIENT_ID,
    redirect_uri: SSO_REDIRECT_URI,
    response_type: 'code',
    scope: 'openid profile email',
    state,
  });

  res.redirect(`${SSO_BASE_URL}/oauth/authorize?${params}`);
});

app.get('/callback', async (req, res) => {
  const { code, state } = req.query;

  if (!code || state !== req.session.oauthState) {
    return res.status(403).send('Invalid OAuth callback');
  }

  delete req.session.oauthState;

  const tokenRes = await fetch(`${SSO_BASE_URL}/oauth/token`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: SSO_CLIENT_ID,
      client_secret: SSO_CLIENT_SECRET,
      redirect_uri: SSO_REDIRECT_URI,
      code: String(code),
    }),
  });

  if (!tokenRes.ok) {
    return res.status(502).json(await tokenRes.json());
  }

  const tokens = await tokenRes.json();

  const userRes = await fetch(`${SSO_BASE_URL}/api/user`, {
    headers: {
      Accept: 'application/json',
      Authorization: `Bearer ${tokens.access_token}`,
    },
  });

  if (!userRes.ok) {
    return res.status(502).json(await userRes.json());
  }

  req.session.user = await userRes.json();
  req.session.accessToken = tokens.access_token;
  res.json(req.session.user);
});

app.get('/me', (req, res) => {
  res.json(req.session.user ?? {});
});

app.listen(3000, () => {
  console.log('App running on http://localhost:3000');
  console.log('Start SSO at http://localhost:3000/login');
});

Browser SPA note

Do not expose client_secret in frontend code. For single-page apps, use a backend-for-frontend (BFF) to exchange the authorization code, or register a public client with PKCE if your SSO server supports it.

Refresh token

async function refreshAccessToken(refreshToken) {
  const response = await fetch(`${SSO_BASE_URL}/oauth/token`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: SSO_CLIENT_ID,
      client_secret: SSO_CLIENT_SECRET,
    }),
  });

  if (!response.ok) throw new Error('Token refresh failed');
  return response.json();
}

Run locally

# Register http://localhost:3000/callback as a redirect URI first
node server.js

# Open http://localhost:3000/login

Logout

app.post('/logout', async (req, res) => {
  const accessToken = req.session.accessToken;

  if (accessToken) {
    try {
      await fetch(`${SSO_BASE_URL}/api/sso/logout`, {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          Authorization: `Bearer ${accessToken}`,
        },
      });
    } catch {
      // still clear local session
    }
  }

  req.session.destroy(() => {
    res.redirect('/');
  });
});