Oauth2 with Salesforce

Yes, that is correct. Do we have an example that shows how the access and refresh tokens are stored? I’m obviously doing something wrong.

Default the access_token and refresh_token are stored in a session. The authorize step returns the access_token and refresh_token, you can use those to store them in your database after a login. In the oauth provider you can set the Token Handling to Self Maintain. Then you are able to tell the provider where to get the tokens from.

Thanks @patrick. I can’t transfer in my mind what you just explained into wappler steps in an api, I’ll do some research in the tutorials and community and when I figure it out, i will post a screenshot, I’m sure it’ll help others that have never done this before.

If anyone has a screenshot of what they have done for the above, it would help massively.

This is my api steps - is there something wrong or else that I should be or not be doing? From what I see in a few examples here and there, the session handling seems to be automated by Wappler and seems to work out of the box? Since my authorize step is failing, I can’t retrieve the access_token and retrieve_token in their sessions, to store them and use them in a self-maintaining mode.

Isn’t it still something failing where wappler doesn’t handle the situation when the app is already authorized? From the tutorials I’ve seen the authorize step is just put after the OAuth provider, which is just what I’ve done. @patrick

The authorize step is your login, you only need to do that once and use the access token you’ve gotten for each api request, when the access token is expired you can refresh it, no need to login again. I believe with the new global oauth provider it is problematic to use the self maintaining mode since you can’t have a database query before the global provider and I noticed that it wasn’t possible to create the oauth provider locally in an action. Some providers (seems salesforce is one of them) give an error when you try to authorize (login) while you are already authorized with them (logged in).

I see - should this be a feature request or a bug then?

Hi @patrick

In order to progress my project whilst you evaluate the situation with the globals, etc… I’ve quickly put together my own SFDC module, with a user-password flow, as I didn’t want to try and tackle with the wappler code.
I’m succesfully retrieving an access_token everytime, this is the code I have used:

 namespace modules;

use \lib\core\Module;

class sfdc extends Module
{
  public function password_oauth($options, $name) {
$is_authorized = isset($_SESSION['access_token']);

if (!$is_authorized)
{
   
$loginurl = $this->app->parseObject($options->tokenEndpoint);	
$params = "grant_type=password"
. "&client_id=" . $this->app->parseObject($options->clientID)
. "&client_secret=" . $this->app->parseObject($options->clientSecret)
. "&username=" . $this->app->parseObject($options->Username)
. "&password=" . $this->app->parseObject($options->Password);

$curl = curl_init($loginurl);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $params);

$json_response = curl_exec($curl);

$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

if ( $status != 200 ) {
    die("Error: call to URL failed with status $status, response $json_response, curl_error " . curl_error($curl) . ", curl_errno " . curl_errno($curl));
}

curl_close($curl);

echo $json_response;
 $token_request_data = json_decode($json_response, true);
 if (empty($token_request_data))
        die("Couldn't decode '$token_request_data' as a JSON object");

    if (!isset($token_request_data['access_token'])||
        !isset($token_request_data['instance_url']))
        die("Missing expected data from ".print_r($token_request_data, true));

    // Save off the values we need for future use
    $_SESSION['access_token'] = $token_request_data['access_token'];
    $_SESSION['instance_url'] = $token_request_data['instance_url'];

    }//end !is_authorized
  }
}

Ideally, I would now want to have this step as being the authorization model for the API step:

So that instead of Authorization: OAuth2 , I can actually select my custom step and pass on the access token so that the API action uses it as required.

Is this something that can be done?

Instead of using the OAuth2 with the api step you can pass the access token directly in the headers.

Authorization: Bearer {{access_token}}

I will discus tomorrow with George on how we can improve the current OAuth2 implementation. We had a same issue with the mailer where a user wanted to use settings from the database but it wasn’t possible with the new global providers. So we probably will be allowing both global an inline providers.

1 Like

Thanks @patrick, I think that worked - I’ll need to make more tests, but this looks promising.
Thanks for your help all along.

PS.: I am the user who reported the issue with the global settings and the mailer - sorry to be a pain in the butt!

2 Likes

I’m aware that you sometimes you have some settings in the database that you require for one of the providers, with the new global providers this is not possible anymore. So the current solution may be better in many situation, there are situations where the old way was better (when the provider requires data from an other step). We have to look for a solution that satisfies both.

Hello @patrick, I am picking up on the limitation you mentioned caused by global providers. I have been researching how to set up an Oauth2 API for which my app needs to use ‘Self maintain’ Token Handling. I have seen from this thread that you say this is no longer possible with the implementation of the new global providers.

I was following a forum thread discussing an app with the same API where it was concluded that ‘Self maintain’ (as opposed to ‘Session’) was necessary. That app was developed last year before global providers were introduced and the oauth provider was created in a local action after a database query. I will have to pause my development until ‘Self maintain’ is possible.

Are you able to say whether a solution to this is likely to be introduced or when it might be scheduled in a software release?

If it helps I can post this issue as a bug or perhaps it ties in with the feature request, Option for Globals at end of API execution, as an additional use case to Ken’s request.

Hi @patrick,
I think I’ve found what the issue was, and it’s probably specific to Salesforce, as I had to change my own code accordingly: SFDC doesn’t honor sessions, so when you are checking if the session “access_token” is still open to consider your user logged in, in the majority of cases, SFDC considers that you aren’t and expects you to authenticate again. The access token generated is the same, but it includes a different signature - and in this case you get a session expired error.
My solution with my own module was to actually login every single time that I perform an API call, and that works well, I understand that the authorize steps only refreshes the session when it thinks it expires, not systematically.

I’m assuming this is an oddity of SalesForce (one of many), I don’t know if it warrants an option to override sessions (since the self maintain option is not possible with the globals model).