Integrating Singpass Login API with Laravel Socialite Provider — Part 2 Writing Socialite Provider

Introduce to OpenID Connect

The authentication architecture behind Singpass Login API which is OpenID connect (OIDC), which allow end to end encryption between Singpass Login API server and Relying Party server. In addition, OIDC provides a way to perform signature signing and token encryption to ensure its security and integrity.

JOSE — Javascript Object Signing and Encryption

Things to know for the latest Singpass Login API

  1. Client Secret is no longer required.
    As you might wonder, you only get the client id for Singpass Login API, how about the client secret, and how can I authenticate with Singpass Login API without a client secret? Started from this year onwards, Singpass Login API required relying party to generate a client assertions basically a JWT digital signature that contains the Headers + Payload + Signature. As previous we have setup a JWKS endpoint, Singpass Login API server will retrieve the verification key (public key) from your JWKS endpoint and perform a signature checking against your provided client assertion. This is due to we the signature can only be produced by signing key (private key) which is stored at server side.
  2. The OpenID Configuration Endpoint
    This endpoint which contains the metadata of the open id configuration including those token url, Singpass JWKS endpoint, type of supported cryptographic algorithm supported for signature signing and token decryption. You should cache this configuration in your Laravel for at least one hour instead of keep sending request when loading the openID configuration, it will result a rate limit issues and Singpass server will temporarily blocked you out. In addition, you should always load those token url and Singpass JWKS endpoint from this configuration JSON instead of hard code it in the Laravel Socialite Provider class, this is due to the when OpenID configuration your socialite will always get the latest OpenID configuration.
  3. The Singpass JWKS endpoint
    This endpoint which contains the Singpass signature verification key, this will be used to verify the decrypted id_token’s signature to ensure that the token payload data is not tampered by anyone. We will cover this later, of how to use this endpoint to perform signature checking against the decrypted id token.

Writing Laravel Socialite Provider

Before get started you need to install laravel/socialite into your Laravel project

composer require laravel/socialite

Create a SingpassProvider class
In your app directory create a socialite folder with SingPassProvider.php in it.

And create a class as SingPassProvider and extend the AbstractProvider class and implements the ProviderInterface contracts make sure that all the methods define in the ProviderInterface must be implemented as the code below

Writing Utility Function
Previously we have describe the used of openID configuration, and JWKS endpoint. Right now we need to write two methods which is getOpenIDConfiguration and retrieveSingPassVerificationKey, generateClientAssertions

The pseudo code can be defined as below:

- Check is there any cache named as singpassOpenIDConfig, if the cache exists then return it
- If not, send a request to openID configuration endpoint and cached it for one hour
- call the getOpenIDConfiguration to get the JWKS endpoint url
- Send a request to JWKS endpoint, which receive a JWKS (JSON web key sets aka an array of JWK)
- import the signing key
- create an algorithm manager which used by creating JWS
- load all the support token signing algorithm from openID config token_endpoint_auth_signing_alg_values_supported attribute
- construct the JWS using JWSBuilder with all the claims provided based on SingpassDocs
- Sign the JWT
- Create a compact serializer and serialize the signed JWT
- return the JWT

Based on above line 82 the payload data which is based on the criteria according to Singpass Login API documentation

Source from NDI — Authorization API Reference (

Implement SingpassProvider methods

On line 50 above we need to swap the access_token with id_token before return the response body, this is due to Laravel Socialite will only refer access_token key as the token value.

Authorisation Endpoint
Singpass Login API requires RP to integrate their Singpass QR Login JS to provide QR scanning login experience via Singpass app. we will cover this on next topic on how to putting all of these together.

Implement ID Token Decryption and Encryption Methods

Once you exchange the token with the token_url_endpoint by using the auth code the provide in the callback url, you will received this JSON response included access_token, id_token, and token:

"access_token" : "Baumlj4B82CnZLv1GhxNoNIIWwW/xx3xp+VcJWyLbEs=", "token_type" : "Bearer",
"id_token" : "eyJraWQiOiJuZGlfc3RnXzAxIiwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJhdWQiOiJ4eE5zVGZsZVFNSG9XNnRiVWdTVk53bkxXUTB4VGVWMCIsInN1YiI6InM9Uzg4MjkzMTRCLHU9MWMwY2VlMzgtM2E4Zi00ZjhhLTgzYmMtN2EwZTRjNTlkNmE5IiwiYW1yIjpbInB3ZCIsInN3ayJdLCJpc3MiOiJodHRwczpcL1wvc3RnLWlkLnNpbmdwYXNzLmdvdi5zZyIsImV4cCI6MTYxODkyOTAxMCwiaWF0IjoxNjE4OTI4NDEwLCJub25jZSI6IkNNM1pcL1wvSytFZ1JnWnNyXC9JdHNUcDhXdTJXeEZHaDIzeUtSeFlqazJDekE9In0.TbFHH_W8Dzh8RJf6iejpZ8SJWG7ytvbxO0nUE8YgTP5tvE0JP0Tk0XmZ6t2P7FSt1BjvZ_Ids3vPMQ9lVJnX1A"

Based on the response above we only interested in id_token not access_token, due to the access_token is just a random key generated by SingPass, the id_token is what we need, and it is an encrypted JWE additional step such as using our decryption key to decrypt the id token and verify the decrypted token with the Singpass verification key which will be available in SingPass JWKS endpoint.

Decrypt ID Token

To decrypt ID token you need to load the decryption key (private key) into JWK via JWKFactory, please ensure that your passphrase is correct and configured in your .env if your decryption key is encrypted.

If the decryption for the id_token is success you will get a JWS, but this is not the end, we still need to verify whether this JWS is signed by SingPass authority, to do that we need to define two methods, one for fetching the JWKS from SingPass.

Once the token is decrypted and verified, mapUserToObject method will get called with a $user parameter which is a JWT, the user data will be in the JWT claims sub attribute in comma separated value.

"aud" : "xxNsTfleQMHoW6tbUgSVNwnLWQ0xTeV0",
"sub" : "s=S8829314B,u=1c0cee38-3a8f-4f8a-83bc-7a0e4c59d6a9", "amr" : [ "pwd", "swk" ],
"iss" : "",
"exp" : 1618929010,
"iat" : 1618928410,
"nonce" : "CM3Z//K+EgRgZsr/ItsTp8Wu2WxFGh23yKRxYjk2CzA="

Based on the sub attribute above, we need to parse those value into a JSON format in order to construct a Socialite User object. We can write the parser as below:

And here is the complete code for our SingPass Login Laravel Socialite Provider

And here is all of the implementation of our Provider, for next part I will cover on how to setup those callback url endpoint, as well as writing a Vue component for Singpass QR Login JS as a wrapper.

Thanks for reading~

Portal To Next Part




Web Developer, self-taught programming and technology enthusiast.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

5 Futuristic usecases for Bots in Business Tech

How to deploy a RailsAPI to Heroku

5 Lessons From My First Year As A Software Engineer

Why you need to have an open mind as a Full-Stack Developer!

Udacity Android Dev Track Community Video: The Team Spirit & The Experience

3 tips to Kick-Ass on an Interview

Why move to cloud for running Bigdata Analytical pipelines?

🌓 From TensorFlow to PyTorch

Lee Li Wei

Lee Li Wei

Web Developer, self-taught programming and technology enthusiast.

More from Medium

Better way of handling exceptions in Laravel: Trammel — Part 1

Elegant and readable randomness using Faker

How to write raw sql query migration in laravel ?

Laravel, ReactJs, Soketi triumvirates: Step by step guide to build a real time WebApp —  Part 1