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('/');
});
});