Skip to content

Checkpoint challenge

Original, hands-on practice. Never modeled on actual exam questions. This closes the chapter OAuth: The Base Pattern, chapter 8 of Securing Salesforce: Identity and Access. If you’ve done Integration Architect’s client credentials lab, the setup will look familiar; this time the point is the scope.

A reporting tool needs to know which org and user it’s connected as, and nothing more. Someone set it up with a client credentials app and an integration user. Your job is to prove what its wristband can and can’t do, then widen it for a second job, and finally take it back.

You’ll see both checks at the stall from module 4: the scope, then the user.

  • An external client app with the client credentials flow, run as an integration user.
  • One narrow scope to start: the identity URL only (id).
  • A call the scope refuses, and the same call working once api is added.
  • The token revoked, and checked to show it’s no longer good.
  • A Developer Edition org from the earlier chapters, or a new one. No company data.
  • No full. You’ll see why it isn’t even offered for this flow.
  • Secrets stay out of anything you save or share. In this page they’re shown as *******************.
  • Requests go to your My Domain, never login.salesforce.com.

1. Make the integration user. Create a user with the Salesforce Integration user license and the Minimum Access - API Only Integrations profile (S11). Give it a permission set that lets it read Accounts, and nothing else. If Salesforce asks for a permission set license to go with it, assign the one it names.

2. Make the app. In Setup, open External Client App Manager and create a local external client app called “Reporting tool”. In its settings, enable OAuth, give it a callback URL (any https:// address will do; this flow doesn’t use it), and select only one scope: Access the identity URL service (id). Select Enable Client Credentials Flow, and save (S8, S9).

3. Choose who it runs as. Edit the app’s policies: enable the client credentials flow there too, and enter the integration user’s username as the execution user (S8). Save.

4. Get the key and secret. In the app’s settings, under OAuth Settings, view the consumer key and secret. Salesforce may ask you to verify who you are first (S1): that’s the entrance protecting its own keys.

5. Get a wristband. From a terminal, post to your My Domain’s token endpoint (S1):

Terminal window
curl -s -X POST https://YOUR-DOMAIN.my.salesforce.com/services/oauth2/token -d grant_type=client_credentials -d client_id=YOUR_KEY -d client_secret=YOUR_SECRET

Read the answer field by field, as in module 3. What’s in scope? Copy the access_token and the id URL somewhere private.

6. Show the wristband at two stalls. First the identity URL, the id value from step 5’s answer, which the id scope allows. Salesforce takes the token in an Authorization header for it, as for the REST API (S2):

Terminal window
curl -s "YOUR_ID_URL" -H "Authorization: Bearer YOUR_TOKEN"

Then a query, which it doesn’t. Use the latest API version your org lists at https://YOUR-DOMAIN.my.salesforce.com/services/data in place of v66.0:

Terminal window
curl -s "https://YOUR-DOMAIN.my.salesforce.com/services/data/v66.0/query?q=SELECT+Name+FROM+Account+LIMIT+1" -H "Authorization: Bearer YOUR_TOKEN"

Write down what each one answers. The words of the error can change from release to release; what matters is that the query is refused and the identity call isn’t.

7. Widen the scope. Back in the app’s settings, add Manage user data via APIs (api) beside id, and save. Get a new wristband (step 5) and look at scope again. Then run the query again. If the new wristband still shows only id, wait a few minutes and get another one.

8. Find the second check. The query works now, and it returns Accounts because the integration user’s permission set allows them. Try a query on an object the permission set doesn’t give it, such as Opportunity. What happens? The scope says “APIs”; the user says “which data”.

9. Take it back. Revoke the wristband (S4):

Terminal window
curl -s -X POST https://YOUR-DOMAIN.my.salesforce.com/services/oauth2/revoke -d token=YOUR_TOKEN

Run the query once more. Then ask the entrance about the token; the app can check its own (S5, S9):

Terminal window
curl -s -X POST https://YOUR-DOMAIN.my.salesforce.com/services/oauth2/introspect -d token=YOUR_TOKEN -d client_id=YOUR_KEY -d client_secret=YOUR_SECRET -d token_type_hint=access_token

For a token that’s no longer valid, the introspection standard says the answer is "active": false and nothing more (RFC 7662). Compare it with the answer you got before you revoked it.

10. Look for what’s missing. In the app’s scope list, is there a way to give this app a refresh token, or full, through client credentials? Look at the answer in step 5 again: there’s no refresh_token field. Salesforce filters full, web and refresh_token out of this flow (S1).

Why didn’t the token request say which scope it wanted? Because it can’t: “you can’t include scopes in the request. Instead, assign scopes to your external client app” (S1). The app decides what the wristband allows.

What would full have done here? Nothing: Salesforce filters it out of client credentials (S1). And for a person’s app, it’s the keys to the whole farm, and it still wouldn’t bring a refresh token on its own (S2).

Why did the Opportunity query fail with api on the token? Because the scope is only the first check. The token acts as the integration user, and the integration user can’t see Opportunities. A scope can’t give more than the user has (module 4).

What if the reporting tool’s secret leaked yesterday? Whoever has it can get a wristband for the integration user: no person, no MFA (S1). Change the secret at once, revoke the tokens, and look at what the integration user did. That’s why the user is narrow, and why the app has one job.

Why one app for the reporting tool, and not one shared by every integration? Salesforce gives each app and user its own tokens (S2). One app per job means you can widen, narrow or revoke one without touching the others.

  • The app: client credentials, run as the integration user, with the scopes you chose.
  • The first wristband: scope was id; the identity URL answered; the query was refused.
  • The second: scope was id api; Accounts answered; Opportunities didn’t.
  • The revoke: the query refused again, and the entrance says the token is no longer good.
  • The secret: not saved anywhere you’d share.
  • IDP-2 (app scope and configuration for authorization): the scopes on the app, and what each allowed.
  • IDP-3 (OAuth implementation concepts): scopes, secrets, tokens, expiry and revocation.
  • AMB-4 (app configuration settings): the settings and the policies of an external client app.