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:
- A record variable which is available for output to represent the
ExternalDataUserAuth
record
- A text variable which is available for input to represent the Auth. Provider ID
- A text variable with is available for input to represent the Named Credential ID
Then create a minimum of two steps:
- 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
- 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!