12

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?

  • 1
    Is this what's represented by the ExternalServiceRegistration status field? – Thomas Taylor Jul 9 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? – Robert Sösemann Jul 9 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. – Robert Sösemann Jul 9 at 17:57
4

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);
 }
  • 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? – Robert Sösemann Jul 10 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 Jul 10 at 18:31
  • And how would I add it. Concat or adding pageref param? – Robert Sösemann Jul 10 at 18:55
  • 1
    The latter: getParameters().put('startURL', ...) – identigral Jul 10 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 Jul 10 at 21:03
7

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.
  • 4
    You ROCK!!!!!!! – Robert Sösemann Jul 9 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. – Robert Sösemann Jul 9 at 19:22
  • 1
    @RobertSösemann Perhaps send an HTTP request that uses the credential, and check if it succeeds? – bigassforce Jul 9 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 Jul 10 at 0:23
  • @bigassforce It worked but with a few tweaks that you might wanna update in your answer. In the Url 1) OID is not needed 2) AuthProvider DeveloperName instead of id 2) startUrl is very selective. Redirecting to /lightning/n/MyPage showed BAD_START_URL – Robert Sösemann Jul 10 at 13:20

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.