2

The following Apex code uses Metadata and SOAP API to create a NamedCredential and Custom Auth Provider to connect to a public REST API that is authenticated via OAuth.

public override PageReference run() {
    createAuthProvider();
    createNamedCredential();
return oAuthKickoffUrl();

}

private void createAuthProvider() { createSObject('<urn1:type>AuthProvider</urn1:type>' + '<urn1:DeveloperName>' + DEV_NAME + '</urn1:DeveloperName>' + '<urn1:FriendlyName>' + DEV_NAME + '</urn1:FriendlyName>' + '<urn1:ProviderType>OpenIdConnect</urn1:ProviderType>' + '<urn1:OptionsSendAccessTokenInHeader>true</urn1:OptionsSendAccessTokenInHeader>' + '<urn1:OptionsSendClientCredentialsInHeader>false</urn1:OptionsSendClientCredentialsInHeader>' + '<urn1:ConsumerKey>' + CLIENT_KEY + '</urn1:ConsumerKey>' + '<urn1:ConsumerSecret>' + CLIENT_SECRET + '</urn1:ConsumerSecret>' + '<urn1:AuthorizeUrl>' + API_ENDPOINT + '/oauth/authorize</urn1:AuthorizeUrl>' + '<urn1:TokenUrl>' + API_ENDPOINT + '/api/v1/oauth/token/</urn1:TokenUrl>' ); }

private void createNamedCredential() { MetadataService.NamedCredential cred = new MetadataService.NamedCredential(); cred.fullName = DEV_NAME; cred.label = DEV_NAME; cred.allowMergeFieldsInBody = false; cred.allowMergeFieldsInHeader = false; cred.authProvider = DEV_NAME; cred.generateAuthorizationHeader = true; cred.principalType = 'NamedUser'; cred.protocol = 'Oauth'; cred.authProvider = DEV_NAME; cred.endpoint = API_ENDPOINT + '/v3';

create(cred);

}

private PageReference oAuthKickoffUrl() { PageReference result = new PageReference(authProvider.OauthKickoffUrl);

String startUrl = '/' + String.valueOf(namedCredential.Id).left(15);
result.getParameters().put('startURL', startUrl);

return result;

}

Everything is created properly and looks ok but the Kickoff Call fails with a page of the API provider saying

Error: invalid_request: Invalid "redirect_uri" in request.

    https://provider.com/oauth/authorize
    ?response_type=code
    &client_id=bJE3456345njoH77n5hdGYMV3a
    &redirect_uri=https%3A%2F%2Ftrusted-saltedcaramel-234-dev-ed.cs163.my.salesforce.com
    %2Fservices%2Fauthcallback%2FProvider_Integration&state=CAAAAX94cbTCMDAwM

(I have used this approach successfully with many other APIs without such issues.)

Robert Sösemann
  • 37,622
  • 26
  • 165
  • 495
  • Can you please post stacktrace to show the line num of the error? – metasync Mar 10 '22 at 17:10
  • No Stacktrace, no line number. The Oauth Page of the API Provider is showing a page with the error message. Nothing fails inside Salesforce. – Robert Sösemann Mar 10 '22 at 17:58
  • 1
    Could you add what System.debug says about your redirectUrl variable (with all the confidential stuff removed)? – Felix van Hove Mar 10 '22 at 21:13
  • @FelixvanHove I added it. – Robert Sösemann Mar 11 '22 at 09:54
  • 1
    The header that you've provided is even better. But would you not (based on your code) expect "services/auth/xds" to appear in the redirect_uri? (Sorry, if this question is not advancing the solution.) – Felix van Hove Mar 11 '22 at 10:07
  • @FelixvanHove why not? It is the Oauth Kickoff URL that I create according to this other question: https://salesforce.stackexchange.com/questions/268763 How do you think it should look like instead. Maybe you are right and I am doing it wrong ;-) – Robert Sösemann Mar 11 '22 at 11:10
  • Again worried to ask something stupid, - have you properly registered your redirect uri on the authorization server? – Felix van Hove Mar 11 '22 at 11:50
  • 1
    @FelixvanHove Don't worry, you help a lot. Are you saying the API Provider needs to know the redirect URI in advance? How would that work? Many different Salesforce orgs with changing mydomains will use this oauth flow. Do I need a Connect App? This made me think that https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oidc_dynamic_client_reg_flow.htm&type=5 – Robert Sösemann Mar 11 '22 at 11:57
  • 1
    If the uri is not registered, anyone between you and the authorization server could replace it with another uri - and off you go! Or not?! – Felix van Hove Mar 11 '22 at 12:04
  • @FelixvanHove Got it. How would an external API provider register such URLs when he builds an AppExchange app that needs to work for unknown mydomains? Can you point me to any documentation describing that? And how does this registering technically work? – Robert Sösemann Mar 11 '22 at 12:15
  • 1
    @RobertSösemann, I would need to look into this myself, but I belive the Salesforce documentation you thought of (OpenID Connect Dynamic Client Registration) is pertinent. – Felix van Hove Mar 11 '22 at 13:54
  • 1
    @FelixvanHove: MyDomains are subdomains and wildcards are allowed in Redirect URI registrations https://auth0.com/docs/get-started/applications/wildcards-for-subdomains – Robert Sösemann Mar 11 '22 at 16:33
  • 1
    @RobertSösemann Wow, cool, have you got it working? Maybe you want to post this as an answer - would be interesting for me too! – Felix van Hove Mar 11 '22 at 17:43

1 Answers1

0

Note: Even if it is a bit embarrassing that this question just showed my own misunderstanding of OAuth, let me add as an answer how we resolved the issue.

Everything on the Salesforce side was correct but our assumption that the External Service Provider could just accept arbitrary Callback URL was plain wrong.

Even if some tools allow wildcards on Redirect URIs this is super insecure and should not be done.

We now register every new client / Salesforce subscriber at the External Provider first so they can know the exact Redirect UI in advance.

Robert Sösemann
  • 37,622
  • 26
  • 165
  • 495