jsforce icon indicating copy to clipboard operation
jsforce copied to clipboard

How to get State in the OAuth2 authorization callback?

Open bpetty-formfast opened this issue 6 years ago • 4 comments

I am passing in a state property into OAuth2.getAuthorizationUrl(...). The documentation does not detail how to get that state back when the Callback is performed. How does one access that stored state value? I need it so that in the callback I can redirect to the correct app once I get an id token from Salesforce.

bpetty-formfast avatar Feb 03 '20 23:02 bpetty-formfast

SFDC adds it as a query parameter in the callback.

Assuming you are using express your route is configured as /callback

oauthRoutes.get('/callback', async (req: Request, res: Response) => {
    const environment = req.query.state; // SFDC sets the scope as a query parameter
    const conn = new jsforce.Connection({ oauth2: getOAth2(environment) });
    
    conn.authorize(code, function(err, userInfo) {
    // .... remaining logic
}

paustint avatar Feb 04 '20 00:02 paustint

Hey, I'm having an issue where my state parameter is being passed within the callback url and then im receiving an uri mismatch error from salesforce. I havent been able to figure out work around because I need the state data inside the callback end point to do some other processes. Any suggestions?

jaagaard01 avatar Oct 15 '21 22:10 jaagaard01

@jaagaard01 - here is how I am doing it with https://getjetstream.app/

If you are getting uri mismatch, it is probably a mis-configuration with the connected app in Salesforce compared to what you are providing in redirectUri. The redirectUri must be explicitly configured in the connected app including the port and match exactly.

Happy to help you troubleshoot more if you get stuck, I usually have the sdfx discord app open @AustinT - you can ping me there.

// Simple helper method just to init the oauth connection
export function getJsforceOauth2(loginUrl: string) {
  return new jsforce.OAuth2({
    loginUrl,
    clientId: ENV.SFDC_CONSUMER_KEY,
    clientSecret: ENV.SFDC_CONSUMER_SECRET,
    redirectUri: ENV.SFDC_CALLBACK_URL,
  });
}

/** HANDLE REDIRECT TO SFDC FOR AUTH */
export function salesforceOauthInitAuth(req: express.Request, res: express.Response) {
  const loginUrl = req.query.loginUrl as string;
  const clientUrl = req.query.clientUrl as string;
  const replaceOrgUniqueId = req.query.replaceOrgUniqueId as string | undefined;
  const state = querystring.stringify({ loginUrl, clientUrl, replaceOrgUniqueId });

  let options = {
    scope: 'full refresh_token',
    state,
    prompt: 'login',
  };

  if (req.query.username) {
    options = Object.assign(options, { login_hint: req.query.username });
  }

  res.redirect(getJsforceOauth2(loginUrl).getAuthorizationUrl(options));
}

/** HANDLE RESPONSE FROM SFDC */
export async function salesforceOauthCallback(req: express.Request, res: express.Response) {
  try {
    const user = req.user as UserProfileServer;
    const state = querystring.parse(req.query.state as string);
    const loginUrl = state.loginUrl as string;
    const clientUrl = state.clientUrl as string;
    const replaceOrgUniqueId = state.replaceOrgUniqueId as string | undefined;

    // ERROR PATH
    if (req.query.error) {
      const errorMsg = req.query.error_description ? req.query.error_description : 'There was an error authenticating Salesforce.';
      const errorObj = { message: errorMsg, error: req.query.error };
      return res.redirect(`/assets/oauth?${querystring.stringify(errorObj as any)}`);
    }

    const conn = new jsforce.Connection({ oauth2: getJsforceOauth2(loginUrl as string) });

// ..... TRUNCATED //

paustint avatar Oct 16 '21 02:10 paustint

@paustint awesome this helps me a lot thank you!

jaagaard01 avatar Oct 18 '21 18:10 jaagaard01