> For the complete documentation index, see [llms.txt](https://docs.corppass.gov.sg/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.corppass.gov.sg/technical-specifications/corppass-authorization-api-fapi-2.0/migration-guides/migration-guide-legacy-to-fapi-2.0.md).

# Migration Guide: Legacy to FAPI 2.0

This guide assists partners in transitioning existing integrations from the [Corppass Authorization API (Legacy)](/technical-specifications/corppass-authorization-api-legacy.md) to the new **FAPI 2.0‑compliant Authorization API**.

The new implementation strengthens security by adopting the **Financial-grade API (FAPI) 2.0 Security Profile**, built on OAuth 2.0 and OpenID Connect.

## Why Migrate?

FAPI 2.0 introduces enhanced security mechanisms, mandated flows, and updated token structures that improve confidentiality, integrity, and replay protection compared to the Legacy Security Profile. These changes align with global security best practices and provide long‑term reliability.

Legacy Security Profile will be deprecated, and partners must migrate to continue using the service.

***

## Key Changes

See [Changelog: FAPI 2.0 Launch](/technical-specifications/corppass-authorization-api-fapi-2.0/changelog/corppass-authorization-api-fapi-2.0-launch.md)

<table data-full-width="true"><thead><tr><th width="194.8046875">Feature</th><th>Legacy Security Profile</th><th>FAPI 2.0 Security Profile</th></tr></thead><tbody><tr><td><strong>Enhanced Security</strong></td><td>Standard authorization code flow</td><td><p>Enhanced security with</p><ul><li><strong>Stricter TTL</strong> requirements.</li><li><strong>Stricter</strong> <a href="/pages/LywRxjs8azw2UCtFr8yf"><strong>Client Assertion</strong></a> requirements.</li><li><strong>Mandatory</strong> <a href="/pages/TOFgMaQYGoeEs2HmOyj3"><strong>Proof Key of Code Exchange (PKCE)</strong></a>.</li><li><strong>Mandatory</strong> <a href="/pages/QOVgmxTpNTkdnLGQCrH6"><strong>Demonstrating Proof of Possession (DPoP)</strong></a> (sender-constrained tokens).</li><li><strong>Stricter</strong> set of allowed <strong>TLS cipher suites</strong>.</li><li>Enforce <strong>HTTPS</strong> scheme for callback redirect URLs</li></ul></td></tr><tr><td><strong>Authorization Flow</strong></td><td>Authorization parameters provided via <em>front-channel</em> (params in URL).</td><td><p>Authorization parameters provided via <em>back-channel</em> <strong>Pushed Authorization Request (PAR)</strong> endpoint.</p><p></p><p>The requests are also contextualised with new parameters for Corppass Login applications:</p><ul><li><code>authentication_context_type</code></li><li><code>authentication_context_message</code> </li></ul><p></p><p>Refer to <a href="/pages/Ah0kPLGW3E6t1tnusPN6">Pushed Authorization Request</a> for more details.</p></td></tr><tr><td><strong>Authorization Request Parameters</strong></td><td><p></p><p>Authorization Endpoint:</p><ul><li><code>client_id</code></li><li><code>code_challenge</code></li><li><code>code_challenge_method</code></li><li><code>response_type</code></li><li><code>redirect_uri</code></li><li><code>scope</code></li><li><code>state</code></li><li><code>nonce</code></li></ul></td><td><p></p><p>Pushed Authorization Request:</p><ul><li><code>client_id</code></li><li><strong><code>client_assertion</code></strong></li><li><strong><code>client_assertion_type</code></strong></li><li><code>code_challenge</code></li><li><code>code_challenge_method</code></li><li><code>response_type</code></li><li><code>redirect_uri</code></li><li><code>scope</code></li><li><code>state</code></li><li><code>nonce</code></li><li><strong><code>acr_values</code></strong></li><li><strong><code>dpop_jkt</code></strong></li></ul><p></p><p>Authorization Endpoint:</p><ul><li><code>client_id</code></li><li><strong><code>request_uri</code></strong> </li></ul><p></p><p>Refer to <a href="/pages/Ah0kPLGW3E6t1tnusPN6">Pushed Authorization Request</a> and <a href="/pages/be3ILFlYmJIRS9Hv9uv3">Authorization Endpoint</a> for more details.</p></td></tr><tr><td><strong>ID Token</strong></td><td><p>Flat, custom structure.</p><ul><li><code>sub</code> encapsulating user information.</li><li><code>entityInfo</code> encapsulating entity information.</li><li><code>userInfo</code> encapsulating acting user's information.</li></ul></td><td><p>Structured, hierarchical structure.</p><ul><li><strong><code>sub</code></strong> representing subject (entity / user) identifier.</li><li><strong><code>sub_attributes</code></strong> encapsulating profile information about the subject (entity on primary / root level, user on actor level).</li><li><strong><code>act</code></strong> encapsulating information about the subject acting on behalf of the primary subject.</li></ul><p></p><p>Refer to <a href="/pages/uq6KYicO9NOENcs1EQ9p">ID Token</a> section for more details.</p></td></tr><tr><td><p><strong>Userinfo Endpoint</strong></p><p><strong>(Additional User / Authorization Data Retrieval)</strong></p></td><td><p>Endpoint: <code>/authorization-info</code>.</p><p></p><p>Returns PascalCase keys:</p><ul><li><code>AuthInfo</code></li><li><code>TPAuthInfo</code> </li></ul><p></p><p>Format: JWS</p></td><td><p><strong>Endpoint: <code>/userinfo</code></strong> (Standard OIDC endpoint name)</p><p></p><p>Returns snake_case keys:</p><ul><li><code>auth_info</code></li><li><code>tp_auth_info</code> </li></ul><p></p><p><strong>Format: JWE</strong></p><p></p><p>Refer to <a href="/pages/fFIrslCA8R1d2tn8SIza">Userinfo Endpoint</a> for more details.</p></td></tr><tr><td><strong>Error Handling</strong></td><td>Legacy / custom error formats.</td><td><p><strong>Standardised OAuth 2.0 Errors</strong>.</p><ul><li><code>error</code></li><li><code>error_description</code></li><li><code>state</code></li></ul></td></tr><tr><td><strong>Scopes</strong></td><td>Standard scopes (<code>openid</code>, <code>authinfo</code>, <code>tpauthinfo</code>)</td><td><p><strong>Finer-grained scopes</strong> (e.g., <code>entity.identity</code>, <code>user.identity</code>, <code>user.name</code>), allowing for more precise data access control.</p><p></p><p>Refer to <a href="/pages/l5fLsA0Ds1G1Xbp0zFzm">Scopes</a> section for more details.</p></td></tr></tbody></table>

***

## Migration Steps

### Step 1: Review Client Configuration & Scopes

Review your client configuration on the [**Corppass Developer Portal**](https://developer.corppass.gov.sg/) to accommodate new mandatory parameters and scope changes.

1. **Review Authentication Context Type:**\
   FAPI 2.0 introduces `authentication_context_type` as a mandatory parameter to define the transaction context.

   1. **Configuration:** Your client profile now includes a list of "Supported Authentication Context Types". Currently, only `APP_AUTHENTICATION_DEFAULT` is supported. This value will be automatically backfilled for all existing clients.
   2. **Constraint:** During an authorization request, you can only request a context type that is explicitly configured for your client. For now, you must use `APP_AUTHENTICATION_DEFAULT` value in your requests.

   Refer to [Authentication Context Parameters](/technical-specifications/corppass-authorization-api-fapi-2.0/integration-guide/1.-pushed-authorization-request-par-endpoint/authentication-context-parameters.md) section for more details.<br>

2. **Review Scopes:**\
   FAPI 2.0 introduces new finer-grained scopes.

   1. **Configuration:** To ensure continuity, Corppass will automatically backfill your client profile with finer-grained FAPI 2.0 scopes corresponding to your legacy access.
   2. **Review:** Audit the backfilled scopes in your client configuration. Remove any that provides access to data your application no longer needs.

   Refer to [Scopes](/technical-specifications/corppass-authorization-api-fapi-2.0/scopes.md) section for more details.

3. **Review OIDC Redirect URLs:**\
   FAPI 2.0 now mandates the use of redirect URLs with `https` scheme.
   1. **Configuration:** Make sure you have at least one usable redirect URL with `https` scheme configured.
   2. **Constraint**: During an authorization request, you must provide a redirect URL with `https` scheme, or else you will not be allowed to initialise authorization on FAPI 2.0.

### Step 2: Retrieve new metadata from OpenID discovery endpoint

The OpenID Connect Discovery URL remains the same as the Legacy API.

1. **New Endpoints Metadata:** Query the existing Discovery URL to retrieve updated provider metadata. Configure your application to use the new endpoint values found in the metadata:
   1. `pushed_authorization_request_endpoint` (New: Required for PAR)
   2. `userinfo_endpoint` (Replaces `/authorization-info`)

Refer to [OpenID Discovery Endpoint](/technical-specifications/corppass-authorization-api-fapi-2.0/integration-guide/0.-well-known-endpoints/openid-discovery-endpoint.md) for more details.

### Step 3: Authorization Request

**Significant Change:** You can no longer construct the authorization URL directly in the browser. You need to implement **Pushed Authorization Request (PAR)**.

1. **Call the PAR Endpoint (`/request`):**
   1. Initiate the flow via a *back-channel `POST` request*.
   2. Include DPoP proof in the request.
   3. Include the necessary OIDC parameters (e.g., `client_id`, `scope`, `redirect_uri`) along with the new mandatory fields (e.g., `client_assertion`, `code_challenge`, `authentication_context_type`).
   4. Corppass returns a `request_uri` that references your payload.
2. **Browser Redirect to Authorization Endpoint (`/authorize`):**
   1. Redirect the user to the Authorization Endpoint using only `client_id` and the returned `request_uri` returned in the previous step.
   2. This replaces the Legacy method of exposing sensitive parameters directly in the browser URL.

Refer to [Pushed Authorization Request (PAR) Endpoint](/technical-specifications/corppass-authorization-api-fapi-2.0/integration-guide/1.-pushed-authorization-request-par-endpoint.md) for more details.

### Step 4: Token Exchange

The Token Endpoint (`/token`) now enforces strict security proofs.

1. **Prepare the Request:**
   1. **Mandatory Client Authentication:** Use `client_assertion` (Private Key JWT).
   2. **Mandatory PKCE:** Include the `code_verifier` matching the `code_challenge` from PAR.
   3. **Mandatory DPoP:** Include a `DPoP` HTTP header. The public key in this header will be bound to the resulting Access Token.
2. **Handle the Response:**
   1. **Access Token:** Store the opaque token for subsequent API calls where needed.
      1. Note: The token is sender-constrained; you must use the same DPoP private key from the request to sign the DPoP header when using this token later.
   2. **ID Token:** Decrypt the JWE using your private encryption key to retrieve the signed payload, then validate the signature. See Step 5.

Refer to [Token Endpoint](/technical-specifications/corppass-authorization-api-fapi-2.0/integration-guide/3.-token-endpoint.md) for more details.

### Step 5: Decrypt & Map ID Token

The ID Token is an Encrypted JWT (JWE).

1. **Decrypt:** Use your private encryption key (from Step 2) to decrypt the token.
2. **Validate:** Verify the signature of the decrypted payload using Corppass's public signing key.
3. **Map New Claims:** Update your parsing logic to the new hierarchical structure.

<table data-full-width="true"><thead><tr><th width="127.703125">Data</th><th>Legacy Claim</th><th>FAPI 2.0 Claim</th></tr></thead><tbody><tr><td>Entity Details</td><td><p><code>entityInfo.*</code> </p><pre class="language-json"><code class="lang-json">{
  "entityInfo": {
    "CPEntID": "T09LL0001B",
    "CPEnt_TYPE": "UEN",
    "CPEnt_Status": "Registered",
    "CPNonUEN_Country": "",
    "CPNonUEN_RegNo": "",
    "CPNonUEN_Name": ""
  }
} 
</code></pre></td><td><p> <code>sub</code>, <code>sub_attributes.*</code> </p><pre class="language-json"><code class="lang-json">{
  "sub": "T09LL0001B",
  "sub_type": "entity",
  "sub_attributes": {
    "entity_type": "UEN",
    "entity_reg_number": "T09LL0001B",
    "entity_coi": "SG",
    "entity_name": "My Example Company",
    "entity_uen_status": "Registered"
  }
}
</code></pre></td></tr><tr><td>User Details</td><td><p><code>sub</code></p><pre class="language-json"><code class="lang-json">{
  "sub": "s=S1234567P,uuid=0f14a2fc-09c2-4780-95f0-8c28347f2780,u=CP192,c=SG"
} 
</code></pre><p><code>userInfo.*</code></p><pre class="language-json"><code class="lang-json">{
  "userInfo":{
    "CPAccType": "User",
    "CPUID_FullName": "John Grisham",
    "ISSPHOLDER": "YES"
  }
}
</code></pre></td><td><p><code>act.*</code></p><pre class="language-json"><code class="lang-json">{
  "act": {
    "sub": "0f14a2fc-09c2-4780-95f0-8c28347f2780",
    "sub_type": "user",
    "sub_attributes": {
      "account_type": "standard", 
      "identity_number": "S1234567P",
      "identity_coi": "SG",
      "name": "John Grisham"
    }
  }
}
</code></pre></td></tr></tbody></table>

Refer to [ID Token](/technical-specifications/corppass-authorization-api-fapi-2.0/integration-guide/3.-token-endpoint/id-token.md) section for more details.

### Step 6: Userinfo Endpoint

This is applicable if your application fetches additional user / authorization data.

1. **Use the new userinfo URL**
   1. In the legacy API, this was available on the `/authorization-info` endpoint.
   2. In the FAPI 2.0 API, this is served on the `/userinfo` endpoint (the metadata retrieved in Step 2, under `userinfo_endpoint`).
2. **Update Authorization header prefix to be DPoP instead of Bearer**
   1. In the legacy API, the Authorization header would have been sent as a bearer token:

      ```
      Authorization: Bearer <your_access_token>
      ```
   2. In the FAPI 2.0 API, this needs to be sent with a `DPoP` prefix:

      ```
      Authorization: DPoP <your_access_token>
      ```
3. **Handle new response format**
   1. Update your JSON parser to handle snake\_case keys (e.g., `auth_info` instead of `AuthInfo`).
   2. Since the response is now returned as a JWE, you must decrypt the payload using your registered private key before processing the claims.

Refer to [Userinfo Endpoint](/technical-specifications/corppass-authorization-api-fapi-2.0/integration-guide/4.-userinfo-endpoint.md) for more details.

### Step 7: Error Handling

Ensure your application handles standard OAuth 2.0 error parameters (`error`, `error_description`, `state`):

1. **Handle Redirect URI:** Error query parameters on the callback URL (e.g., `https://client.com/cb?error=access_denied&error_description=...`)
2. **Handle API Response:** JSON bodies for `/request`, `/token` , or `userinfo` failures.

Refer to the respective endpoints for more details.
