# 3. The Token Request

The next step in the Singpass authentication flow is the token request. This involves making a POST request to our **`token_endpoint`** (as defined in our [OpenID configuration](https://docs.developer.singpass.gov.sg/docs/technical-specifications/technical-concepts/openid-connect-discovery)) to exchange an authorization code (obtained from the redirect URL) for an ID token and access token.

## Request Body

You should send the request body in `application/x-www-form-urlencoded` format. The [DPoP header](https://docs.developer.singpass.gov.sg/docs/technical-specifications/technical-concepts/demonstrating-proof-of-possession-dpop) containing the DPoP JWT proof should also be included in your request.

The request body should have the following parameters:

<table><thead><tr><th width="199.28515625">Parameter</th><th>Description</th><th>Data type</th></tr></thead><tbody><tr><td><code>redirect_uri</code></td><td>The <code>redirect_uri</code> which you had used earlier when making the Authorization request.</td><td>URL</td></tr><tr><td><code>grant_type</code></td><td>The grant being requested.</td><td>Must be the string <code>authorization_code</code>, as mandated by OIDC specifications.</td></tr><tr><td><code>code</code></td><td>The authorization code issued by us, obtained via redirection back to your <code>redirect_url</code> in the previous step.</td><td>A base64url-encoded string.</td></tr><tr><td><code>client_assertion_type</code></td><td>The type of client assertion.</td><td>Must be the string <code>urn:ietf:params:oauth:client-assertion-type:jwt-bearer</code>, as mandated by OIDC specifications.</td></tr><tr><td><code>client_assertion</code></td><td>Your client assertion, which is used for authentication. Refer to our <a href="../technical-concepts/generation-of-client-assertion">guide</a> for instructions on how this should be generated.</td><td>A string containing the signed JWT.</td></tr><tr><td><code>code_verifier</code></td><td>The code verifier which you have generated when constructing your authorization request. This is used as part of <a href="../technical-concepts/proof-key-for-code-exchange-pkce">PKCE</a>.</td><td>String containing only alphanumeric characters, dashes, and underscores, between 43-128 characters long.</td></tr></tbody></table>

<details>

<summary>Sample request</summary>

{% code overflow="wrap" %}

```http
POST /fapi/token HTTP/1.1
Host: stg-id.singpass.gov.sg
Content-Type: application/x-www-form-urlencoded
DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiRTRhOHp0NTZuMnF3WmE3R1ZPdVV4SHMtUHNQWVIzYnVCNGhtcURyTnV3OCIsInkiOiJWQ3ktckFmN3dMOC1PeWlhdS1kS0dJdmN5THpTMHJULTFlQkxyTGVNM09NIn19.eyJqdGkiOiJiZmM5NWRmOS02NDVjLTQ4OTUtOTM5MS03OWFjMGYzMzI0YWEiLCJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9zdGctaWQuc2luZ3Bhc3MuZ292LnNnL2ZhcGkvdG9rZW4iLCJpYXQiOjE3NzA4MDU5MzMsImV4cCI6MTc3MDgwNjA1M30.ofhxMZLOme4Q5cQXii7bRyucH5A-xFUNJ0vJMlYSHacsV2oHyZJ2SWBsXXpsB4xHkzdwWamNldqoNZ-90-DYoA

grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fcallback&code_verifier=-gMZo7Qjn113fE6Ri_OGnycEV3idiSkvbFcWIaQyQYQ&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhc0M3eTF3dUZtcXdDWHlzNlM1RW5hSUxOY3k0eUpEeCIsInN1YiI6ImFzQzd5MXd1Rm1xd0NYeXM2UzVFbmFJTE5jeTR5SkR4IiwiYXVkIjoiaHR0cHM6Ly9zdGctaWQuc2luZ3Bhc3MuZ292LnNnL2ZhcGkiLCJpYXQiOjE3NzA3OTEzMTgsImV4cCI6MTc3MDc5MTQzOCwianRpIjoiODJmMzE2MmYtMjMwNS00ZmRkLTk5MjUtZWJiZGY0ZTgzNjIxIn0.OoUXyRpjT3zismetrSerQwAD7zi5NnODnINDRjDnuYovVof1tOoU3B-JtQ7HqpLBypUfb147gWjwUsSA2F54XA&code=4C0sivAY9mUdYL9C3V6tccx38FdX7-4grP_ZRbhgNCI

```

{% endcode %}

</details>

## Response Body

### Successful Response

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

<table><thead><tr><th width="143.640625">Parameter</th><th width="359.60546875">Description</th><th>Data type</th></tr></thead><tbody><tr><td><code>access_token</code></td><td><p>This is an encoded JWT that can be used to obtain user information using the UserInfo endpoint.</p><p><br>For Login applications, you should parse the ID token directly instead of using this access token. Refer to the "<a href="https://docs.developer.singpass.gov.sg/docs/technical-specifications/integration-guide/4.-parsing-the-id-token">4. Parsing the ID Token</a>" section for guidance.</p><p><br>This access token has a lifetime of 30 minutes.</p></td><td>String</td></tr><tr><td><code>id_token</code></td><td>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.</td><td>String</td></tr><tr><td><code>token_type</code></td><td>The type of token being requested.</td><td>This will always be <code>DPoP</code>, as mandated by FAPI 2.0 specifications.</td></tr></tbody></table>

<details>

<summary>Sample successful response body</summary>

{% code overflow="wrap" %}

```json
{
    "id_token": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJraWQiOiJlbmMtMjAyNCIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImdwM2RVdWc5OWYwYTNoN0xxQzRqZllkQkVBbEtvamlTMkVlQmRXbE1idEUiLCJ5IjoiQlBCaFdkMmFEUjhiSWdaZ0RxQUp5VE96R0hRX2s5cjhqMGtiRFJyNkdRZyJ9fQ.9vW4Y2c4K3bTGGdviOKyCmeU-_akIhBxRYcfE8ZVHAWu_PIcO-wOOqjARbp2pE3nMfvDBK0-1spmIA-LKBWO-I2TMe0_Eijg.7vzyv750dzEXuY2kBCVw6g.bQscx4K2J2WQrRxc--xGP2nAcdPw_bxpdUuAUB7KLZouPNIA70E528ttni7DPbC4ek7zv8ZHZfIgMc6SPQr_b8BvPTcq1Zz4_yc3q4ouVPyra41iMZvPhHf3dB3kdDvLoeLx3HTP-Tilxp2OQGCO1pqC8KDAKEnhJK6E-8XUBlSjsyeSlPwFjK7t4mdQ_hCM19M0C2mAB9lg8vWeGs_Hj55lyKvPXdg9fWIFSBaPrsaYaG9urO_EkswkHrkxVz3LfIRsu2irIx-AtM4Hf_PQAKwhbY55vXoYC3H18vFcK61i_5nyP0t2D_XgfONIZVGJaM_FbEW1DSvs1j8EPMgif8uv9Tdf0lgmMHzu75znKkKkz4bZQg_Srmtr8j3Smz7Tg4X4o0rdrNspeNsx-G1dJENjMLCUJdvejAlql7uFa1AVm13uGsaD8wSaDuvQcFSqlCxOqMLQulbBwNCEr-FRFDFn1sqIDzFS6G25bE_mfQ2aumx7VLAHihRYu9xHGBgLJpKFIoGU5OEV-yOGkGNFVtnR3CubVuCKAFyiJ-jZnE33x56a5xy2cApUK3gZkPZf9ovMnpiu7Lb9wFcFsiO2EzAs3xp7LbUaaEH9VhVXxMHP2qKFr9ww71_xQRvZvGoPajO17ErqfNkss3R1l0BzbFAIA-uN4hdOpE2a-LmTnHAgVmeg5u3u6Y3cXUS0Gpi_QG7sTBMbBhtkCKymlg8brZP8MQzkAFKb8OKlxDr-cHB0iytxA7Q9NtK7tBa9pFYYs46bsrU0_XdoUSSxzFBfyicInGVcZd4Es3i07RzxiDSn2Ku72epLGPxteFipYQeebOiONpf3viN6_mDQpSoUOcG72mNxS7pxk8QIC6BoffFVx2Pn8zjf81BHU4S_v_Gs.AyOBe2pNqOeXMTkjlW3WI3X4UUUOot6tDlcYPoF_Z7w",
    "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6ImFsaWFzL3N0Zy1zcC1hdXRoLWFwaS1pZC10b2tlbi1zaWduaW5nLWtleS1rbXMtYXN5bW1ldHJpYy1rZXktYWxpYXMifQ.eyJzdWIiOiIwMjdjNDdjMi02ZGM1LTRiMjItYTgwZi1iYmM1MmM1NzUwNjQiLCJjbGllbnRfaWQiOiJkTkdFT1NVanlKalRxeXRieHNzcHlKQXlRSWozdHloYSIsInNjb3BlIjoib3BlbmlkIHVpbmZpbiBuYW1lIGNoaWxkcmVuYmlydGhyZWNvcmRzLmRvYiBjaGlsZHJlbmJpcnRocmVjb3Jkcy50b2IgZGl2b3JjZWRhdGUgZG9iIGRyaXZpbmdsaWNlbmNlLmRpc3F1YWxpZmljYXRpb24uc3RhcnRkYXRlIGRyaXZpbmdsaWNlbmNlLmRpc3F1YWxpZmljYXRpb24uZW5kZGF0ZSBkcml2aW5nbGljZW5jZS5wZGwuZXhwaXJ5ZGF0ZSBkcml2aW5nbGljZW5jZS5xZGwuZXhwaXJ5ZGF0ZSBkcml2aW5nbGljZW5jZS5yZXZvY2F0aW9uLnN0YXJ0ZGF0ZSBkcml2aW5nbGljZW5jZS5yZXZvY2F0aW9uLmVuZGRhdGUgZHJpdmluZ2xpY2VuY2Uuc3VzcGVuc2lvbi5zdGFydGRhdGUgZHJpdmluZ2xpY2VuY2Uuc3VzcGVuc2lvbi5lbmRkYXRlIGhkYm93bmVyc2hpcC5kYXRlb2Zvd25lcnNoaXB0cmFuc2ZlciBoZGJvd25lcnNoaXAuZGF0ZW9mcHVyY2hhc2UgaGRib3duZXJzaGlwLmxlYXNlY29tbWVuY2VtZW50ZGF0ZSBsdGF2b2NhdGlvbmFsbGljZW5jZXMuYmF2bC5leHBpcnlkYXRlIGx0YXZvY2F0aW9uYWxsaWNlbmNlcy5iZHZsLmV4cGlyeWRhdGUgbHRhdm9jYXRpb25hbGxpY2VuY2VzLm9kdmwuZXhwaXJ5ZGF0ZSBsdGF2b2NhdGlvbmFsbGljZW5jZXMucGR2bC5leHBpcnlkYXRlIGx0YXZvY2F0aW9uYWxsaWNlbmNlcy50ZHZsLmV4cGlyeWRhdGUgbWFycmlhZ2VkYXRlIHBhc3NleHBpcnlkYXRlIHBhc3Nwb3J0ZXhwaXJ5ZGF0ZSBzcG9uc29yZWRjaGlsZHJlbnJlY29yZHMuZG9iIHNwb25zb3JlZGNoaWxkcmVucmVjb3Jkcy5zY3ByZ3JhbnRkYXRlIHZlaGljbGVzLmNvZWV4cGlyeWRhdGUgdmVoaWNsZXMuZmlyc3RyZWdpc3RyYXRpb25kYXRlIHZlaGljbGVzLm9yaWdpbmFscmVnaXN0cmF0aW9uZGF0ZSB2ZWhpY2xlcy5yb2FkdGF4ZXhwaXJ5ZGF0ZSB2ZWhpY2xlcy55ZWFyb2ZtYW51ZmFjdHVyZSBjcGZiYWxhbmNlcy5tYSBjcGZiYWxhbmNlcy5vYSBjcGZiYWxhbmNlcy5yYSBjcGZiYWxhbmNlcy5zYSBjcGZjb250cmlidXRpb25zIGNwZmVtcGxveWVycyIsImp0aSI6ImF0LXZhenRZOU5fYy1HYTBFTkx0ekJVajh2bnVlb2tyazVyTTczRmZ0aHNtZ1kiLCJpYXQiOjE3NzA3MDk1MzIsImV4cCI6MTc3MDcxMTMzMiwiYXVkIjoiaHR0cHM6Ly9zdGctaWQuc2luZ3Bhc3MuZ292LnNnL2ZhcGkvdXNlcmluZm8iLCJpc3MiOiJodHRwczovL3N0Zy1pZC5zaW5ncGFzcy5nb3Yuc2cvZmFwaSIsImNuZiI6eyJqa3QiOiJySF84UFJkUnZtWHg5dVNBMjVQTGdZSGJmQ3FMdlNJc1M1Yml3S004RmJvIn19.CVV8zLacj6nigOu-VU13DubRZcIyEvMpyOOF3m4KR8p9Zp_XNcPGRiQCx_Je9wNXFEUHcXZiWvhVfYVE7P5AxA",
    "token_type": "DPoP"
}
```

{% endcode %}

</details>

### 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:

<table><thead><tr><th width="164.765625">Parameter</th><th width="404.29296875">Description</th><th>Data type</th></tr></thead><tbody><tr><td><code>error</code></td><td>An error code identifying the type of error that has occurred.</td><td>This will be an enum value. The possible values are detailed <a href="#possible-error-values">below</a>.</td></tr><tr><td><code>error_description</code></td><td>A human-readable text description of the error.</td><td>String. This is optional.</td></tr><tr><td><code>error_uri</code></td><td>URI of a web page that includes additional information about the error</td><td>URL. This is optional.</td></tr></tbody></table>

<details>

<summary>Sample error response</summary>

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

</details>

#### Possible error values

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

<table><thead><tr><th width="251.06640625">error</th><th>What this error indicates</th></tr></thead><tbody><tr><td><code>invalid_request</code></td><td>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 <code>DPoP</code> header in your request.</td></tr><tr><td><code>unsupported_grant_type</code></td><td>This means that you had passed a <code>grant_type</code> value which is not the value <code>authorization_code</code>. You should update your implementation to send <code>grant_type</code> as <code>authorization_code</code>.</td></tr><tr><td><code>invalid_grant</code></td><td><p>This can be caused by two possible issues:</p><ul><li>The authorization code has expired. You should ensure that token exchange happens within 60 seconds of the authorization code being issued.</li><li>The <code>redirect_uri</code> provided is different from the one which you have sent in the Pushed Authorization Request</li></ul></td></tr><tr><td><code>invalid_client</code></td><td>The <code>client_assertion</code> that you generated was invalid. Read our <a href="../technical-concepts/generation-of-client-assertion">guide on client assertions</a> 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.</td></tr><tr><td><code>invalid_dpop_proof</code></td><td>The <code>DPoP</code> header which you have provided is invalid, expired, or malformed. Read our <a href="../technical-concepts/demonstrating-proof-of-possession-dpop">DPoP guide</a> and ensure that your implementation follows the instructions in the guide.</td></tr><tr><td><code>server_error</code></td><td><p>The server encountered an unexpected error. You should check <code>error_description</code> to understand what was the cause of the error.</p><p>This error can potentially be caused by your JWKS endpoint not being reachable. Read our guide on <a href="../../technical-concepts/json-web-key-sets-jwks#requirements-for-jwks">JWKS</a> and ensure that you follow the requirements. In particular, your JWKS endpoint must be publicly accessible and should respond in a timely manner.</p><p>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.</p></td></tr><tr><td><code>temporarily_unavailable</code></td><td>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.</td></tr></tbody></table>
