---
schema_version: "1"
id: 04a60799138dd9178b70b907483255838aff0d22939fb0c4c67f67e3b9a76ea1
title: Generate the Access Token and Frontdoor URL for Tableau Next Embedding
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/analytics/sdk/guides/sdk-access-token.md
canonical_url: https://developer.salesforce.com/docs/analytics/sdk/guide/sdk-access-token.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: fe5999b3c202c070b1ac6d1c98dd6925aa3acafdabd9019f0f7e3651aec3fc68
products:
  - Tableau Next Embedding SDK
  - analytics
---
# 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.

These code examples show detailed steps for authorization. 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`.

**note:** These examles are JavaScript and run 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.

### Shared setup example - the session is required, keeping the token server-side

```javascript
import express from 'express';
import session from 'express-session';
import crypto from 'node:crypto';

const { LOGIN_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI, APP_ORIGIN, SESSION_SECRET } = process.env;

const app = express();
app.use(session({
  secret: SESSION_SECRET,
  resave: false,
  saveUninitialized: true,
  cookie: { httpOnly: true, sameSite: 'lax' },
}));

// --- PKCE helpers ---
const base64url = (buf) =>
  buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

const generatePkce = () => {
  const verifier = base64url(crypto.randomBytes(64));
  const challenge = base64url(crypto.createHash('sha256').update(verifier).digest());
  return { verifier, challenge };
};
```

### Step 1: Authorization request example
```javascript
app.get('/login', (req, res) => {
  const { verifier, challenge } = generatePkce();
  req.session.codeVerifier = verifier;

  // Per-session `state` for CSRF / login-mix-up protection. PKCE secures the code
  // exchange; `state` is a separate defense on the callback — use both.
  const state = base64url(crypto.randomBytes(32));
  req.session.oauthState = state;

  const params = new URLSearchParams({
    response_type: 'code',
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    scope: 'api refresh_token lightning web',
    code_challenge: challenge,
    code_challenge_method: 'S256',
    state,
  });
  res.redirect(`${LOGIN_URL}/services/oauth2/authorize?${params}`);
});
```

### Step 2: Token exchange example, keeping the token server-side
```javascript
aapp.get('/getAccessToken', async (req, res) => {
  const { code, state, error, error_description } = req.query;
  if (error) return res.status(400).send(`OAuth error: ${error_description || error}`);

  // Reject the callback unless `state` matches the value stored in Step 1.
  if (!state || state !== req.session.oauthState) {
    return res.status(400).send('Invalid or missing OAuth state');
  }
  delete req.session.oauthState; // single-use

  const { codeVerifier } = req.session;
  if (!code || !codeVerifier) return res.status(400).send('Missing code or PKCE verifier');

  const params = new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    code: String(code),
    code_verifier: String(codeVerifier),
  });
  if (CLIENT_SECRET) params.append('client_secret', CLIENT_SECRET);

  const response = await fetch(`${LOGIN_URL}/services/oauth2/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: params.toString(),
  });
  const data = await response.json();
  if (!response.ok || data.error) {
    return res
      .status(response.status || 500)
      .send(`Token exchange failed: ${data.error_description || data.error}`);
  }

  // Keep credentials server-side. Do NOT send them to the browser.
  req.session.accessToken = data.access_token;
  req.session.instanceUrl = data.instance_url;
  req.session.refreshToken = data.refresh_token; // present if the refresh_token scope was granted

  res.redirect(APP_ORIGIN); // send the user back to your app; it then calls /getFrontdoorURL
});
```
## 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.

### Generate the Frontdoor URL example
```javascript
app.get('/getFrontdoorURL', async (req, res) => {
  if (!req.session.accessToken || !req.session.instanceUrl) {
    return res.status(401).json({ error: 'Not authenticated. Visit /login first.' });
  }

  const response = await fetch(`${req.session.instanceUrl}/services/oauth2/singleaccess`, {
    method: 'POST',
    headers: {
      accept: 'application/json',
      authorization: `Bearer ${req.session.accessToken}`,
      'content-type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams(),
  });
  if (!response.ok) {
    return res
      .status(response.status)
      .json({ error: `Could not generate FrontDoor URL (${response.status}). Re-authenticate at /login.` });
  }

  const data = await response.json();
  // The ONLY value that reaches the browser. Pass it to the SDK as `authCredential`.
  res.json({ frontdoorUrl: data.frontdoor_uri });
});
```

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:** Calling `logout()` tears down the SDK-managed embedded session(s), for the orgs you pass in, or all SDK-initialized orgs if you call it with no arguments. It doesn't sign the user out of unrelated Salesforce sessions in the same browser.

For more information, see [logout()](/docs//analytics/sdk/references/sdk-js-v2/logout.html).
