Skip to main content

Environment: [Developer Edition demo org / your actual org type], My Domain https://co1791181560741.my.salesforce.com

Setup completed and verified:

  • External Client App "ERP_Agent" — OAuth enabled, Enable Token Exchange Flow checked, Issue JWT-based access tokens for named users checked, scopes api + openid, Permitted Users = "Admin approved users are pre-authorized" with Permission Set assigned, IP Relaxation = "Relax IP restrictions". Rebuilt from scratch once to rule out a corrupted app object — same result both times.
  • Apex class SalesErpExchangeHandler extends Auth.Oauth2TokenExchangeHandler — compiles cleanly; validateIncomingToken/getUserForTokenSubject logic independently verified correct via Execute Anonymous against a real token.
  • OauthTokenExchangeHandler metadata record — deployed, confirmed via Setup → Token Exchange Handlers page showing it linked and enabled for ERP_Agent, with apexExecutionUser set to a valid, active integration user (confirmed exact Username match).
  • A separate External Client App ("Sales_Agent_Broker") using Client Credentials Flow works correctly and reliably issues valid JWTs — ruling out broader org-level OAuth/My Domain issues.

Symptom: POST to /services/oauth2/token with grant_type=urn:ietf:params:oauth:grant-type:token-exchange consistently returns:

json

{"error":"access_denied","error_description":"end-user denied authorization"}

Tried both subject_token_type values (jwt and access_token)

  • Tried with and without explicit scope and token_handler parameters
  • Verbose curl (-v) shows no additional detail — no WWW-Authenticate header, just the generic JSON body
  • An Apex-Class-level trace flag (CLASS_TRACING, confirmed present via Tooling API query on TraceFlag) on SalesErpExchangeHandler captures zero log entries across every attempt — strong evidence the handler is never being invoked, meaning the rejection happens before Salesforce reaches the Apex layer at all.

Question:

What pre-Apex check is this failing, and is there an org-level enablement for Token Exchange Flow beyond the per-app checkbox that isn't exposed in Setup? 

 

#OAuth 2.0  #Apex

3 respuestas
  1. Hoy, 14:40

    Ranjith, the class trace flag is only a filter. Debug Logs gets a file when SalesErpExchangeHandler actually runs. It never does, so that TraceFlag row doesn't produce a log. Login History is the same. I pointed you there too early. This deny lands before a user session is opened, so there's no row to read.

    Put Permitted Users back to Admin approved users are pre-authorized. All users may self-authorize still expects a consent click, and this token POST never shows one.

    The user on ERP_Agent's permission set has to be the user the subject token was issued to. apexExecutionUser only matters once the handler runs. If you pasted a token from Sales_Agent_Broker, that user is the client-credentials run-as user on that app, and that person has to be pre-authorized on ERP_Agent.

    If that subject token is a JWT, subject_token_type access_token never gets to your class. Use the jwt URN with it. I don't know of an extra Developer Edition switch past Enable Token Exchange Flow.

0/9000