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. 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.
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.
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.
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.
1import express from 'express';2import crypto from 'crypto';34const app = express();56// --- Configuration --------------------------------------------------------7const LOGIN_URL = 'https://<your-salesforce-org>'; // e.g. https://login.salesforce.com or your My Domain8const CLIENT_ID = '<client_id_from_your_External_Client_App>';9const CLIENT_SECRET = '<client_secret_from_your_External_Client_App>'; // optional — only set if "Require Secret for Web Server Flow" is enabled10const REDIRECT_URI = 'https://<your-app-host>/getAccessToken'; // must match the External Client App's callback URL1112// --- PKCE helpers (RFC 7636, S256) ----------------------------------------13// Only needed if the ECA has "Require Proof Key for Code Exchange (PKCE)14// extension for Supported Authorization Flows" enabled. Remove these lines if your ECA 15// doesn't require PKCE.16function generatePkce(){17 const codeVerifier = crypto.randomBytes(96).toString('base64url');18 const codeChallenge = crypto.createHash('sha256')19 .update(codeVerifier)20 .digest()21 .toString('base64url');22 return{codeVerifier, codeChallenge};23}2425// --- Step 1: Redirect the user to Salesforce's authorization endpoint -----26app.get('/oauth2/auth', (req, res)=>{27 const params = new URLSearchParams({28 response_type: 'code',29 client_id: CLIENT_ID,30 redirect_uri: REDIRECT_URI,31 scope: 'api refresh_token lightning web'32});3334 // Add PKCE parameters only if the ECA's "Require Proof Key for Code Exchange" policy is enabled. 35 // Remove these lines if your ECA doesn't require PKCE.36 const{codeVerifier, codeChallenge} = generatePkce();37 req.session.codeVerifier = codeVerifier; // keep verifier server-side for Step 238 params.append('code_challenge', codeChallenge);39 params.append('code_challenge_method', 'S256');4041 res.redirect(`${LOGIN_URL}/services/oauth2/authorize?${params.toString()}`);42});4344// --- Step 2: Exchange the authorization code for an access token ----------45app.get('/getAccessToken', async(req, res)=>{46 const{code} = req.query;47 if(!code){48 return res.status(400).json({error: 'Missing authorization code'});49}5051 const params = new URLSearchParams({52 grant_type: 'authorization_code',53 client_id: CLIENT_ID,54 redirect_uri: REDIRECT_URI,55 code: String(code)56});5758 // Send the PKCE verifier only when PKCE was used in Step 1.59 if(req.session.codeVerifier){60 params.append('code_verifier', String(req.session.codeVerifier));61}6263 // Send client_secret only when the ECA has "Require Secret for Web Server Flow" enabled64 // (for example, managed ECAs with enhanced security settings).65 if(CLIENT_SECRET){66 params.append('client_secret', CLIENT_SECRET);67}6869 const tokenResponse = await fetch(`${LOGIN_URL}/services/oauth2/token`, {70 method: 'POST',71 headers:{'Content-Type': 'application/x-www-form-urlencoded'},72 body: params.toString()73});7475 if(!tokenResponse.ok){76 return res.status(500).json({error: 'Token exchange failed', details: await tokenResponse.text()});77}7879 const{access_token, instance_url} = await tokenResponse.json();80 // Use access_token + instance_url to generate the frontdoor URL (see next section).81 res.json({access_token, instance_url});82});
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.
Note
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.
Frontdoor URLs are short-lived. For session refresh, you must generate a new frontdoor URL.
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.
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.