---
schema_version: "1"
id: eb5a51a770882b1432c07bb966216fb815d476ca1b93eaa07916b18ef394f41f
title: "Headless Identity APIs: Authorization Code and Credentials Flow for Public Clients"
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/xcloud/264-0-0/remoteaccess_authcodecreds_singlepageapp.md
canonical_url: https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_authcodecreds_singlepageapp.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: 246934c230b8d30b0641d83f3e3637f5120f1a6d129dbbd825071240cb7b58be
description: For public clients such as single-page apps or mobile apps, you can set up headless login for customers and partners by using the Authorization Code and Credentials Flow. This flow is built on the OAuth 2.0 Authorization Code grant type. With the Authorization Code and Credentials Flow, you control the front-end login experience in a third-party app. You call Salesforce Headless Login APIs via your Experience Cloud site to handle the back-end work of authenticating users and granting access to protected Salesforce resources. With separate front-end and back-end processes, your users can log in and access Salesforce data without leaving your app. For single-page apps, you use a server-side callback endpoint to extract the authorization code, and you perform the code exchange from the browser via client-side JavaScript.
products:
  - Security
  - Cross Cloud
release: "264"
---
# Headless Identity APIs: Authorization Code and Credentials Flow for Public Clients

For public clients such as single-page apps or mobile apps, you can set up headless login for customers and partners by using the Authorization Code and Credentials Flow. This flow is built on the OAuth 2.0 Authorization Code grant type. With the Authorization Code and Credentials Flow, you control the front-end login experience in a third-party app. You call Salesforce Headless Login APIs via your Experience Cloud site to handle the back-end work of authenticating users and granting access to protected Salesforce resources. With separate front-end and back-end processes, your users can log in and access Salesforce data without leaving your app. For single-page apps, you use a server-side callback endpoint to extract the authorization code, and you perform the code exchange from the browser via client-side JavaScript.

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

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

Warning For security, we strongly recommend that you always use the client-server variation of this flow whenever you can. The client-server variation gives you extra protection for the consumer secret during the code exchange. For more information, see [Headless Identity APIs: Authorization Code and Credentials Flow for Private Clients](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_authorization_code_credentials_flow.htm&release=264.0.0&type=5 "For private clients, such as client-server apps, you can set up headless login for customers and partners by using the Authorization Code and Credentials Flow, which is built on the OAuth 2.0 Authorization Code grant type.").

![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 Here, third-party app refers to any app outside of Salesforce.

This help content describes how to set up the flow and how it works. To set up an end-to-end example implementation, see the Headless Identity Implementation Guide.

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.").
- Integrate your off-platform app with Salesforce using one of these options.
    - [Configure an External Client App for the Authorization Code and Credentials Flow](https://help.salesforce.com/s/articleView?id=xcloud.eca_authorization_code_credentials.htm&type=5&language=en_US)
    - [Configure a Connected App for the Authorization Code and Credentials Flow](https://help.salesforce.com/s/articleView?id=xcloud.authorization_code_credentials_configure.htm&release=264.0.0&type=5 "The Authorization Code and Credentials Flow is the foundation of headless login, registration, passwordless login, and guest user identity. Before setting up these features, enable the Authorization Code and Credentials Flow at an org-wide level and configure required settings and access policies for your connected app.")

Because you manage Salesforce Customer Identity through Experience Cloud sites, you can configure the Authorization Code and Credentials Flow only for customers and partners using an Experience Cloud site subdomain such as `https://MyExperienceCloudSite.my.site.com`. You can’t set up this flow for employees accessing the Salesforce platform with login.salesforce.com or your org-specific My Domain login URL, or for employees who access Experience Cloud sites.

Here’s an example use case for the Authorization Code and Credentials Flow. You work for a travel company that stores customer data in Salesforce. You created a custom single-page app, and you want users to have access to their past travel bookings from your app. You also want full control over the login experience so that you can align with your company’s branding. So you configure your custom app as an external client app or connected app and set up the Authorization Code and Credentials Flow.

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 a simplified overview of the flow in action.

![Diagram showing Authorization Code and Credentials Flow for single-page apps](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-xcloud-identity-264-0-0-production-enus/699dab6f-8f1a-4662-b24f-6521d5b747f7/identity/images/authcodecreds_singlepageapp.png)

- Your user goes to your custom app, where your login form is natively displayed within the app, and enters their username and password. 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.
- If you’re using the Proof Key for Code Exchange (PKCE) extension, the app generates values to verify the authorization code. If you’re not using PKCE, your flow skips this step. We strongly recommend that you always use PKCE when implementing this flow for single-page apps.
- From the browser, your custom app—via JavaScript—sends a headless authorization request to the Salesforce Headless Login API authorization endpoint on your Experience Cloud site.
- 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.
- Salesforce Headless Login API validates the user credentials and returns an HTTP 302 redirect to a preconfigured URL containing the authorization code. Salesforce then automatically sends the redirect response to the redirect URL, which points to the server-side callback handler.
- The server-side callback handler extracts the authorization code from the 302 redirect and returns it to the app.
- The client-side JavaScript receives the redirect URL parameters and initiates the code exchange with a POST request to the token endpoint.
- Salesforce Headless Login API validates the request and returns an access token response to the app.
- Client-side JavaScript on the app processes the access token and creates the user’s session.
- The user is now logged in, and they perform an action in your custom app that initiates a request for Salesforce data. For example, they click a button to access their travel booking history, which is stored in the Salesforce Experience Cloud site.
- Your custom app makes an authenticated request to a protected Salesforce endpoint, such as a Salesforce API.
- The user can now access their protected data in your custom app. For example, they can see their travel booking history.

A key component of this flow is the client-side JavaScript that sends the authorization request, performs the code exchange, and processes the access token. Here’s a JavaScript example that uses a connected app.

```javascript
var clientId = "<Connected App Client ID>";
var baseURL = "<Experience Cloud Domain>";
var redirectURL = "<Experience Cloud Domain>/services/apexrest/code/extraction"
      
// Performs the code exchange
function doCodeExchange(authorizeResponse) {
   //Perform Code Exchange
   //Get Access Token
   var client = new XMLHttpRequest();
   client.open("POST", authorizeResponse.sfdc_community_url + "/services/oauth2/token", true);
   client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   client.send("code=" + authorizeResponse.code + "&grant_type=authorization_code&client_id=" + clientId + "&redirect_uri=" + redirectURL);
   client.onreadystatechange = function() {
       if(this.readyState == 3) {
           response = JSON.parse(client.response);
           getUserInfo(response.access_token, response.sfdc_community_url)
       }
   }
}

// Gets User Info
function getUserInfo(accessToken, userInfoBaseURL) {
   var client = new XMLHttpRequest();
   client.open("GET", userInfoBaseURL + "/services/oauth2/userinfo", true);
   client.setRequestHeader("Content-Type", "application/json");
   client.setRequestHeader("Authorization", "Bearer " + accessToken);
   client.send();
   client.onreadystatechange = function() {
       if(this.readyState == 3) {
           response = JSON.parse(client.response);
           response.access_token = accessToken;
           document.getElementById("json").textContent = JSON.stringify(response, undefined, 2);
           document.getElementById("results").style.display="block";
       }
   }
}

//Starts the Login Process
function startLogin() {
   var username = document.getElementById('user_name').value;
   var password = document.getElementById('password').value;
  
   var encodedUNP = btoa(username + ':' + password);
   var client = new XMLHttpRequest();
  
   client.open("POST", baseURL + "/services/oauth2/authorize", true);
   client.setRequestHeader("Auth-Request-Type", "Named-User");
   client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   client.setRequestHeader("Authorization", "Basic " + encodedUNP);
   client.send("response_type=code_credentials&client_id=" + clientId + "&redirect_uri=" + redirectURL);
   client.onreadystatechange = function() {
       if(this.readyState == 3) {
           response = JSON.parse(client.response);
           doCodeExchange(response);
       }
   }
   return false;
}    
```

Another key component is the server-side callback handler that extracts the code from the 302 redirect and returns it to the app. In this example, the handler is an Apex class exposed as a public REST endpoint, and is Cross Origin Resource Sharing (CORS) enabled to offer cross-site scripting protection. To simplify development, use the [OAuth 2.0 echo endpoint](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_echo_endpoint.htm&type=5&language=en_US) to get the authorization code.

```java
@RestResource(urlMapping='/code/extraction')
global class CodeExtractorAPI {
  
   @HttpGet
   global static CodeResponse doGet() {
       RestRequest req = RestContext.request;
       RestResponse res = RestContext.response;
       try {
           res.statusCode = 200;
           return new CodeResponse(req.params.get('code'), req.params.get('sfdc_community_url'), req.params.get('sfdc_community_id'), req.params.get('state'));
       } catch (Exception e) {
           res.statusCode = 500;
           return new CodeResponse('Could not parse auth code redirect URI');
       }
      
   }
  
   // Response Wrapper
   global class CodeResponse {
       String code;
       String sfdc_community_url;
       String sfdc_community_id;
       String state;
       Boolean success;
       String errMsg;
      
       public CodeResponse(String code, String sfdc_community_url, String sfdc_community_id, String state) {
           this.code = code;
           this.sfdc_community_url = sfdc_community_url;
           this.sfdc_community_id = sfdc_community_id;
           this.state = state;
           this.success = true;
       }
      
        public CodeResponse(String errMsg) {
           this.success = false;
           this.errMsg = this.errMsg;
       }       
   }
}
```

Here’s a detailed breakdown of this flow.

## End User Opens Third-Party App and Logs In

Your user opens your third-party app intending to log in. In your app, your login form appears showing username and password fields and a login button. Salesforce doesn’t provide this login form. Its look and feel are up to you. Your user enters their username and password and clicks the login button.

## Third-Party App Generates `code_verifier` and `code_challenge` Values (Optional)

If you’re using the Proof Key for Code Exchange (PKCE) extension, the app generates values used to verify the authorization code.

Your flow includes this step only if you’re using the Proof Key for Code Exchange (PKCE) extension. As a security best practice, we strongly recommend using the PKCE extension when you implement the Authorization Code and Credentials Flow, especially for single-page apps. 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`.

## Third-Party App Headlessly Requests an Authorization Code

From the browser, your custom app—via JavaScript—sends a headless authorization code request to the Salesforce Headless Login API authorization endpoint. If you aren't using headless user discovery, use either the GET or POST method for this request. If you are using headless user discovery, only POST requests are supported.

In this client-side JavaScript excerpt, the authorization request is sent as part of the `startLogin` function. After retrieving the user credentials, the function constructs and sends a POST request, including a redirect URL that points to the server-side callback handler.

```javascript
function startLogin() {
   var username = document.getElementById('user_name').value;
   var password = document.getElementById('password').value;
   var encodedUNP = btoa(username + ':' + password);
   var client = new XMLHttpRequest();
   client.open("POST", baseURL + "/services/oauth2/authorize", true);
   client.setRequestHeader("Auth-Request-Type", "Named-User");
   client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   client.setRequestHeader("Authorization", "Basic " + encodedUNP);
   client.send("response_type=code_credentials&client_id=" + clientId + "&redirect_uri=" + redirectURL);
   client.onreadystatechange = function() {
       if(this.readyState == 3) {
           console.log("here");
           console.log(client.response);
           response = JSON.parse(client.response);
           if (response.success) {
               getUserInfo(response.access_token, baseURL);
           }
       }
   }
   return false;
}    

```

For both GET and POST requests, you must include the header `Auth-Request-Type: Named-User`.

Depending on which method you use and your external client app or connected app configuration, you’re sometimes required to include an authorization header of type Basic containing the user’s credentials. If you’re using a GET request, you must send the credentials—the user’s username and password, appended to each other and Base64-encoded—in an authorization header. Here’s an example GET request.

```
GET /services/oauth2/authorize? HTTP 1.1
Host: MyDomainName.my.site.com
Auth-Request-Type: Named-User
Authorization: Basic <encoded username:password>

response_type=code_credentials&
redirect_uri=https://www.MyExperienceCloudSite.my.site.com/services/apexrest/code/extraction&
client_id=******&
code_challenge=Y29kZ*******
```

If you’re using a POST request, you can include the Base64-encoded user credentials in an authorization header, or you can put them in the request body.

If you're using headless user discovery, you don't send a username and password. Instead, you send an identifier in the `login_hint` parameter, optional custom data, and a password. Include the identifier, custom data, and password in the body of a POST request. Don't use a GET 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 UVID 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 UVID, 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.

If you implement the guest user flow on your app, you can optionally use this header to pass in a JSON Web Token (JWT)-based access token containing a unique visitor ID (UVID) tied to a guest user’s identity. By passing the UVID 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.

You can also include the plain UVID value in the request body.

![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 If you have the **Require user credentials in the POST body for Authorization Code and Credentials Flow** setting enabled on your external client app or connected app, you can send the user credentials only in the request body. With this setting enabled, you can’t use the GET method for the authorization code request. You can use only the POST method.

For both GET and POST methods, include these required parameters in the body of the authorization request.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e292">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e294">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e292 "><code>client_id</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e294 ">The consumer key of the external client app or connected app.</td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e292 "><code>redirect_uri</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e294 ">
              <p>The URL where users are redirected after a successful authentication. The redirect URI
                must match one of the values in the Callback URL field on the external client app or
                connected app. Otherwise, the approval fails.</p>
              <p>For public clients, the redirect URI must
                point to an endpoint that can process the 302 redirect from Salesforce. To simplify
                development, use the <a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_echo_endpoint.htm&amp;type=5&amp;language=en_US" target="_blank">OAuth echo endpoint</a>, such as
                <samp class="filepath">https://MyExperienceCloudSite.my.site.com/services/oauth2/echo</samp>.
              </p>
              <p style="margin-bottom:0;">These examples use an Apex REST code extraction endpoint. For example, <code>&lt;Experience Cloud
          Domain&gt;/services/apexrest/code/extraction</code>.</p>
            </td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e292 "><code>response_type</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e294 ">The OAuth 2.0 grant type that the app requests. For the Authorization Code and
              Credentials Flow, the value must <code>code_credentials</code>.</td>
          </tr>
        </tbody>
      </table>

You can also include these optional parameters in the authorization request.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e339">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e341">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e339 "><code>code_challenge</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e341 ">
              <p>Required if using the PKCE extension. 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 base64url-encoded
                as defined in <a href="https://tools.ietf.org/html/rfc4648#section-5" target="_blank"><samp class="filepath">https://tools.ietf.org/html/rfc4648#section-5</samp></a>. </p>
              <p>This
                parameter is required if a <code>code_verifier</code> is specified
                in the token request.</p>
              <ul>
                <li>If the <code>code_challenge</code> value is provided in the
                  authorization request and a <code>code_verifier</code> value is
                  provided in the token request, Salesforce compares the <code>code_challenge</code> to the <code>code_verifier</code>. If
                  the <code>code_challenge</code> is invalid or doesn’t match, the
                  login fails with the <code>invalid_request</code> error code.</li>
                <li>If the <code>code_challenge</code> value is provided in the
                  authorization request, but a <code>code_verifier</code> value
                  isn’t provided in the token request, the login fails with the <code>invalid_grant</code> error code.</li>
              </ul>
            </td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e339 "><code>scope</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e341 ">Permissions that define the type of protected resources the app can access. You assign
              scopes to an external client app or connected app when you build it, and they’re included
              with the OAuth tokens during the authorization flow. If you don’t include this parameter,
              all scopes assigned to the app are requested. To restrict the scopes further, pass a subset
              of the assigned scopes in this parameter. For valid parameters, see <a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_tokens_scopes.htm&amp;type=5&amp;language=en_US" target="_blank">OAuth Scopes</a>.</td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e339 "><code>state</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e341 ">Any state that the external web service requests to be sent to the callback URL. This
              value must be URL encoded.</td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e339 "><code>uvid_hint</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e341 ">
              <p>A plain <code>UVID</code> value, which is a Version 4 UUID
                that’s generated and managed entirely by your app. To get 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. You can
                optionally use this parameter to pass in a UVID value tied to a guest user’s identity,
                carrying contextual information from a guest user session into a named user
                session.</p>
              <p style="margin-bottom:0;">Instead of passing the UVID in the request body, you can also pass it in a
                JWT-based token with a UVID via the <code>UVID-Hint</code>
                header.</p>
            </td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e339 "><code>login_hint</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e341 ">Required if using headless user discovery. 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="d42407e339 "><code>customdata</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e341 ">
              <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;">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.</p>
            </td>
          </tr>
        </tbody>
      </table>

## (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).

## Salesforce Validates Credentials and Returns a 302 Redirect to Server-Side Callback Handler

Salesforce Headless Login API receives the authorization request. It validates the user credentials and returns an HTTP 302 redirect to a preconfigured URL containing the authorization code. Salesforce then automatically sends the redirect response to the redirect URL, which points to the server-side callback handler at the `/code/extraction` endpoint in this example. During this process, the browser isn’t redirected—everything happens behind the scenes.

## Server-Side Callback Handler Extracts Code and Returns It to App

The server-side callback handler extracts the authorization code and other data. The example Apex callback handler uses the `doGet` method to extract the code, Experience Cloud site URL, site ID, and state from the 302 redirect.

```java
@RestResource(urlMapping='/code/extraction')
global class CodeExtractorAPI {
  
   @HttpGet
   global static CodeResponse doGet() {
       RestRequest req = RestContext.request;
       RestResponse res = RestContext.response;
       try {
           res.statusCode = 200;
           return new CodeResponse(req.params.get('code'), req.params.get('sfdc_community_url'), req.params.get('sfdc_community_id'), req.params.get('state'));
       } catch (Exception e) {
           res.statusCode = 500;
           return new CodeResponse('Could not parse auth code redirect URI');
       }
      
   }
The response wrapper sets the variables for the code response, including success and error indicators.
// Response Wrapper
   global class CodeResponse {
       String code;
       String sfdc_community_url;
       String sfdc_community_id;
       String state;
       Boolean success;
       String errMsg;
      
       public CodeResponse(String code, String sfdc_community_url, String sfdc_community_id, String state) {
           this.code = code;
           this.sfdc_community_url = sfdc_community_url;
           this.sfdc_community_id = sfdc_community_id;
           this.state = state;
           this.success = true;
       }
      
        public CodeResponse(String errMsg) {
           this.success = false;
           this.errMsg = this.errMsg;
       }       
   }
```

Here’s another example of code extraction using PHP: Hypertext Preprocessor (PHP).

```
<?
header('Access-Control-Allow-Headers: *');
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json; charset=utf-8');
$out = [];
foreach ($_GET as $name => $value) {
   $out[$name] = $value;
}
echo json_encode($out);
?>
```

After extracting the code, the callback handler sends it back to the browser.

## App Receives Code Response and Performs Code Exchange

The browser receives the code response. Here’s an example of a successful response in the browser console log.

```html
{"success":true,"state":"https://MyExperienceCloudSite.my.site.com/","sfdc_community_url":"https://MyExperienceCloudSite.my.site.com/vforcesite","sfdc_community_id":"0DBxxxxxxxxxxxx","errMsg":null,"code":"aPrxC1*******"}
```

The browser headlessly requests to exchange the code for an access token. In the client-side JavaScript example, the `doCodeExchange` function sends the code in a POST request to the Experience Cloud token endpoint.

To avoid exposing the consumer secret to the browser, you must disable the Require Secret for Web Server Flow and Require Secret for Refresh Token Flow settings on your external client app or connected app. With these settings disabled, the consumer secret isn’t required in the authorization request. If possible, we recommend that you perform the code exchange using a server back end. See [Headless Identity APIs: Authorization Code and Credentials Flow for Private Clients](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_authorization_code_credentials_flow.htm&release=264.0.0&type=5 "For private clients, such as client-server apps, you can set up headless login for customers and partners by using the Authorization Code and Credentials Flow, which is built on the OAuth 2.0 Authorization Code grant type.").

```javascript
var clientId = "<Connected App Client ID>";
var baseURL = "<Experience Cloud Domain>";
var redirectURL = "<Experience Cloud Domain>/services/apexrest/code/extraction"
      
// Performs the code exchange
function doCodeExchange(authorizeResponse) {
   //Perform Code Exchange
   //Get Access Token
   var client = new XMLHttpRequest();
   client.open("POST", authorizeResponse.sfdc_community_url + "/services/oauth2/token", true);
   client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   client.send("code=" + authorizeResponse.code + "&grant_type=authorization_code&client_id=" + clientId + "&redirect_uri=" + redirectURL);
   client.onreadystatechange = function() {
       if(this.readyState == 3) {
           response = JSON.parse(client.response);
           getUserInfo(response.access_token, response.sfdc_community_url)
       }
   }
} 
```

For the access token request, you can use only a POST request—GET requests aren’t supported. You must include a `Content-Type` header. Include these required parameters in the request body.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e540">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e542">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e540 "><code>client_id</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e542 ">The consumer key of the external client app or connected app.</td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e540 "><code>code</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e542 ">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="d42407e540 "><code>grant_type</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e542 ">The type of validation that the app can provide to prove it’s a safe visitor. For the
              Authorization Code and Credentials 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="d42407e540 "><code>redirect_uri</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e542 ">
              <p>The URL where users are redirected after a successful authentication. The redirect URI
                must match one of the values in the Callback URL field on the external client app or
                connected app. Otherwise, the approval fails.</p>
              <p>For public clients, the redirect URI must
                point to an endpoint that can process the 302 redirect from Salesforce. To simplify
                development, use the <a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_echo_endpoint.htm&amp;type=5&amp;language=en_US" target="_blank">OAuth echo endpoint</a>, such as
                <samp class="filepath">https://MyExperienceCloudSite.my.site.com/services/oauth2/echo</samp>.
              </p>
              <p style="margin-bottom:0;">These examples use an Apex REST code extraction endpoint. For example, <code>&lt;Experience Cloud
          Domain&gt;/services/apexrest/code/extraction</code>.</p>
            </td>
          </tr>
        </tbody>
      </table>

You can also include these optional parameters in the token request.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e593">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e595">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e593 "><code>client_secret</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e595 "><span>The consumer secret of the external client app or connected app.</span></td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e593 "><code>code_verifier</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e595 ">
              <p>Required if using the PKCE extension. Specifies 128 bytes of random data with high
                entropy to make guessing the <code>code</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://tools.ietf.org/html/rfc4648#section-5" target="_blank"><samp class="filepath">https://tools.ietf.org/html/rfc4648#section-5</samp>.</a></p>
              <ul>
                <li>If the <samp class="parmname">code_verifier</samp> value is provided in the token request and a
                  <samp class="parmname">code_challenge</samp> value is in the authorization request, Salesforce
                  compares the <samp class="parmname">code_verifier</samp> to the
                  <samp class="parmname">code_challenge</samp>. If the <samp class="parmname">code_verifier</samp> is invalid
                  or doesn’t match, the login fails with the <code>invalid_grant</code> error code.
                </li>
                <li>If the <samp class="parmname">code_verifier</samp> value is provided in the token request, but a
                  <samp class="parmname">code_challenge</samp> value isn’t provided in the authorization request,
                  the login fails with the <code>invalid_grant</code> error
                  code.
                </li>
              </ul>
            </td>
          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e593 "><code>format</code></td>
            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e595 ">
              <p>The expected format of the response. Salesforce supports these formats.</p>
              <ul>
                <li><code>urlencoded</code></li>
                <li><code>json</code> (default)</li>
                <li><code>xml</code></li>
              </ul>
            </td>
          </tr>
        </tbody>
      </table>

## Salesforce Grants an Access Token

After validating the app’s credentials, Salesforce Headless Login API returns an access token to the browser. Here’s an example access token response in JSON format.

```json
{
"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 required parameters.

 
| Parameter | Description |
| --- | --- |
| `access_token` | OAuth token that an external client app or connected 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` | 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). |
| `instance_url` | A URL indicating the instance of the user’s org. For example: `https://yourInstance.salesforce.com/`. |
| `issued_at` | Time stamp of when the signature was created, expressed as the number of milliseconds from 1970-01-01T0:0:0Z UTC. |
| `signature` | 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` | The URL of the Experience Cloud site. |
| `sfdc_community_id` | The user’s Experience Cloud site ID. |
| `token_type` | A `Bearer` token type, which is used for all responses that include an access token. |

The access token response can also include these parameters.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col style="width:50%">
          <col style="width:50%">
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e846">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d42407e849">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e846 ">
              <code>id_token</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e849 ">
              <p>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 client app. See <a href="http://openid.net/specs/openid-connect-basic-1_0-28.html" target="_blank">OpenID Connect specifications</a>.</p>
              <p style="margin-bottom:0;">This parameter is returned
                if the <samp class="parmname">scope</samp> parameter includes <code>openid</code>.</p>
            </td>

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

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e849 ">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 <code>refresh_token</code> scope.</td>

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

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d42407e849 ">
              <span>The state requested by the client. This value is included only if the <code>state</code> parameter is included in the original query
                string.</span>
            </td>

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

## App Processes Token Response and Creates the User Session

The browser stores the information from the token response and creates the user session. At this point, the user is logged in, and the client-side JavaScript calls the Salesforce User Info endpoint to confirm that the login was successful, as shown in this excerpt.

![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 Ensure that you complete a full security review of your access token storage. Never store the access token in the browser’s local storage, and avoid storing it in a cookie if possible.

```javascript
// Gets User Info
function getUserInfo(accessToken, userInfoBaseURL) {
   var client = new XMLHttpRequest();
   client.open("GET", userInfoBaseURL + "/services/oauth2/userinfo", true);
   client.setRequestHeader("Content-Type", "application/json");
   client.setRequestHeader("Authorization", "Bearer " + accessToken);
   client.send();
   client.onreadystatechange = function() {
       if(this.readyState == 3) {
           response = JSON.parse(client.response);
           response.access_token = accessToken;
           document.getElementById("json").textContent = JSON.stringify(response, undefined, 2);
           document.getElementById("results").style.display="block";
       }
   }
}
```

## End User Is Logged in and Performs an Action in the App

Your user is now logged in. They perform an action in your app that requires access to Salesforce data. For example, they click a button to view their travel booking 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 set up your external client app or connected app for the Authorization Code and Credentials Flow, 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.

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

## End User Can Access Salesforce Data

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