Skip to content
This repository has been archived by the owner on Dec 2, 2022. It is now read-only.
/ spcp-auth-client Public archive

Integrates SingPass and CorpPass SAML into your node.js application

License

Notifications You must be signed in to change notification settings

opengovsg/spcp-auth-client

Repository files navigation

DEPRECATED: PLEASE READ

Singpass and Corppass no longer support SAML as of December 2022. Please use @govtechsg/singpass-myinfo-oidc-helper to connect via OIDC instead.

This package has been deprecated and is no longer maintained.

spcp-auth-client

A node.js library for SingPass and CorpPass, common authentication methods for public-facing government systems in Singapore

Quick Start

const SPCPAuthClient = require('@opengovsg/spcp-auth-client')

const client = new SPCPAuthClient({
  partnerEntityId: '<your partner entity id>',
  idpLoginURL: '<the SingPass/CorpPass IDP url to redirect login attempts to>',
  idpEndpoint: '<the SingPass/CorpPass IDP url for out-of-band (OOB) authentication>',
  esrvcID: '<the e-service identifier registered with SingPass/CorpPass>',
  appCert: '<the e-service public certificate issued to SingPass/CorpPass>',
  appKey: '<the e-service certificate private key>',
  appEncryptionKey: '<optional: the e-service certificate private key used for decrypting artifact response, if different from appKey>',
  spcpCert: '<the public certificate of SingPass/CorpPass, for OOB authentication>',
  extract: '<custom fn or SPCPAuthClient.extract.CORPPASS or SPCPAuthClient.extract.SINGPASS (default)>',
})

const express = require('express')

const POST_LOGIN_PAGE = '/<target-url-after-login>'

const app = express()

// If a user is logging in, redirect to SingPass/CorpPass
app.route('/login', (req, res) => {
  const redirectURL = client.createRedirectURL(POST_LOGIN_PAGE)
  res.status(200).send({ redirectURL })
})

// SingPass/CorpPass would eventually pass control back
// by GET-ing a pre-agreed endpoint, proceed to obtain the user's
// identity using out-of-band (OOB) authentication
app.route('/assert', (req, res) => {
  const { SAMLart: samlArt, RelayState: relayState } = req.query
  client.getAttributes(samlArt, relayState, (err, data) => {
    if (err) {
      // Indicate through cookies or headers that an error has occurred
      console.error(err)
      res.cookie('login.error', err.message)
    } else {
      // If all is well and login occurs, the attributes are given
      // In all cases, the relayState as provided in getAttributes() is given
      const { attributes, relayState } = data

      // For SingPass, a user `name will be given
      // Refer to unit tests to infer what CorpPass will give
      const { UserName: userName } = attributes
      // Embed a session cookie or pass back some Authorization bearer token
      const FOUR_HOURS = 4 * 60 * 60 * 1000
      const jwt = client.createJWT({ userName }, FOUR_HOURS)
      res.cookie('connect.sid', jwt)
    }
    res.redirect(relayState)
  })
})

// Verify if session has been authenticated with our JWT
const isAuthenticated = (req, res, next) => {
  client.verifyJWT(req.cookies['connect.sid'], (err, data) => {
    if (err) {
      res.status(400).send('Unauthorized')
    } else {
      req.userName = data.userName
      next()
    }
  })
}
app.route(
  '/protected-route',
  isAuthenticated,
  // ...
)

About SingPass/CorpPass and this package

SingPass and CorpPass are identity providers to provide a single set of login credentials for Singapore residents and Singapore-based corporate entities respectively. They are both based on SAML 2.0, and interact with service providers through HTTP Artifact Binding. The artifact returned by the identity provider is a SAML Assertion consisting of attributes concerning the user.

What the attributes actually are depends on the identity provider:

  • SingPass will return the user's NRIC as the UserName attribute (this is even if the user has a non-NRIC login id)
  • CorpPass will return an attribute whose name is the UEN of the corporate entity, and whose value is a base64-encoded payload of an XML document whose structure is defined in Section 4.4.3 of the CorpPass Interface Specification v1.5

This package is a very lightweight implementation of the above, written after failing to find an npm package that supports artifact binding. It is meant for those who are solely focused on using SingPass or CorpPass as a sign-in mechanism, solely to retrieve the contents of the SAML AttributeStatement, without being too concerned about other SAML 2.0 features.

More full-fledged SAML 2.0 implementations for node.js include:

  • @socialtables/saml-protocol (GitHub, npm) - a port of the Java-based Spring-Security-SAML
  • saml2-js (GitHub, npm) - CoffeeScript implementation from Clever

Note that these do not have HTTP Artifact Binding at time of writing, but would probably accept pull requests

Contributing

We welcome contributions to code open-sourced by the Government Technology Agency of Singapore. All contributors will be asked to sign a Contributor License Agreement (CLA) in order to ensure that everybody is free to use their contributions.