3. Token Exchange

Token exchange refers to the process of making a POST request to the token endpoint to exchange the authorization code (obtained from the redirect URL in the previous step) with an ID token and access token.

The token endpoint is defined in the token_endpoint field located in our authorization server's OpenID Configuration.

Request Body

You should send the request body in application/x-www-form-urlencoded format. The DPoP header containing the DPoP JWT proof should also be included in your request.

The request body should have the following parameters:

Parameter
Description
Data type

client_id

The client ID of your registered client, provided by Singpass during app onboarding. It is the App ID found at the top of your app configuration page.

A 32-character case-sensitive alphanumeric string.

redirect_uri

The redirect_uri which you had used earlier when making the Authorization request.

URL

grant_type

The grant being requested.

Must be the string authorization_code, as mandated by OIDC specifications.

code

The authorization code issued by us, obtained via redirection back to your redirect_url in the previous step.

A base64url-encoded string.

client_assertion_type

The type of client assertion.

Must be the string urn:ietf:params:oauth:client-assertion-type:jwt-bearer, as mandated by OIDC specifications.

client_assertion

Your client assertion, which is used for authentication. Refer to our guide for instructions on how this should be generated.

A string containing the signed JWT.

code_verifier

The code verifier which you have generated when constructing your authorization request. This is used as part of PKCE.

String containing only alphanumeric characters, dashes, and underscores, between 43-128 characters long.

Sample request
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiQXNWakh4elZ4MURaNnNKcEpzUnM2ek5YYXFmcFR3UmNfcXV0bWw0aEFJQSIsInkiOiI0SkhwVVZDRE5DaXhOTW9OclIzSElodFRzTWNfMF9NcmdpMzJxR3VoUkQ4In19.eyJqdGkiOiIyZmM3Y2Q4ZC0xN2IzLTRlYTUtYTg4ZC1lZWM0NTY5M2JjZmUiLCJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vZGF0YSIsImlhdCI6MTc1NjcwODI0N30.wBYjFQRzY2o5aG82LjR1X8qT9bZ-jK3c7hI5fE0gP9vR_7sU6tW5xV4yZ3aB2cE1dF0eG9hI8jJ7kL6mN5oP4qR
Host: id.singpass.gov.sg

grant_type=authorization_code&redirect_uri=https%3A%2F%2Fpartner.gov.sg%2Fredirect&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjY3NTUxZWE2ZWE2NDk2ZmZiMWRkZmI4YTFhM2FlYjA0In0.eyJzdWIiOiJUNXNNNWE1M1lhdzNVUnlERXYyeTkxMjlDYkVsQ04yRiIsImF1ZCI6Imh0dHBzOi8vaWQuc2luZ3Bhc3MuZ292LnNnL2ZhcGkiLCJpc3MiOiJUNXNNNWE1M1lhdzNVUnlERXYyeTkxMjlDYkVsQ04yRiIsImlhdCI6IjE3NTY3ODUzNzciLCJleHAiOiIxNzU2Nzg1NDkzIiwiY29kZSI6IlhjeXpsU2VYMWhJeUpGbHN0eHNTRl9VZVhDNUR0aVlrRmdKOFZWeDUybWcifQ.fQMHTVNeCsa_mK6pmfjfi0rxERxDtu6eiMFkkpFdQrirc3u9LxP1_-E_7yIalIhmot1NdBQxbA9s_VztlQUhTQ&client_id=T5sM5a53Yaw3URyDEv2y9129CbElCN2F&code_verifier=mN7szCmmIc6Z2Vg-iaX7f7RDVsKAhY5GG-r7Crq0jxTdxY0xyPKnsAEWtEMdZ3D8QW5rs-C824W3Jwntcw

Response Body

Successful Response

If the request was successful. we will return a response in application/json format which includes the following parameters:

Parameter
Description
Data type

access_token

For Login applications, this is a random string which should not be used. For Myinfo (v5) applications, this is an encoded JWT that can be used to obtain user information using the UserInfo endpoint. This access token has a lifetime of 30 minutes.

String

id_token

The encrypted ID token in JWT format, signed by us. More information on how to parse this ID token is available in the next page.

String

token_type

The type of token being requested.

This will always be DPoP, as mandated by FAPI 2.0 specifications.

Sample successful response body
{
  "id_token": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.A3-aQ1V0a2-FFgT9YS8b_vX-k8aPyiQh_C0d7gB5rE8e_g6hI5jK4lM3nO2pQ1rS_tU9vW8xY7zZ-aB1c2D3eF4gH5iJ6kL7mN8oP9qR0sT1uV2wX3yZ4aB5c6D7eF8gH9iJ0kL1mN2oP3qR4sT5uV6wX7yZ8aB9c.3f8v2n5k9b1m4p7s.g-sW3r_x9zY7vU5tQ3rS-p1oN0mK9jI7hG5fD3c2B1a_Z_Y-X_W_V_U_T_S_R_Q_P_O_N_M_L_K_J_I_H_G_F_E_D_C_B_A-placeholder-ciphertext.V2xX1yZ0aB1c2D3eF4gH5iJ6kL7",
  "access_token": "Ul5JqK8vJX8Jfgo7UjpiIigF7DciW7YGnUACLN1/T80=",
  "token_type": "DPoP"
}

Error Response

If there was an error with the request, we will instead return a response body in application/json format with the following parameters:

Parameter
Description
Data type

error

An error code identifying the type of error that has occurred.

This will be an enum value. The possible values are detailed below.

error_description

A human-readable text description of the error.

String. This is optional.

error_uri

URI of a web page that includes additional information about the error

URL. This is optional.

Sample error response
{
  "error": "invalid_request",
  "error_description": "The request is missing a required parameter, includes an unsupported value, or is malformed."
}

Possible error values

The table below lists the possible values of error that we may return:

error
What this error indicates

invalid_request

The request had missing or invalid parameters. You should ensure that all the parameters follow the specifications listed above. This error will also be returned if you did not send the DPoP header in your request.

unsupported_grant_type

This means that you had passed a grant_type value which is not the value authorization_code. You should update your implementation to send grant_type as authorization_code.

invalid_grant

This means that the authorization code has expired. You should ensure that token exchange happens within 60 seconds of the authorization code being issued.

invalid_client

This can be caused by a few possible issues:

  • The client_assertion that you generated was invalid. Read our guide on client assertions and ensure that your implementation follows the guide. The method to generate the client assertion should be the same as the one used to generate the client assertion for the Pushed Authorization Request.

  • The redirect_uri provided is different from the one which you have sent in the Pushed Authorization Request

  • The client_id provided is invalid

invalid_dpop_proof

The DPoP header which you have provided is invalid, expired, or malformed. Read our DPoP guide and ensure that your implementation follows the instructions in the guide.

server_error

The server encountered an unexpected error. You should check error_description to understand what was the cause of the error.

This error can potentially be caused by your JWKS endpoint not being reachable. Read our guide on JWKS and ensure that you follow the requirements. In particular, your JWKS endpoint must be publicly accessible and should respond in a timely manner.

If the cause was not your JWKS endpoint, you may perform up to 3 retries using an exponential backoff strategy. If the request still fails, you should guide users to alternative authentication methods.

temporarily_unavailable

The server is temporarily unavailable to handle the request. You may perform up to 3 retries using an exponential backoff strategy. If the request still fails, you should guide users to alternative authentication methods, or to guide them to try again some time later.

Last updated

Was this helpful?