Express Example
TODO: Standardize with Anty's Express example app once PKCE has been rolled out To add: front-end to obtain authorize url from /api/auth-url and 302 happens at browser
redirect url to /api/callback as this bypass front end logic. Browser will make the call with cookies
explain everytime front-end needs userinfo, can call userinfo
This page provides a step-by-step guide of how to integrate the Node.js SDK in a simple Express server. This Express app can be used as a
back-end server for your front-end (e.g. with React); or a
standalone web server using MVC (e.g. with Embedded JavaScript templating EJS).
The steps to integrate the Node.js SDK are as follows
Integrate the frontend and backend
Step 1: Setup the base Express app
In the directory you want to create your server, run
npm init -y
Follow the relevant steps based on whether you are creating a TypeScript or JavaScript project.
A new package.json
file should have been created. Open the file and
Add a new script
"dev": "nodemon index.ts"
under"scripts"
- this will be the script we use to start our development server
Next, install the relevant dependencies, including the sgID Node.js SDK.
npm i @opengovsg/sgid-client express uuid cookie-parser node-cache ts-node dotenv
npm i -D nodemon typescript @types/node @types/express @types/cookie-parser @types/uuid
Then, create a new index.ts
file and setup the base Express app as follows
import express, { Express } from "express";
import cookieParser from "cookier-parser";
import dotenv from "dotenv";
dotenv.config();
const app: Express = express();
app.use(cookieParser()); // used to interact with the browser's cookies
app.get("/", (req, res) => {
res.send("Hello from sgID");
});
app.listen(3000, () => {
console.log(`⚡️[server]: Server is running at http://localhost:3000`);
});
To start the Express server, run
npm run dev
Step 2: Initialize the SDK
After setting up the base Express app, you are ready to start integrating with sgID.
Firstly, create a .env
file, copy the following into the file, and fill out your sgID credentials.
// Replace the values below with your own client credentials
SGID_CLIENT_ID=<your client id>
SGID_CLIENT_SECRET=<your client secret>
SGID_PRIVATE_KEY=<your private key>
Next, initialize the SDK by calling the constructor and passing in the environment variables.
import { SgidClient } from '@opengovsg/sgid-client'
const sgidClient = new SgidClient({
clientId: String(process.env.SGID_CLIENT_ID),
clientSecret: String(process.env.SGID_CLIENT_SECRET),
privateKey: String(process.env.SGID_PRIVATE_KEY),
redirectUri: 'http://localhost:3000/api/callback',
})
Step 3: Create the /api/auth-url endpoint
When an end user clicks on the sign in button on your application (e.g. 'Login with Singpass app'), it should redirect the browser to this endpoint.

The /api/auth-url
endpoint should do the following
Generate a session ID
Generate a PKCE pair (consisting of code challenge and code verifier)
Store the code verifier in an in-memory cache with the session ID as the key
Generate an authorization URL
Set the session ID in the browser's cookies
Redirect the browser to the authorization URL
import NodeCache from "node-cache";
import { v4 as uuidv4 } from "uuid";
const nodeCache = new NodeCache();
app.get("/api/auth-url", (req, res) => {
// Generate a session ID
const sessionId = uuidv4();
// Generate a PKCE pair
const { codeVerifier, codeChallenge } = generatePkcePair();
// Store the code verifier in memory
const memoryObject = { codeVerifier };
nodeCache.set(sessionId, memoryObject);
// Generate an authorization URL
const { url } = sgidClient.authorizationUrl({
codeChallenge: codeChallenge
});
// Set the session ID in the browser's cookies
res.cookie("sessionId", sessionId);
// Redirect to the authorization URL (i.e. QR code page)
res.redirect(url);
});
Step 4: Create the /api/callback endpoint
After the user scans the QR code with their Singpass mobile app and authorizes your application to access the specified scopes, the sgID server will redirect the user's browser to the redirect_uri
you specified earlier (either when initializing the SDK or when passed as a parameter to the authorizationUrl
function).
The redirect will include the authorization code and the state (if provided earlier) in the form of query parameters. An example URL would look something like this
http://localhost:3000/api/callback?
code=someAuthCode
&state=somestate
The /api/callback
endpoint should do the following
Retrieve the authorization code from query params, and the session ID from browser cookies
Retrieve the code verifier from memory using the session ID
Exchange the authorization code and code verifier for the access token
Store the access token in memory
Redirect the browser to a logged in page (or any page of your choice)
app.get("/api/callback", async (req, res) => {
// Retrieve the authorization code from the query params
const { code } = req.query;
// Retrieve the session ID from the browser's cookies
const { sessionId } = req.cookies;
// Retrieve the code verifier from memory using the session ID
const memoryObject = nodeCache.take(sessionId);
// Exchange the auth code for the access token
const { accessToken, sub } = await sgidClient.callback({
code,
codeVerifier: memoryObject.codeVerifier
});
// Store the access token in memory
const newMemoryObject = { ...memoryObject, accessToken };
nodeCache.set(sessionId, newMemoryObject);
// Redirect the browser to a logged in page instead
// - if your Express server is a back-end for your front-end
res.redirect("https://yourSPAFrontEnd.com/logged-in");
// - OR if your Express server is a web server serving the HTML pages
res.redirect("http://localhost:3000/logged-in");
});
Step 5: Create the /api/userinfo endpoint
Once the browser has been redirected to a logged in page, your are able to use the access token stored in memory to request user info from the sgID server through this endpoint.
The /api/userinfo
endpoint should do the following
Retrieve the session ID from browser cookies
Retrieve the access token from memory using the session ID
Request user info using the access token
Return the user info
app.get("/api/userinfo", async (req, res) => {
// Retrieve the session ID from the browser's cookies
const { sessionId } = req.cookies;
// Retrieve the access token from memory using the session ID
const memoryObject = nodeCache.take(sessionId);
// Request the userinfo using the access token
const data = await sgidClient.userinfo({
accessToken: memoryObject.accessToken
});
// Return the user info
res.json(data);
});
Step 6: Integrate the frontend and backend
Now that your backend is setup properly, you will need to wire your frontend application to the backend server. As part of this example, we will be using this frontend repo built with React (you can use any framework to build your own frontend).
Firstly, clone the repository by running
git clone https://github.com/opengovsg/sgid-demo-frontend.git
Congratulations! 🎉
Your application is now capable of using sgID as an authentication and authorization service. To test out your local Express server as a back-end for our SPA front-end, please refer to the development environment page.
Source code
Last updated