3

I have a named credential configured against the the Microsoft Graph API (https://graph.microsoft.com) with a Per User Identity Type, an OAuth 2.0 Authentication Protocol, and a configured Authentication Provider setup against Microsoft Access Control Service.

I'm able to call the API programmatically if I navigate as a user to my user settings > then Authentication Settings for External Systems, create a new setting against the Named Credential, and then authorize against Microsoft, and then use the named credential in an API call to Microsoft from some context as that user (for example via an LWC).

It looks like that page creates an ExternalDataUserAuth standard object, but this requires a lot of clicks and is a bit cumbersome for users. I assume you can create it via the Metadata API and the Apex Wrapper Salesforce Metdata API, but I would prefer to avoid adding even more classes into my org if possible.

Is it possible to create an ExternalDataUserAuth record without the Metadata API and then send the user through the OAuth 2.0 flow?

1 Answer 1

5

You can create an ExternalDataUserAuth record with a flow! Create an Autolaunched Flow (No Trigger), you may also need to specify the flow will run in System Context Without Sharing.

Create three resources:

  1. A record variable which is available for output to represent the ExternalDataUserAuth record
  2. A text variable which is available for input to represent the Auth. Provider ID
  3. A text variable with is available for input to represent the Named Credential ID

Then create a minimum of two steps:

  1. A variable assignment step to populate the ExternalDataUserAuth record with AuthProviderID equal to resource #2 above, ExternalDataSourceId equal to resource #3 above, and UserId equal to $User.Id
  2. Create Records step to insert the ExternalDataUserAuth record.

You can then call this flow from an Apex class when you detect that your user is not authorized after receiving a 401 HTTP status code in response to an API request.

Here is some sample apex code for calling the flow:

    NamedCredential nc = [SELECT Id, AuthProviderId 
                            FROM NamedCredential
                           WHERE DeveloperName = :namedCredentialDeveloperName 
                           LIMIT 1];
    AuthProvider provider = [SELECT Id, DeveloperName 
                               FROM AuthProvider
                              WHERE Id = :nc.AuthProviderId 
                              LIMIT 1];
    List<ExternalDataUserAuth> userAuth = [SELECT Id 
                                             FROM ExternalDataUserAuth 
                                            WHERE UserId = :UserInfo.getUserId() 
                                              AND AuthProviderId = :provider.Id 
                                              AND ExternalDataSourceId = :nc.id 
                                            LIMIT 1];
    
    /* This is necessary if a user created an ExternalDataUserAuth record 
       manually but didn't finish authorizing */
    if(userAuth.size() > 0) {
        userAuthId = userAuth[0].Id;
    } else {
        // Assumes you named second and third variables in flow above 'AuthProviderId' and 'NamedCredentialId'
        Map<String,String> flowInputs = new Map<String,String> {
            'AuthProviderId' => provider.Id, 
            'NamedCredentialId' => nc.Id
        };
        // Assumes name of flow is 'AuthHelperFlow'
        Flow.Interview.AuthHelperFlow authHelper = new Flow.Interview.AuthHelperFlow(flowInputs);
        authHelper.start();
        // Assumes name of first variable above is 'ExternalDataUserAuth'
        ExternalDataUserAuth auth = (ExternalDataUserAuth)authHelper.getVariableValue('ExternalDataUserAuth');
        String userAuthId = auth.Id;

Now that you have userAuthId, which represents the External Data User Auth record ID you created above, you can add that to the URL to send a user through the authorization flow like so:

String externalAuthUrl = URL.getSalesforceBaseUrl().toExternalForm()+'/services/auth/xds/'+provider.DeveloperName+'?startURL=%2F'+ userAuthId.left(15) +'%3Fisdtp%3Dvw';

Pass that URL to an Aura component or LWC and open a new window with that URL to start the User authorizing against Microsoft.

You can open a new window from an LWC like so:

window.open(this.urlPassedFromApex, '_blank', 'width=700, height=800');

(You could leave out the width=700, height=800 if you prefer it to open however your user has their browser configured.)

Either it will have them enter their credentials or take them directly back to Salesforce. They can then close that window.

After this you can successfully use your named credential to call the Microsoft Graph API as this user!

1
  • Hi @kyle, i tried this approach but the "Authentication systems for External systems" still shows the authentication status as Pending. Any pointers would be appreciated. Commented May 31 at 14:52

You must log in to answer this question.

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