---
schema_version: "1"
id: 7b8f0dff2f4415be2c26c2586d47c6ebaed2bf8036378311e188eb041df7f574
title: "OAuth 2.0 for First-Party Applications: Headless Passwordless Login for Private Clients"
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/xcloud/264-0-0/remoteaccess_auth_challenge_passwordless_login.md
canonical_url: https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_auth_challenge_passwordless_login.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: 65b99c65b63c60c23e0ab167ebce3dec50fa0efffaff626bc08c1410170653b2
description: With headless passwordless login, users log in to your off-platform app via their email address or phone number and a one-time password. To set up passwordless login for an off-platform app developed by your company, use this headless passwordless login flow, which implements the OAuth 2.0 for First-Party Applications draft protocol. With this flow, you can entirely control the front-end login experience in your first-party app while Salesforce handles the backend work of authenticating users and granting access to protected resources. This flow is supported only for private clients, such as client-server apps, and is supported only for external users.
products:
  - Security
  - Cross Cloud
release: "264"
---
# OAuth 2.0 for First-Party Applications: Headless Passwordless Login for Private Clients

With headless passwordless login, users log in to your off-platform app via their email address or phone number and a one-time password. To set up passwordless login for an off-platform app developed by your company, use this headless passwordless login flow, which implements the OAuth 2.0 for First-Party Applications draft protocol. With this flow, you can entirely control the front-end login experience in your first-party app while Salesforce handles the backend work of authenticating users and granting access to protected resources. This flow is supported only for private clients, such as client-server apps, and is supported only for external users.

### Required Editions

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small edition"><colgroup><col></colgroup><tbody><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: both Salesforce Classic and Lightning Experience</td></tr><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: <strong>Enterprise</strong>, <strong>Unlimited</strong>, and <strong>Developer</strong> Editions</td></tr></tbody></table>

To set up headless passwordless login for a private client, you can also use [the passwordless login variation](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_headless_passwordless_login_private_clients.htm&type=5&language=en_US) of the Authorization Code and Credentials Flow, which implements Headless Identity APIs. Both flows accomplish the same use case—headless passwordless login for an app outside of the Salesforce platform. But there are a few key differences to keep in mind.

 
| OAuth For First-Party APPS | Headless Identity APIS |
| --- | --- |
| Supported only for private clients. | Supported for public and private clients. |
| Conforms to the [OAuth 2.0 for First-Party Applications draft protocol](https://www.ietf.org/archive/id/draft-parecki-oauth-first-party-apps-00.html). | Proprietary Salesforce flow that's built on top of the OAuth 2.0 standard. |
| Supported only for the Salesforce external client app framework. Also, the only way to configure external client app settings for this flow is via Metadata API. | Supported for both the Salesforce external client app and connected app frameworks. |
| For security, this flow always requires a client attestation JWT. Salesforce uses the client attestation JWT to validate that the app was developed by your company. | For security, requires either authentication or reCAPTCHA, but not a client attestation JWT. |

Before setting up this flow, complete these steps.

- [Complete prerequisites for headless identity](https://help.salesforce.com/s/articleView?id=xcloud.headless_identity_prerequisites.htm&release=264.0.0&type=5 "Before you get started with a Headless Identity implementation for your off-platform apps, cross off these prerequisites in Salesforce.").
- [Generate a client attestation JWT](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_auth_challenge_generate_jwt.htm&release=264.0.0&type=5 "When using headless identity flows that implement the OAuth 2.0 for First-Party Applications draft standard, use a client attestation JWT to prove that apps sending requests to Salesforce are first-party apps created by your company.").
- [Configure a Salesforce external client app](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_eca_auth_challenge.htm&release=264.0.0&type=5 "To develop headless identity flows that use the OAuth 2.0 for First-Party Applications draft standard, configure an external client app. For these flows, you must configure your external client app via Metadata API. Use these steps to configure an app for headless username-password login, passwordless login, and registration.").
- [Configure Experience Cloud settings](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_auth_challenge_experience_settings.htm&release=264.0.0&type=5 "Before you build headless username-password login, passwordless login, and registration flows using the OAuth 2.0 for First-Party Applications draft protocol, configure Experience Cloud settings on the Login & Registration page.").

By default, users enter their username to log in. To give users more options, set up headless user discovery. For example, develop a flow where users enter their email address, phone number, or even an order number. See [Headless Login without a Username](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_headless_discovery.htm&type=5&language=en_US).

Here's an overview of how the flow works.

- Step 1: An end user goes to your first-party app and enters their email address or phone number. Or, if you're using headless user discovery, they enter an identifier such as an email address, phone number, or order number, along with their password.
- Step 2: Your app finds the username associated with the user’s phone number or email address.
- Step 3: Your app mints a client attestation JWT. It also generates Proof Key for Code Exchange (PKCE) parameters.
- Step 4: Your first-party app sends a headless POST request to the services/oauth2/v1/authorization\_challenge endpoint on your Experience Cloud site. The request includes the user's client attestation JWT and the information that the user submitted, along with other parameters.
- Step 5: To confirm that your first-party app sent the request, Salesforce validates the client attestation JWT and then validates the other parameters in the request. If the request is successful, Salesforce returns an error response with an `auth_session`, but the response indicates that Salesforce sent an OTP to the user.
- (Optional) If you're using headless user discovery, your Apex handler finds the user based on the identifier that they used to log in. If the user credentials are valid and the user has a verified email address or phone number, the login proceeds.
- Step 6: Salesforce sends an OTP to the user's email address or phone number.
- Step 7: Your app natively displays a verification form.
- Step 8: The user enters their OTP in the verification form.
- Step 9: Your app sends another POST request to the services/oauth2/v1/authorization\_challenge endpoint, this time to request an authorization code. This request includes the `auth_session` returned from the first request along with the OTP that the user entered.
- Step 10: Salesforce verifies the request, returns the authorization code, and terminates the `auth_session`.
- Step 11: To exchange the code for an access token, your first-party app sends a request to the /services/oauth2/token endpoint.
- Step 12: Salesforce returns a token response containing the access token.
- Step 13: Your first-party app processes the token response and creates the user's session.
- Step 14: The end user is now logged in and performs an action in your app that requires access to a protected Salesforce resource.
- Step 15: Your first-party app makes an authenticated call to a Salesforce API.
- Step 16: The user can now access their Salesforce data in your first-party app.

## Step 1: End User Enters Their Email Address or Phone Number for Login

An end user visits your first-party app. Your app natively displays a login form requesting only the user's email address or phone number. The user enters their email address or phone number in the form.

## Step 2: First-Party App Finds the Username

Your first-party app finds the username associated with the user's phone number or email address.

## Step 3: First-Party App Mints a Client Attestation JWT and Generates `code_verifier` and `code_challenge` Values for PKCE

The app [mints a client attestation JWT](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_auth_challenge_generate_jwt.htm&release=264.0.0&type=5 "When using headless identity flows that implement the OAuth 2.0 for First-Party Applications draft standard, use a client attestation JWT to prove that apps sending requests to Salesforce are first-party apps created by your company.").

The app also generates values for the PKCE parameters used to verify the authorization code.

![Note](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-xcloud-identity-264-0-0-production-enus/699dab6f-8f1a-4662-b24f-6521d5b747f7/images/icon_note.png)

Note We strongly recommend that you always implement PKCE for this flow. Otherwise, you don't get its security benefits.

For more information on PKCE, see the specificationRFC 7636: Proof Key for Code Exchange by OAuth Public Clients provided by the [Internet Engineering Task Force (IETF)](https://datatracker.ietf.org/doc/rfc7636/).

The PKCE specification defined in [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) also includes an optional `code_challenge_method` parameter that you can send in the authorization request. Salesforce ignores any value that you send in this parameter and defaults to `SHA256`.

## Step 4: App Sends Initial Request to Authorization Challenge Endpoint

To initialize passwordless login, your app sends the user's credentials, along with other parameters, to the services/oauth2/v1/authorization\_challenge endpoint on your Experience Cloud site via a headless POST request.

There are no required headers for this request. Optionally, to connect this flow to the headless guest flow, you can include a `Uvid-Hint` header with a JWT-based access token containing a unique visitor ID (UVID) value.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small " summary="Initial POST Request to Authorization Challenge Endpoint: Headers">
        <caption>Initial POST Request to Authorization Challenge Endpoint: Headers</caption>
        <colgroup>
          <col style="width:33.33333333333333%">
          <col style="width:33.33333333333333%">
          <col style="width:33.33333333333333%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e359">Header</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e362">Required?</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e365">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Header" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e359 ">
              <code>Uvid-Hint</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e362 ">
              <p>No. If you implement the guest user flow on your app, you can optionally use
                this header to pass in a JWT-based access token containing a UVID tied to a guest
                user’s identity. By passing the <code>UVID</code> into a
                named user flow, you can carry contextual information from a guest user session,
                like the user’s cookie preferences, into a named user session.</p>
              <p style="margin-bottom:0;">Instead of passing
                a JWT-based token with a <code>UVID</code> in a header,
                you can also pass the plain <code>UVID</code> value in the
                request body.</p>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e365 ">A JWT-based access token containing a <code>UVID</code>
              value, which is a Version 4 universally unique identifier (UUID) that’s generated
              and managed entirely by your app. To get an access token with a <code>UVID</code>, you must enable your external client app or
              connected app to issue JWT-based access tokens and implement the headless guest flow
              on your app.</td>

          </tr>
        </tbody>
      </table>

Include these parameters in the request body.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small " summary="Initial POST Request to Authorization Challenge Endpoint: Body Parameters">
        <caption>Initial POST Request to Authorization Challenge Endpoint: Body Parameters</caption>
        <colgroup>
          <col style="width:33.33333333333333%">
          <col style="width:33.33333333333333%">
          <col style="width:33.33333333333333%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e428">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e431">Required?</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e434">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>username</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">The username associated with the user's email address or phone number.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>login_type</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">The method that the user is using to log in. The value can either be <code>sms</code> or <code>email</code>.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>client_assertion</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">The client attestation JWT that you generated, signed by the certificate
              configured for your external client app.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>recaptcha_token</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">
              <p>Required if these conditions apply to your configuration:</p>
              <ul>
                <li>You enabled <strong> Require reCAPTCHA to access the Headless Passwordless Login
                    API</strong> in your Experience Cloud Login &amp; Registration settings.</li>
                <li>You're using reCAPTCHA v2 or v3.</li>
              </ul>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">An encrypted token issued by the Google reCAPTCHA API when a user completes a
              reCAPTCHA challenge.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>recaptchaevent</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">
              <p>Required if these conditions apply to your configuration:</p>
              <ul>
                <li>You enabled <strong> Require reCAPTCHA to access the OAuth 2.0 authorization
                    challenge endpoint for username-password login authorization flows</strong> in your
                  Experience Cloud Login &amp; Registration settings.</li>
                <li>You're using reCAPTCHA Enterprise.</li>
              </ul>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">
              <p>A JSON object containing these subparameters.</p>
              <ul>
                <li><code>token</code>—An encrypted token issued by the
                  Google reCAPTCHA API when a user completes a reCAPTCHA challenge.</li>
                <li><code>siteKey</code>—The Google reCAPTCHA site key. </li>
                <li>(Optional)<code>expectedAction</code>—The action that
                  you expect the user to take to initiate reCAPTCHA, such as <code>login</code>. This parameter maps to Google's <code>action</code> parameter.</li>
                <li><code>projectId</code>—The project ID from
                  Google.</li>
              </ul>
              <p style="margin-bottom:0;">For more information, see <a href="https://cloud.google.com/recaptcha/docs/setup-overview-web" target="_blank">Google's reCAPTCHA documentation</a>.</p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>scope</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">No.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">
              <p>Permissions that define the type of protected resources that the external
                client app can access. You assign scopes to the external client app when you build
                it, and they’re included with the OAuth tokens during the authorization flow.</p>
              <p style="margin-bottom:0;">Use
                this parameter to request a subset of the scopes assigned to your external client
                app. If you don’t include this parameter, all scopes assigned to the app are
                requested</p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>client_id</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 "> The consumer key of the external client app. </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>code_challenge</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">Required if you required PKCE for your external client app. For this flow's
              security features to work correctly, we strongly recommend that you always require
              PKCE.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">
              <p>Specifies the SHA256 hash value of the <code>code_verifier</code> value in the token request. Set this parameter to help
                prevent authorization code interception attacks. The value must be Base-64 URL
                encoded as defined in <a href="https://tools.ietf.org/html/rfc4648#section-5" target="_blank">https://tools.ietf.org/html/rfc4648#section-5</a></p>
              <p>If a <code>code_challenge</code> is provided in the authorization
                request and a <code>code_verifier</code> is provided in
                the token request, Salesforce compares the two values. If the <code>code_challenge</code> is invalid or doesn’t match, the
                login fails with the <code>invalid_request</code> error
                code.</p>
              <p style="margin-bottom:0;">If the <code>code_challenge</code> is provided
                in the authorization request, but there’s no <code>code_verifier</code> in the token request, the login fails with the <code>invalid_grant</code> error code.</p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>email_template</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">
              <p>Required to specify multiple email templates if email template allowlisting is
                enabled.</p>
              <p>If you didn’t enable email template allowlisting, you can’t include
                this parameter.</p>
              <p style="margin-bottom:0;">If you don’t include this parameter, Salesforce uses the
                default email template configured in your Experience Cloud settings, regardless of
                whether allowlisting is enabled. If there’s no template configured, Salesforce
                uses a default OTP email template. The email template language for default
                templates is controlled by the user’s language settings in Salesforce.</p>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">
              <p>The custom email template developer name. This parameter can include only an
                email template from the allowlist.</p>
              <p style="margin-bottom:0;">To control the language for custom email
                templates, create templates in the desired language.</p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>login_hint</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">Required if you're using a headless user discovery Apex handler.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">An identifier that your Apex handler uses to find a user's Salesforce account.
              For example, collect a user's order number in your app and pass it in the <code>login_hint</code> parameter. We send the <code>login_hint</code> value straight to your Apex
              handler.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e428 ">
              <code>customdata</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e431 ">
              <p>Required if you're using a headless user discovery handler that handles custom
                data. For example, if you're also using the handler with a login flow that handles
                custom data, you must pass custom data in the forgot password flow.</p>
              <p style="margin-bottom:0;">Otherwise,
                it's optional but can be useful to help your handler find the user.</p>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e434 ">A JSON string containing additional data that your Apex headless discovery
              handler uses to find a user's Salesforce account. For example, pass information
              about the user's locale.</td>

          </tr>
        </tbody>
      </table>

Here's an example request.

```
POST /services/oauth2/v1/authorization_challenge HTTP/1.1
Host: MyExperienceCloudSite.my.site.com

username=janice.edwards@example.com&
login_type=sms&
client_assertion=**********&
recaptcha_token=********&
scope=profile&
client_id=******&
code_challenge=********
```

## Step 5: Salesforce Validates the Request

Salesforce first attempts to validate the client attestation JWT by checking that the signature passed in the `client_asssertion` parameter matches the signature for the certificate configured for the external client app.

If the client attestation JWT isn't valid, Salesforce returns an `invalid_attestation` error and you must resubmit the request with all of the parameters you originally submitted. Here's an example error response.

```
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store

{
  "error": "invalid_attestation",
  "error_code": "client_attestation_failed"
}
```

If the client attestation JWT is valid, but there are other issues with the request, Salesforce returns an error response specifying what went wrong. Here's an example response that's returned if Salesforce can't find a username that matches the one sent in the request.

```
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store

{
  "error": "authorization_required",
  "auth_session": "uY29tL2F1d*****",
  "error_code": "invalid_credentials"
}
```

The response includes an `auth_session` parameter that remains valid for 5 minutes after it's issued. During the timeframe while the `auth_session` is valid, you can use it to resubmit the request. In the corrected versions that you resubmit, include the `auth-session` and the corrected values for the parameters that caused the request to fail. You must also resubmit the `password` with each request because Salesforce doesn't store this sensitive value. But for other parameters, if they didn't cause the request to fail, you can leave them out. Salesforce already knows that you submitted these parameters because they're linked to the `auth_session`.

![Note](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-xcloud-identity-264-0-0-production-enus/699dab6f-8f1a-4662-b24f-6521d5b747f7/images/icon_note.png)

Note reCAPTCHA parameters don't get linked to the `auth_session`, but you can resubmit the request without them unless they caused the request to fail.

For example, if your client attestation JWT is valid but the request fails because the username was incorrect, resubmit a request that includes only the `auth_session`, `username`, and `password`.

## (Optional) Headless User Discovery Handler Finds the User

If you're using a headless user discovery handler, the handler takes the `login_hint` and `customdata`parameters and finds the associated user. The handler confirms that the email address or phone number for the user is verified.

For an example handler, see [Auth.HeadlessUserDiscoveryHandler](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_interface_Auth_HeadlessUserDiscoveryHandler.htm).

## Step 6: Salesforce Sends an OTP to the User

If the request is successful, Salesforce still returns an error response because it can't yet grant the authorization code. But this time, the error response indicates that login was initialized and that Salesforce sent an OTP to the user's email address or phone number. To confirm that the request was successful, look out for the error code `login_initialized` and the state `otp_sent`. The response also includes an `auth_session` parameter, which is important for the next step.

Here's an example response.

```
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store

{
  "error": "authorization_required",
  "auth_session": "uY29tL2F1dGhlbnRpY",
  "error_code": "login_initialized",
  "login_status": {
    "type": "SMS",
    "state": "otp_sent",
    "displayData": "+120******58"
   }
}
```

## Step 7: Your App Natively Displays a Verification Form

In your first-party app, you display a verification form where the end user can enter their OTP. The look and feel of this form are entirely up to you.

## Step 8: User Enters OTP in Verification Form

The user receives the OTP at their email address or phone number and enters it in the verification form in your first-party app.

## Step 9: Your App Requests an Authorization Code

To request an authorization code, your app sends another POST request to the /services/oauth2/v1/authorization\_challenge endpoint. This request has no required headers. Include these parameters in the request body.

Authorization Code Request: Body Parameters   
| Parameter | Required? | Description |
| --- | --- | --- |
| `auth_session` | Yes. | Represents the current login attempt. Use the `auth_session` value that you received in the response from your first request to the authorization challenge endpoint. Make sure to use the `auth_session` from the request that indicated that login was initialized and the OTP was sent. |
| `login_otp` | Yes. | The OTP that the end user entered in your app's verification form. |

Here's an example request.

```
POST /services/oauth2/v1/authorization_challenge HTTP/1.1
Host: MyExperienceCloudSite.my.site.com

auth_session=uY29tL2F1dGhlbnRpY*
login_otp=<otp_from_sms>
```

## Step 10: Salesforce Returns an Authorization Code

If the OTP is correct and the `auth_session` is valid, Salesforce returns an authorization code and terminates the `auth_session`. Here's an example authorization code response.

```
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store

{
  "authorization_code": "uY29tL2F1d******"
}
```

## Step 11: Your App Exchanges the Authorization Code for an Access Token

After you get the authorization code, your app sends a request to the services/oauth2/token endpoint.

This request has no headers. Include these parameters in the request body.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small " summary="Token Request: Body Parameters">
        <caption>Token Request: Body Parameters</caption>
        <colgroup>
          <col style="width:33.33333333333333%">
          <col style="width:33.33333333333333%">
          <col style="width:33.33333333333333%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e979">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e982">Required?</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d28519e985">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e979 ">
              <code>code</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e982 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e985 ">The authorization server creates an authorization code, which is a short-lived
              token, and passes it to the client after successful authentication. The client sends
              the authorization code to the authorization server to obtain an access token and,
              optionally, a refresh token.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e979 ">
              <code>client_id</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e982 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e985 ">The consumer key of the external client app.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e979 ">
              <code>client_secret</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e982 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e985 ">The consumer secret of the external client app. In this flow, it acts as a
              password that the app uses to access Salesforce.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e979 ">
              <code>redirect_uri</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e982 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e985 ">The URL where users are redirected after a successful authentication. The
              redirect URI must match one of the values in the external client app Callback URL
              field. Otherwise, the approval fails. This value must be URL encoded.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e979 ">
              <code>grant_type</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e982 ">Yes.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e985 ">The type of validation that the app can provide to prove it’s a safe visitor.
              For this flow, the value must be <code>authorization_code</code>.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e979 ">
              <code>code_verifier</code>
            </td>

            <td data-label="Required?" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e982 ">Required if you required PKCE for your external client app. For this flow's
              security features to work correctly, we strongly recommend that you always require
              PKCE.</td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d28519e985 ">
              <p>Specifies 128 bytes of random data with high entropy to make guessing the code
                value difficult. Set this parameter to help prevent authorization code interception
                attacks. The value must be base64url-encoded as defined in <a href="https://datatracker.ietf.org/doc/html/rfc4648#section-5" target="_blank">https://datatracker.ietf.org/doc/html/rfc4648#section-5</a>.</p>
              <p>If there’s a <code>code_verifier</code> value in the token request and a
                <code>code_challenge</code> value in the authorization
                request, Salesforce compares the two values. If the <code>code_verifier</code> is invalid or doesn’t match, the login fails with the
                <code>invalid_grant</code> error code.
              </p>
              <p style="margin-bottom:0;">If the
                <code>code_verifier</code> value is in the token
                request, but there’s no <code>code_challenge</code> value
                in the authorization request, the login fails with the <code>invalid_grant</code> error code.
              </p>
            </td>

          </tr>
        </tbody>
      </table>

Here's an example token request.

```
POST services/oauth2/token? HTTP 1.1
Host: MyExperienceCloudSite.my.site.com

code=********&
client_id=**********&
client_secret=*********&
redirect_uri=<callback_URL>&
grant_type=authorization_code&
code_verifier=*******
```

## Step 12: Salesforce Grants an Access Token

After validating the app’s credentials. Salesforce returns an access token. Here’s an example access token response in JSON format.

```
{
"access_token":"*******************",
"sfdc_community_url":"https://MyDomainName.my.site.com",
"sfdc_community_id":"0DBxxxxxxxxxxxx",
"signature":"ts6wm/svX3jXlCGR4uu+SbA04M6qhD1SAgVTEwZ59P4=",
"scope":"openid api",
"id_token":"XXXXXX",
"instance_url":"https://yourInstance.salesforce.com",
"id":"https://yourInstance.salesforce.com/id/00Dxxxxxxxxxxxx/005xxxxxxxxxxxx",
"token_type":"Bearer",
"issued_at":"1667600739962"
}
```

The access token response contains these parameters.

Token Response Parameters   
| Parameter | Required? | Description |
| --- | --- | --- |
| `access_token` | Yes. | OAuth token that an external client app uses to request access to a protected resource on behalf of the client application. Additional permissions in the form of scopes can accompany the access token. |
| `id` | Yes. | An identity URL that can be used to identify the user and to query for more information about the user. See [Identity URLs](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_using_openid.htm&type=5&language=en_US). |
| `id_token` | No. | A signed data structure that contains authenticated user attributes, including a unique identifier for the user and a timestamp indicating when the token was issued. It also identifies the requesting app. See [OpenID Connect specifications](https://openid.net/specs/openid-connect-basic-1_0-28.html). |
| `instance_url` | Yes. | A URL indicating the instance of the user’s org. For example, `https://yourInstance.salesforce.com/`. |
| `issued_at` | Yes. | A timestamp of when the signature was created, expressed as the number of milliseconds from 1970-01-01T0:0:0Z UTC. |
| `refresh_token` | No. | Token obtained from the web server, user-agent, or hybrid app token flow. This value is a secret. Take appropriate measures to protect it. This parameter is returned only if your external client app or connected app is set up with a `refresh_token` scope. |
| `signature` | Yes. | Base64-encoded HMAC-SHA256 signature signed with the `client_secret`. The signature can include the concatenated ID and `issued_at` value, which you can use to verify that the identity URL hasn’t changed since the server sent it. |
| `sfdc_community_url` | Yes. | The URL of the Experience Cloud site. |
| `sfdc_community_id` | Yes. | The user’s Experience Cloud site ID. |
| `state` | No. | The state requested by the client. This value is included only if the `state` parameter is included in the original query string. |
| `token_type` | Yes. | A `Bearer` token type, which is used for all responses that include an access token. |

## Step 13: App Creates the User’s Session

Your app receives the access token response, processes it, and creates the user’s session.

## Step 14: User Is Logged In and Performs an Action in the App

The end user is now logged in, and they perform an action in your app that requires access to Salesforce data. For example, they click a button to view their order history, which is stored in Salesforce.

![Note](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-xcloud-identity-264-0-0-production-enus/699dab6f-8f1a-4662-b24f-6521d5b747f7/images/icon_note.png)

Note When you configured your external client app, you set the Permitted Users policy to **Admin-approved users are pre-authorized** and configure which profiles or permission sets can access the app. With this policy, users access the app without authorizing it, so they don’t get an authorization screen prompting them to let the app access their data.

## Step 15: App Makes an Authenticated Call to a Salesforce Endpoint

To access the user’s Salesforce data, your app uses the access token to make an authenticated call to a protected Salesforce endpoint, such as a Salesforce API.

## Step 16: User Can Access Salesforce Data

The user can now access protected Salesforce data in your app. For example, they can see their order history. From the user’s perspective, the entire process from logging in to accessing their data happened without ever requiring them to leave the app.