15

I use the Metadata API and the Apex wrapper to create an AuthProvider and NamedCredential after the installation of my managed package app.

MetadataService.NamedCredential cred = new MetadataService.NamedCredential();
cred.fullName = 'MyCred';
cred.label = 'MyCred';
cred.allowMergeFieldsInBody = false;
cred.allowMergeFieldsInHeader = true;
cred.authProvider = 'MyKeycloak';
cred.generateAuthorizationHeader = true;
cred.oauthScope = 'api refresh_token';
cred.principalType = 'NamedUser';
cred.protocol = 'Oauth';
cred.endpoint = apiEndpoint.trim();

The problem is the Authorization is still in Pending status as a user seems to manually Edit and Save the Named Credential to trigger it.

Is there a way I can trigger that by my code when I insert the NamedCredential?

3
  • 1
    Is this what's represented by the ExternalServiceRegistration status field? Commented Jul 9, 2019 at 16:56
  • Not sure but looks like this. The question is: can I create it using the API and will this trigger my browser to redirect to the Auth Url? Commented Jul 9, 2019 at 17:48
  • @identigral I tend not to as in the communities because the answers here are 99% faster and of better quality. But if I am not successful here I will also ask there. Commented Jul 9, 2019 at 17:57

2 Answers 2

10

The flow can be invoked from a user-facing process. Here are the parameters:

  • /services/auth/xds/{org_id}/{authprovider_developer_name} - this issues the logout,
  • &startURL=/0XA... - this ID tells SF which Named Credential to link the grant against,
  • &scope=full... - list of scopes/privileges presented to the user at the Allow screen,

Apex code to return such a URL from a VF page action or button:

public PageReference authorizeNamedCredential(String authProvider, String namedCredential)
{
    AuthProvider provider = [
        SELECT Id
        FROM AuthProvider
        WHERE DeveloperName = :authProvider
    ];
    
    NamedCredential credential = [
        SELECT Id
        FROM NamedCredential
        WHERE DeveloperName = :namedCredential
    ];
    
    String oid = UserInfo.getOrganizationId();
    String domain = Url.getOrgDomainUrl().toExternalForm();
    String path = '/services/auth/xds/' + oid + '/' + authProvider;
    String startUrl = '/' + String.valueOf(credential.Id).left(15);
    
    PageReference redirect = new PageReference(domain + path);
    redirect.getParameters().put('startURL', startUrl);
    redirect.getParameters().put('scope', 'full refresh_token');
    return redirect;
}

After logging back in, the user must manually click Allow:

named credential grant

Technically it's possible to skip the manual authorization nag by pre-authorizing the app on a Profile or Permission Set. But the pre-authorization step is itself a manual process!

  • Deploy the Connected App into the org per your Metadata API
  • Go to Setup > Connected Apps > Manage Apps > Edit Policies
  • Change "Permitted Users" to "Admin approved users are pre-authorized" then save
  • Edit the Profiles or Permission Sets then navigate to Connected App Access
  • Enable the app, then save.
8
  • 4
    You ROCK!!!!!!! Commented Jul 9, 2019 at 18:50
  • Is there also a way to find out in Apex if such an Authorization has already taken place? I would like to show the user the option to authorize only if it is in in "Pending" mode. Commented Jul 9, 2019 at 19:22
  • 1
    @RobertSösemann Perhaps send an HTTP request that uses the credential, and check if it succeeds? Commented Jul 9, 2019 at 20:48
  • 3
    Brilliant--Thank You! Now I can close my very similar question: salesforce.stackexchange.com/questions/261726/… (To be fair, @RobertSösemann described it better :-)
    – DaveS
    Commented Jul 10, 2019 at 0:23
  • 1
    @RRoesler That's right, the only allowed URL is the Named Credential ID itself, and SF uses that when providing the dance to know what component is being authorized. Commented Jun 9, 2020 at 6:41
6

If all you need to do is to kick off the oAuth flow so that the user may authorize the app, the auth provider supports this natively:

public PageReference (String authProvider) {

        AuthProvider provider = [
            SELECT Id,OauthKickoffUrl
            FROM AuthProvider
            WHERE DeveloperName = :authProvider
        ];

        return new PageReference(provider.OauthKickoffUrl);
 }
8
  • This rocks even more! What about the redirect Url after the flow? Can I use something like a Visualforce page in Lightning using startUrl=/lightning/n/MyPage? Commented Jul 10, 2019 at 18:11
  • 1
    @RobertSösemann According to the docs, startURL is officially supported for external auth providers...although I am curious if oAuth flow would respect it as it's really designed for SSO.
    – identigral
    Commented Jul 10, 2019 at 18:31
  • And how would I add it. Concat or adding pageref param? Commented Jul 10, 2019 at 18:55
  • 1
    The latter: getParameters().put('startURL', ...)
    – identigral
    Commented Jul 10, 2019 at 19:13
  • 1
    @RobertSösemann startURL is the only possibility I know about. If you're using a URI (a relative URL..), the docs say it should work. It's probably doing a check on the URI to make sure you're not redirecting them to a resource where access wasn't authorized in this sequence. Perhaps that's the issue, e.g. if you're authorizing my.sf.com and /lightning resides under a different subdomain. Don't know much more than this educated guess, I am afraid. Try a simpler startURL value to test this.
    – identigral
    Commented Jul 10, 2019 at 21:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .