Next.js Example
Completely WIP
This page provides a step-by-step guide of how to integrate the Node.js SDK in a Next.js (>= 13.4) project. The guide is split into two sections to focus on the API route handlers and the front end pages.
API route handlers
Create the
/api/auth-url
route handlerCreate the
/api/callback
route handlerCreate the
/api/userinfo
route handler
Front end pages
Create a button to redirect to
/api/auth-url
Create a
/logged-in
page to fetch and render user info
Step 1.1: Setup the base Next app
In the directory you want to create your server, run
npx create-next-app@latest
then follow the prompts to customize your app.
To start the Next app, run
npm run dev
During integration with sgID, please start the server by running
npm run build
npm run start
This is because the in-memory store gets reset while loading a page/route for the first time - thus invalidating the browser's session. (This is due to hot module reloading (HMR) in the development environment)
Step 1.2: Initialize the SDK
After setting up the base Next 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>
Initialize the SDK by calling the constructor and providing your client credentials.
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 1.3: Create the /api/auth-url route handler
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";
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 1.4: Create the /api/callback route handler
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);
if (!memoryObject) {
res.status(400).send("No authorization request was made before");
}
const { codeVerifier } = memoryObject;
// Exchange the auth code for the access token
const { accessToken, sub } = await sgidClient.callback({
code,
codeVerifier
});
// Store the access token in memory
const newMemoryObject = { 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 1.5: Create the /api/userinfo route handler
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);
if (!memoryObject) {
res.status(400).send("No access token found with the associated session ID");
}
const { accessToken } = memoryObject;
// Request the userinfo using the access token
const data = await sgidClient.userinfo({ accessToken });
// Return the user info
res.json(data);
});
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