10

Following on from a previous question:

I've created a custom REST endpoint which proxies access to Auth.AuthToken.getAccessToken(authProviderId, providerName); as suggested by @identigral.

Apex REST controller class using API v49.0

@RestResource(urlMapping='/AccessToken/*')
global with sharing class AccessTokenRESTController {
    @HttpGet
    global static void getAccessToken() {
        try {
            String token = Auth.AuthToken.getAccessToken(authProviderId, providerName);

            System.debug(token);

            if (token == null) {
                RestContext.response.statusCode = 404;
                RestContext.Response.responseBody = Blob.valueOf('Unable to find token for authProviderId: ' + authProviderId);
                //return; // commented out for debugging
            }

            AccessTokenInfo info = new AccessTokenInfo();
            info.AuthProviderId = authProviderId;
            info.AccessToken = token;

            RestContext.Response.addHeader('Content-Type', 'application/json');
            RestContext.Response.statusCode = 200;
            RestContext.Response.responseBody = Blob.valueOf(JSON.serializePretty(info));
        } catch (Exception error) {
            System.debug(LoggingLevel.ERROR, error);
            throw error;
        }
    }

    public class AccessTokenInfo {
        public String Name {
            get {
                return UserInfo.getName();
            }
        }
        public String Username {
            get {
                return UserInfo.getUsername();
            }
        }
        public String AccessToken { get; set; }
        public String AuthProviderId { get; set; }
    }

    private static String authProviderId {
        get {
            List<AuthProvider> providers = [SELECT Id FROM AuthProvider WHERE DeveloperName = :authProviderDeveloperName];

            if (providers.size() == 0) {
                throw new CustomException('Unable to find an Auth. Provider with DeveloperName: ' + authProviderDeveloperName);
            }

            System.debug(providers);

            return providers[0].Id;
        }
    }

    private static String authProviderDeveloperName {
        get {
            return 'MockLab';
        }
    }

    private static String providerName {
        get {
            return 'Open ID Connect';
        }
    }
}

The REST controller is accessible via a Site and I am passing a bearer token when I call the endpoint.

The Auth. Provider configuration is:

  • Provider Type: Open ID Connect
  • Name: MockLab
  • URL Suffix: MockLab
  • Consumer Key: mocklab_oauth2
  • Consumer Secret: whatever
  • Authorize Endpoint URL: http://weyz3.mocklab.io/oauth/authorize
  • Token Endpoint URL: http://weyz3.mocklab.io/oauth/token
  • User Info Endpoint URL: blank
  • Token Issuer: blank
  • Default Scopes: blank
  • Send access token in header: true
  • Send client credentials in header: false
  • Include Consumer Secret in API Responses: true
  • Custom Error URL: blank
  • Custom Logout URL: blank
  • Registration Handler: blank
  • Execute Registration As: blank
  • Portal: blank
  • Icon URL: blank

The NamedCredential configuration is:

  • Label: MockLab
  • Name: MockLab
  • URL: https://weyz3.mocklab.io/
  • Certificate: blank
  • Identity Type: Named Principal
  • Authentication Protocol: OAuth 2.0
  • Authentication Provider: MockLab
  • Scope: openid, profile, email
  • Authentication Status: Authenticated
  • Generate Authorization Header: true
  • Allow Merge Fields in HTTP Header: false
  • Allow Merge Fields in HTTP Body: false
  • Outbound Network Connection: false

I've completed the OAuth flow, so the NamedCredential has an Authentication Status of Authenticated.

I've also tested the NamedCredential works by connecting to the external API and fetching some data using Apex.

But when I call my custom REST endpoint to get the access token, I always get a null value.

Example response:

{
  "Username" : "xxx@xxx.com",
  "Name" : "Admin User",
  "AuthProviderId" : "0SO4K000000GozdWAC",
  "AccessToken" : null
}

Based on the response, I can see the request has successfully authenticated using the bearer token.

But the call to Auth.AuthToken.getAccessToken(authProviderId, providerName); is still returning null

If I try running this code in the developer console:

String token = Auth.AuthToken.getAccessToken('0SO4K000000GozdWAC', 'Open ID Connect');
System.debug(token);

I get a null value.

I've tried calling the method via a LWC component:

@AuraEnabled(cacheable=true)
public static String getAccessToken() {
    return Auth.AuthToken.getAccessToken('0SO4K000000GozdWAC', 'Open ID Connect');
}

I get a null value.

Everything is done using the same Admin user.

UPDATE: I've changed the Auth. Provider and Named Credenital details to the actual values for a MockLab environment I have set up, so you can test using exactly the same information/setup I am using.

Questions

  1. What am I doing wrong?
  2. How can I fix it, so the controller returns the token?
9
  • 1
    You completed the OAuth flow as the NamedCredential as some user. Are you using the same user to authorize the call to the REST web service? – identigral Oct 8 '20 at 0:42
  • @identigral - I believe so. I completed the OAuth flow as Salesforce Admin, and I'm accessing the site page using a bearer token for the same Salesforce Admin. The external system I completed the OAuth flow against uses the same email address as the salesforce admin user. What details should I be checking are the same to ensure this is correct? – Robs Oct 8 '20 at 6:16
  • 2
    @Robs Got some update from SF developer support : they said R&D team is aware of a "potential" issue to get the token. The issue is not considered a bug at the moment, so no Known Issue can be created since the issue is technically still considered under investigation – Shamina Oct 21 '20 at 17:30
  • 1
    @Shamina Do you happen to know if SF lists these kind of issues under investigation anywhere? Or is it know only within their internal team? – arut Oct 22 '20 at 5:16
  • 2
    @arut as per SF support, its only internal for now and scheduled build is planned for Spring 21. they provided 2 internal investigation numbers W-8080901, W-7845636 as reference for followup in the future – Shamina Oct 22 '20 at 5:59
3
+250

Edited

I did some tests with your code and setup in a Developer Org,

enter image description here


Test 1: Log in as the user who created the Auth. Provider in Workbench

Result: I can get the token in workbench.

enter image description here


Test 2: Grant apex class access to a site and visit the site

Result: I got a null as a site user.

enter image description here


Test 3: Log in as another System Admin in Workbench

Result: I got a null

enter image description here


I guess only the user created the Auth. Provider can get the token using Auth.AuthToken.getAccessToken(authProviderId, providerName);

Can you confirm that the username in your sample response is the same as the username of the Salesforce Admin that created the Auth.Provider?


I created a Connected App and got a token of the current user

enter image description here

then I used the user token to call the rest api and managed to get the AccessToken in Postman.

enter image description here

While it is possible to successfully get the token, this introduces a new token.

16
  • 1
    I can get the AccessToken in the workbench, so the solution using Auth.AuthToken.getAccessToken in the REST API is possible. But I got a null as another System Admin, so I think only the user created the Auth. Provider can get the token. I then created a connected app to call the REST API on behalf of the user who created the Auth. Provider, and it works. Since this connected app solution introduces a new token, I'd like to ask how you connect ESB to Salesforce, because if you're already using the connected app, this may not be an issue. – Adam Oct 14 '20 at 3:18
  • 1
    That's really strange, because it never works for me!? and I am always using the same Admin user. – Robs Oct 14 '20 at 15:28
  • 1
    @Adam, Can you provide some details related to your first screenshot (workbench screenshot showing the access token value)? Like what were the configuration values used for the Auth provider and the connected app? Did you use mocklab indicated in Rob's question for this or did you use OpenIDConnect mentioned in your answer update? – arut Oct 19 '20 at 17:50
  • 1
    @arut I updated my answer, I use the same code and settings as Rob. – Adam Oct 21 '20 at 2:54
  • 2
    @arut Have you visited the "OAuth-Only Initialization URL" listed In the Auth. Provider page? – Adam Oct 21 '20 at 8:09

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.