0

I'm using a Named Credential and custom Auth Provider to get access to the Xero service. I have the following Auth.AuthProviderPluginClass

public with sharing class XeroAuthProvider extends Auth.AuthProviderPluginClass {

public String getCustomMetadataType() {
    return 'XeroConfig__mdt';
}

public PageReference initiate(Map<String, String> config, String stateToPropagate) {
    System.debug(LoggingLevel.WARN, 'initiate-config: ' + config);
    System.debug(LoggingLevel.WARN, 'initiate-stateToPropagate: ' + stateToPropagate);
    String redirectUrl = System.Url.getSalesforceBaseUrl().toExternalForm().replace('http:','https:') + '/services/authcallback/XeroOAuth2';
    System.debug('RedirectURL: ' + redirectUrl);
    String url = config.get('Endpoint__c');
    String strConsumerKey = config.get('ConsumerKey__c');
    String strScope = config.get('Scope__c');
    url += '?redirect_uri=' + redirectUrl;
    url += '&response_type=code';
    url += '&client_id=' + strConsumerKey;
    url += '&scope=' + strScope;
    url += '&state=' + stateToPropagate;
    System.debug('url: ' + url);
    return new PageReference(url);
}

public Auth.AuthProviderTokenResponse handleCallback(Map<String, String> config, Auth.AuthProviderCallbackState callbackState) {
    System.debug(LoggingLevel.WARN, 'handleCallback-config: ' + config);
    System.debug(LoggingLevel.WARN, 'handleCallback-callbackState: ' + callbackState);
    System.debug(LoggingLevel.WARN, 'handleCallback-token: ' + callbackState.queryParameters.get('code'));
    String redirectUrl = System.Url.getSalesforceBaseUrl().toExternalForm().replace('http:','https:') + '/services/authcallback/XeroOAuth2';
    HttpRequest req = new HttpRequest();
    req.setMethod('POST');
    req.setEndpoint('https://identity.xero.com/connect/token');
    req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
    req.setHeader('Authorization','Basic ' + EncodingUtil.base64Encode(Blob.valueOf( config.get('ConsumerKey__c') + ':' + config.get('ClientSecret__c'))));

    req.setBody('grant_type=authorization_code&redirect_uri=' + EncodingUtil.urlEncode(redirectUrl,'UTF-8') + '&code=' + EncodingUtil.urlEncode(callbackState.queryParameters.get('code'), 'UTF-8'));

    System.debug('Request: ' + req);
    System.debug('Headers: ' + req.getHeader('Authorization'));
    HttpResponse resp = new Http().send(req);
    System.debug('Response: ' + resp);
    Map<String, String> mpResponse = (Map<String, String>) JSON.deserialize(resp.getBody(), Map<String,String>.class);
    return new Auth.AuthProviderTokenResponse('XeroOAuth2', (String) mpResponse.get('access_token'), (String) mpResponse.get('refresh_token'), callbackState.queryParameters.get('state'));
}

public Auth.UserData getUserInfo(Map<String, String> config, Auth.AuthProviderTokenResponse response) {
    System.debug(LoggingLevel.WARN, 'getUserInfo-config: ' + config);
    System.debug(LoggingLevel.WARN, 'getUserInfo-response: ' + response);
    System.debug('provider: ' + response.provider);
    System.debug('state: ' + response.state);

    return new Auth.UserData('fakeId', 'first', 'last', 'full', 'email', 'link', null, null, null, null, null);
}

public override Auth.OAuthRefreshResult refresh(Map<String, String> config, String refreshToken) {
    System.debug(LoggingLevel.WARN, 'refresh-config: ' + config);
    System.debug(LoggingLevel.WARN, 'refresh-refreshToken: ' + refreshToken);
    HttpRequest req = new HttpRequest();
    req.setMethod('POST');
    req.setEndpoint('https://identity.xero.com/connect/token');
    req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
    req.setHeader('Authorization','Basic ' + EncodingUtil.base64Encode(Blob.valueOf( config.get('ConsumerKey__c') + ':' + config.get('ClientSecret__c'))));

    req.setBody('grant_type=refresh_token&refresh_token=' + refreshToken);

    System.debug('Request: ' + req);
    HttpResponse resp = new Http().send(req);
    Map<String, String> mpResponse = (Map<String, String>) JSON.deserialize(resp.getBody(), Map<String, String>.class);
    for ( String strKey : mpResponse.keySet()) {
        System.debug('Key: ' + strKey + ': ' + mpResponse.get(strKey));
    }
    return new Auth.OAuthRefreshResult(mpResponse.get('access_token'), mpResponse.get('refresh_token'));
}

}

Code courtesy of https://github.com/grekker/pbp-authenticating-external-apps/blob/master/force-app/main/default/classes/CustomAuthProvider.cls

Named Credential: Name Credential references XeroOAuth2 custom Authentication Provider which in turns references XeroAuthProvider class. When I save the Named Credential Salesforce begins the OAuth process and gets me to login to Xero. That all works fine. In the debug logs I can see the access and refresh tokens. I can use the Named Credential to access the Xero service and retrieve information as per: 'callout:Xero/Accounts'. The Named Credential / Auth Provider combination retrieves the access token as expected and inserts it in the header. What I can't do is Auth.AuthToken.getAccessToken('0SOO0000000xxxxXXX','XeroOAuth2');

It always returns null. The Named Credential and login process would've been run in my SysAdmin context. The getAccessToken is run in the Dev Console context. They are the same user. I think they are the same context. I have tried with 15 and 18 character authProviderIds. The providerName matches that used on the last line of handleCallback().

Does anyone know why getAccessToken() always returns null? Better sill how to fix it?

Thanks, Greg.

2
  • Have you tried using the Auth provider name as xerooauth2 in all lowercase in your code. Try querying the name of Auth provider, I belive they are all stored in lowercase.
    – manjit5190
    Commented Mar 20, 2021 at 8:48
  • I have used this: SELECT Id, ProviderType, FriendlyName, DeveloperName FROM AuthProvider and get back mixed case FriendlyName and DeveloperName 'XeroOAuth2'. Is there another field/object I should be looking at?
    – Greg Shaw
    Commented Mar 21, 2021 at 0:40

1 Answer 1

0

I got the Answer from here: Stackoverflow (Custom REST controller to access Auth.AuthToken.getAccessToken returning null)

Simple Answer is: Open Authentication Provider, open on the OAuth-Only Initialization URL, login. Now you get the Accesstoken!

You must log in to answer this question.

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