4

I know others have asked this question.  The previous solutions have not worked.  Using the OAuth JWT flow.

  1.  All Users Can Authorize is selected
  2.  User authorized via https://login.salesforce.com/services/oauth2/authorize?client_id=&redirect_uri=&response_type=code
  3.  OAuth Scopes include
    Access and manage your data (api)
    Perform requests on your behalf at any time (refresh_token, offline_access)

POST to test.salesforce.com OR login.salesforce.com return {"error":"invalid_grant","error_description":"user hasn't approved this consumer"}
Why am I receiving this error?

If I change the aud of my JWT from https://login.salesforce.com to https://XXX.force.com, I receive
{"error":"invalid_grant","error_description":"audience is invalid"} which is the standard error response for an invalid token and is what I would expect.  I take that to mean my certificates are working correctly since I only gives the error when I change audience to an invalid value.

What am I missing? Why am I getting the user hasn't approved this consumer error?

4

From the Salesforce OAuth JWT Flow documentation (hidden):

A JWT OAuth 2.0 bearer assertion request looks at all the previous approvals for the user that include a refresh_token. If matching approvals are found, the values of the approved scopes are combined and an access_token is issued (with "token_type" value "Bearer"). If no previous approvals included a refresh_token, no approved scopes are available, and the request fails as unauthorized.

The previously attempted authorization URL used the web server flow. The server flow does NOT create a refresh token.
Incorrect: https://login.salesforce.com/services/oauth2/authorize?client_id=&redirect_uri=&response_type=code

I created a one-time curl request using the User Agent Flow:
Correct:
https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=<your_client_id>&redirect_uri=<your_redirect_uri>

The User Agent Flow request set a refresh_token and everything worked smoothly from there, allowing me to use the JWT Flow for all future requests.

1
  • For either the sub or prn value, make sure you are using a username that exists in that org.
  • For the aud make sure you are using test.salesforce.com for a sandbox environment and login.salesforce.com for production.

The issuer (iss) must be the OAuth client_id or the connected app for which the developer registered their certificate.

String iss = INT_USER_SETTINGS.Consumer_Key__c;

The audience (aud) must be https://login.salesforce.com or https://test.salesforce.com

String aud = getHostname();

The subject (sub) must be the username of the desired Salesforce user. For backward compatibility, you can use principal (prn) instead of subject (sub).

String prn = INT_USER_SETTINGS.Username__c;

The validity (exp) must be the expiration time of the assertion within 5 minutes, expressed as the number of seconds from 1970-01-01T0:0:0Z measured in UTC.

String exp = String.valueOf(System.currentTimeMillis() / 1000);

Construct a JSON Claims Set for the JWT with iss, sub, aud, and exp.

String bodyJson =  JSON.serialize(new Body(iss, prn, aud, exp));
etc..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.