# Generate the Access Token and Frontdoor URL for Tableau Next Embedding

To complete the authentication process, you must generate an access token and a frontdoor URL. Use the generated Frontdoor URL as the `authCredential` value when you initialize the SDK.

## Generate the Access Token

To generate the access token, use the [OAuth 2.0 Web Server Flow](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_web_server_flow.htm). The web server flow is a two-step flow for obtaining an access token. Step 1 is browser-driven, where the user redirects to Salesforce and step 2 is server-side, where your backend exchanges the code.

1. Authorization request: Your app redirects the user to the Salesforce authorization endpoint, `/services/oauth2/authorize`. After the user authenticates, Salesforce redirects back to the configured callback URL with an authorization code in the query string.
2. Token exchange: Your app sends an HTTP POST request to the Salesforce token endpoint, `/services/oauth2/token`, with the authorization code and the app's `client_id`. Salesforce responds with a JSON payload containing the `access_token` and `instance_url`.

Proof Key for Code Exchange (PKCE) is only required if your External Client App (ECA) has the OAuth security policy **Require Proof Key for Code Exchange (PKCE) extension for Supported Authorization Flows** enabled. When using this policy, your app must include a code challenge on the authorization request and code verifier on the token request. For more information, see [Use the OAuth 2.0 Proof Key for Code Exchange (PKCE) Extension](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_pkce.htm).

If your ECA uses the OAuth security policy **Require Secret for Web Server Flow**, the POST request to the token endpoint must also include your app's `client_secret` as a form parameter alongside the `client_id`. If you're not using this policy, sending the `client_id` alone is sufficient.

This code example shows how to append the `client_secret` value conditionally to the request body. In this example, the host references your Salesforce org and `CLIENT_ID` and `CLIENT_SECRET` are the values you copied and saved from the ECA OAuth settings. Remember, the consumer key is the `client_id` and the consumer secret the `client_secret`.

```javascript
import express from "express";
import crypto from "crypto";

const app = express();

// --- Configuration --------------------------------------------------------
const LOGIN_URL = "https://<your-salesforce-org>"; // e.g. https://login.salesforce.com or your My Domain
const CLIENT_ID = "<client_id_from_your_External_Client_App>";
const CLIENT_SECRET = "<client_secret_from_your_External_Client_App>"; // optional — only set if "Require Secret for Web Server Flow" is enabled
const REDIRECT_URI = "https://<your-app-host>/getAccessToken"; // must match the External Client App's callback URL

// --- PKCE helpers (RFC 7636, S256) ----------------------------------------
// Only needed if the ECA has "Require Proof Key for Code Exchange (PKCE)
// extension for Supported Authorization Flows" enabled. Remove these lines if your ECA
// doesn't require PKCE.
function generatePkce() {
  const codeVerifier = crypto.randomBytes(96).toString("base64url");
  const codeChallenge = crypto
    .createHash("sha256")
    .update(codeVerifier)
    .digest()
    .toString("base64url");
  return { codeVerifier, codeChallenge };
}

// --- Step 1: Redirect the user to Salesforce's authorization endpoint -----
app.get("/oauth2/auth", (req, res) => {
  const params = new URLSearchParams({
    response_type: "code",
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    scope: "api refresh_token lightning web",
  });

  // Add PKCE parameters only if the ECA's "Require Proof Key for Code Exchange" policy is enabled.
  // Remove these lines if your ECA doesn't require PKCE.
  const { codeVerifier, codeChallenge } = generatePkce();
  req.session.codeVerifier = codeVerifier; // keep verifier server-side for Step 2
  params.append("code_challenge", codeChallenge);
  params.append("code_challenge_method", "S256");

  res.redirect(`${LOGIN_URL}/services/oauth2/authorize?${params.toString()}`);
});

// --- Step 2: Exchange the authorization code for an access token ----------
app.get("/getAccessToken", async (req, res) => {
  const { code } = req.query;
  if (!code) {
    return res.status(400).json({ error: "Missing authorization code" });
  }

  const params = new URLSearchParams({
    grant_type: "authorization_code",
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    code: String(code),
  });

  // Send the PKCE verifier only when PKCE was used in Step 1.
  if (req.session.codeVerifier) {
    params.append("code_verifier", String(req.session.codeVerifier));
  }

  // Send client_secret only when the ECA has "Require Secret for Web Server Flow" enabled
  // (for example, managed ECAs with enhanced security settings).
  if (CLIENT_SECRET) {
    params.append("client_secret", CLIENT_SECRET);
  }

  const tokenResponse = await fetch(`${LOGIN_URL}/services/oauth2/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: params.toString(),
  });

  if (!tokenResponse.ok) {
    return res
      .status(500)
      .json({ error: "Token exchange failed", details: await tokenResponse.text() });
  }

  const { access_token, instance_url } = await tokenResponse.json();
  // Use access_token + instance_url to generate the frontdoor URL (see next section).
  res.json({ access_token, instance_url });
});
```

:::note
This example is JavaScript and runs on Node.js with Express. You can implement the same flow in any server-side language, substituting the equivalent HTTP and crypto primitives in your stack of choice. For example, use Java with Spring Boot, Python with Flask or FastAPI, Go with net/http, Ruby on Rails, or .NET / C#. The endpoint paths, parameters, and request and response shapes are identical regardless of language.
:::

## Generate a Frontdoor URL for Embedding

Use Frontdoor URLs to bridge into UI sessions, giving your users uninterrupted access to Salesforce and other apps. The Frontdoor URL uses an existing session to log users into a new UI automatically without making them enter their credentials again. For Tableau Next embedding, only the embedded components need and use the frontdoor URL.

:::note
Frontdoor URLs are short-lived. For session refresh, you must generate a new frontdoor URL.
:::

### Example: Generate a Frontdoor URL in JavaScript

```javascript
const getFrontdoorUrl = async (accessToken: string, instanceUrl: string) => {
  try {
    const frontdoorEndpoint = `${instanceUrl}/services/oauth2/singleaccess`;

    const myHeaders = new Headers();
    myHeaders.append("accept", "application/json");
    myHeaders.append("authorization", `Bearer ${accessToken}`);
    myHeaders.append("content-type", "application/x-www-form-urlencoded");

    const urlencoded = new URLSearchParams();

    const requestOptions = {
      method: "POST",
      headers: myHeaders,
      body: urlencoded,
    };

    const response = await fetch(frontdoorEndpoint, requestOptions);
    if (!response.ok) {
      return {
        error: `Salesforce API responded with ${response.status}`,
        statusCode: response.status,
      };
    }
    const responseData = await response.json();
    return { frontdoorUrl: responseData.frontdoor_uri };
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error("Error in getFrontdoorUrl:", error);
    return { error: error.message || "Failed to generate frontdoor URL" };
  }
};
```

For information on how to use the UI Bridge API to generate frontdoor URLs, see [Generate a frontdoor URL to Bridge into UI Sessions](https://help.salesforce.com/s/articleView?id=xcloud.frontdoor_singleaccess.htm\&type=5).

## Authentication Management

- Don't hard code OAuth tokens or frontdoor URLs in your client-side code.
- Pass credentials to the browser only when strictly necessary. Malicious actors can scrape credentials from the browser.

## Session Management and Logout

The SDK provides a `logout()` method to terminate the Salesforce session.

:::important
Using the `logout()` method logs out all other Salesforce sessions running in the same browser. Consider this impact as you design your user logout flow.
:::

For more information, see [logout()](/docs//analytics/sdk/references/sdk-js-v2/logout.md).
