3

In working through an answer to this question, I found that I can set the oauthToken value on a NamedCredential using an Apex wrapper for the Metadata API. However, as far as I am aware, that still involves installing a specific managed package, unless you want to roll your own wrapper.

Is there any other way to update the oauthToken?

Apex Metadata API Wrapper

No problem setting this field via Apex:

MetadataService.NamedCredential credential = new MetadataService.NamedCredential();
credential.fullName = 'Demo_Credential';
credential.label = 'Demo Credential';
credential.endpoint = 'https://www.demo.com';
credential.principalType = 'Anonymous';
credential.protocol = 'NoAuthentication';
credential.oauthToken = '<newTokenValue>';

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
service.updateMetadata(
    new List<MetadataService.Metadata> { credential }
);

UI

While I was able to update it via Apex, this field is not available to read or edit in the UI.

Named Credential Edit: Demo Credential

IDE

While I was able to add NamedCredential to my package.xml file, I was not able to push it back to the server using MavensMate (Mac).

Demo_Credential Metadata

Workbench

I played around with the Deploy action but was not able to get anything working there either. I'm not super familiar with how that interface is supposed to work.

Developer Workbench Metadata Deploy

  • seems like by design named credentials is supposed to mask from the admin/developer the acquisition/lifecycle of the oauth token, I am not surprised. – cropredy Apr 4 '17 at 22:47
  • But it's nigh impossible to set for the average admin. :( And it's actually pretty easy to view the token value anyway for anyone who has an IDE. – Adrian Larson Apr 4 '17 at 22:49
  • 2
    I'm not sure what "long term" means on the roadmap, potentially Winter '18, but a REST API for Metadata Deploy and Retrieve operations is coming to an org near you some time soon. – Mark Pond Apr 5 '17 at 0:09
  • 1
    @AdrianLarson - How are you viewing the token. I tried in 3 different IDE's and it is not returning it. Must be setting it wrong or something. I would like to verify that the JS does indeed update it – Eric Apr 5 '17 at 1:01
2
+25

Not sure if it actually puts a new oAuth token as I cannot query it via dev console but it does successfully update from VF:

May not be what you wanted, not sure, but maybe it will help

<apex:page id="NamedCredUpdate">

    <script>
        var sessionId = '{!$API.Session_ID}';
        function sendMetadataRequest(request, callback) {
            var binding = new XMLHttpRequest();

            binding.open('POST', location.protocol+'//'+location.host+ '/services/Soap/m/37.0');
            binding.setRequestHeader('SOAPAction', '""');
            binding.setRequestHeader('Content-Type', 'text/xml');
            binding.onreadystatechange =
                    function () {
                        if (this.readyState == 4) {
                            var parser = new DOMParser();
                            var doc = parser.parseFromString(this.response, 'application/xml');
                            console.log(doc);

                            var errors = doc.getElementsByTagName('errors');
                            var faultstring = '';
                            var messageText = '';

                            faultstring = doc.getElementsByTagName('faultstring');

                            if (faultstring.length != 0) {
                                messageText = faultstring[0].innerHTML;
                            } else {
                                for (var errorIdx = 0; errorIdx < errors.length; errorIdx++)
                                    messageText += errors.item(errorIdx).getElementsByTagName('message').item(0).innerHTML + '<br/>';
                            }

                            callback(
                                    messageText == '' ? doc : 'Error Making Configuration Changes: ' + messageText

                            );

                        }
                    };
            binding.send(request);

        }

        function updateNamedCredential(credName, callback) {
            var request =
                    '<?xml version="1.0" encoding="utf-8"?>' +
                    '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
                    '<env:Header>' +
                    '<urn:SessionHeader xmlns:urn="http://soap.sforce.com/2006/04/metadata">' +
                    '<urn:sessionId>' + sessionId + '</urn:sessionId>' +
                    '</urn:SessionHeader>' +
                    '</env:Header>' +
                    '<env:Body>' +
                    '<upsertMetadata xmlns="http://soap.sforce.com/2006/04/metadata">' +
                    '<metadata xsi:type="NamedCredential">' +
                    '<oauthToken>{!$API.Session_ID}</oauthToken>' +
                    '<fullName>' + credName + '</fullName>' +
                    '<label>' + credName + '</label>' +
                    '<endpoint>https://www.google.com</endpoint>' +
                    '<principalType>Anonymous</principalType>' +
                    '<protocol>NoAuthentication</protocol>' +
                    '</metadata>' +
                    '</upsertMetadata>' + '</env:Body>' + '</env:Envelope>';

            sendMetadataRequest(request, callback);
        }


    </script>

    <apex:form>
        <button type="button" onclick="updateNamedCredential('MyTest', function(msg){console.log(msg)});">Click Me</button>
    </apex:form>
</apex:page>
  • Damn, it's a busy time for me. Sorry I wasn't on top of this whole situation. I still haven't had time to vet. – Adrian Larson Apr 12 '17 at 22:19
  • @AdrianLarson - You ever get this resolved? – Eric May 24 '17 at 22:01
  • No, unfortunately, it's dropped off everyone's priority list for the time being. :( I will get back to it, but it may be another month yet. – Adrian Larson May 24 '17 at 22:03
  • Of course as soon as I go to test it, I find out someone refreshed the sandbox without talking to anyone. :( It's going to take me a while to restore my work. – Adrian Larson Jun 14 '17 at 14:30
1

I've generated a bare bones Apex class to use WebServiceCallout.invoke to update the NamedCredential.

//Generated by FuseIT WSDL2Apex (http://www.fuseit.com/Solutions/SFDC-Explorer/Help-WSDL-Parser.aspx)
//Warning: '<xsd:anyType>' element anyType has been converted to string in CustomMetadataValue
//Warning: '<xsd:anyType>' element anyType has been converted to string in FieldValue
//Warning: '<xsd:any>' element type has been changed to dataType List<DOM.XmlNode> called anyElement
//Methods Included: updateMetadata
//Methods Excluded: cancelDeploy, checkDeployStatus, checkRetrieveStatus, createMetadata, deleteMetadata, deploy, deployRecentValidation, describeMetadata, describeValueType, listMetadata, readMetadata, renameMetadata, retrieve, upsertMetadata
// Primary Port Class Name: MetadataPort    
public class MetadataService {
    public class AllOrNoneHeader_element {
        public Boolean allOrNone;
        private String[] allOrNone_type_info = new String[]{'allOrNone','http://soap.sforce.com/2006/04/metadata','boolean','1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'allOrNone'};
    }
    public class CallOptions_element {
        public String client;
        private String[] client_type_info = new String[]{'client','http://soap.sforce.com/2006/04/metadata','string','1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'client'};
    }
    public class Error {
        public MetadataService.ExtendedErrorDetails[] extendedErrorDetails;
        public String[] fields;
        public String message;
        public String statusCode;
        private String[] extendedErrorDetails_type_info = new String[]{'extendedErrorDetails','http://soap.sforce.com/2006/04/metadata','ExtendedErrorDetails','0','-1','false'};
        private String[] fields_type_info = new String[]{'fields','http://soap.sforce.com/2006/04/metadata','string','0','-1','false'};
        private String[] message_type_info = new String[]{'message','http://soap.sforce.com/2006/04/metadata','string','1','1','false'};
        private String[] statusCode_type_info = new String[]{'statusCode','http://soap.sforce.com/2006/04/metadata','StatusCode','1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'extendedErrorDetails','fields','message','statusCode'};
    }

    //Warning: '<xsd:any>' element type has been changed to dataType List<DOM.XmlNode> called anyElement
    public class ExtendedErrorDetails {
        public String extendedErrorCode;
        public List<DOM.XmlNode> anyElement;
        private String[] extendedErrorCode_type_info = new String[]{'extendedErrorCode','http://soap.sforce.com/2006/04/metadata','ExtendedErrorCode','1','1','false'};
        private String[] anyElement_type_info = new String[]{'anyElement','http://soap.sforce.com/2006/04/metadata','List<DOM.XmlNode>','0','1','true'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'extendedErrorCode','anyElement'};
    }
    public virtual class Metadata {
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata','string','0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'fullName'};
    }
    public class MetadataPort {
        public String endpoint_x = 'https://login.salesforce.com/services/Soap/m/39.0';
        public Map<String,String> inputHttpHeaders_x;
        public Map<String,String> outputHttpHeaders_x;
        public String clientCertName_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        public MetadataService.SessionHeader_element SessionHeader;
        private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/2006/04/metadata';
        public MetadataService.CallOptions_element CallOptions;
        private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/2006/04/metadata';
        public MetadataService.AllOrNoneHeader_element AllOrNoneHeader;
        private String AllOrNoneHeader_hns = 'AllOrNoneHeader=http://soap.sforce.com/2006/04/metadata';
        private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','MetadataService'};

        public MetadataService.SaveResult[] updateMetadata(MetadataService.Metadata[] metadata) {
            MetadataService.updateMetadata_element request_x = new MetadataService.updateMetadata_element();
            MetadataService.updateMetadataResponse_element response_x;
            request_x.metadata = metadata;
            Map<String, MetadataService.updateMetadataResponse_element> response_map_x = new Map<String, MetadataService.updateMetadataResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
                this,
                request_x,
                response_map_x,
                new String[]{endpoint_x,
                '',
                'http://soap.sforce.com/2006/04/metadata',
                'updateMetadata',
                'http://soap.sforce.com/2006/04/metadata',
                'updateMetadataResponse',
                'MetadataService.updateMetadataResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.result;
        }
    }
    public class SaveResult {
        public MetadataService.Error[] errors;
        public String fullName;
        public Boolean success;
        private String[] errors_type_info = new String[]{'errors','http://soap.sforce.com/2006/04/metadata','Error','0','-1','false'};
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata','string','1','1','false'};
        private String[] success_type_info = new String[]{'success','http://soap.sforce.com/2006/04/metadata','boolean','1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'errors','fullName','success'};
    }
    public class SessionHeader_element {
        public String sessionId;
        private String[] sessionId_type_info = new String[]{'sessionId','http://soap.sforce.com/2006/04/metadata','string','1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'sessionId'};
    }
    public class updateMetadata_element {
        public MetadataService.Metadata[] metadata;
        private String[] metadata_type_info = new String[]{'metadata','http://soap.sforce.com/2006/04/metadata','Metadata','0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'metadata'};
    }
    public class updateMetadataResponse_element {
        public MetadataService.SaveResult[] result;
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata','SaveResult','0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }

     public class NamedCredential extends Metadata {
        public String type = 'NamedCredential';
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        public String authProvider;
        public String certificate;
        public String endpoint;
        public String label;
        public String oauthRefreshToken;
        public String oauthScope;
        public String oauthToken;
        public String password;
        public String principalType;
        public String protocol;
        public String username;
        private String[] authProvider_type_info = new String[]{'authProvider','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] certificate_type_info = new String[]{'certificate','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] endpoint_type_info = new String[]{'endpoint','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] label_type_info = new String[]{'label','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] oauthRefreshToken_type_info = new String[]{'oauthRefreshToken','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] oauthScope_type_info = new String[]{'oauthScope','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] oauthToken_type_info = new String[]{'oauthToken','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] password_type_info = new String[]{'password','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] principalType_type_info = new String[]{'principalType','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] protocol_type_info = new String[]{'protocol','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] username_type_info = new String[]{'username','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] type_att_info = new String[]{'xsi:type'};
        private String[] field_order_type_info = new String[]{'fullName', 'authProvider','certificate','endpoint','label','oauthRefreshToken','oauthScope','oauthToken','password','principalType','protocol','username'};
    }
}

Usage is identical to the Metadata Service wrapper (as they are both generated from the WSDL) but is solely focused on updating NamedCredential records.

MetadataService.NamedCredential credential = new MetadataService.NamedCredential();
credential.fullName = 'Demo_Credential';
credential.label = 'Demo Credential';
credential.endpoint = 'https://www.demo.com';
credential.principalType = 'Anonymous';
credential.protocol = 'NoAuthentication';
credential.oauthToken = '<newTokenValue>';

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
service.endpoint_x = URL.getSalesforceBaseUrl().toExternalForm() + '/services/Soap/m/39.0';
service.updateMetadata(
    new List<MetadataService.Metadata> { credential }
);
System.assertEquals(1, results.size());
MetadataService.SaveResult sr = results[0];
System.debug(sr);
System.assert(sr.success);

I also deployed the following package, which didn't report any errors and was confirmed to update the NamedCredential.MasterLabel

Zip Structure

  • package.xml
  • namedCredentials/Demo_Credential.namedCredential

package.xml

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Demo_Credential</members>
        <name>NamedCredential</name>
    </types>
    <version>39.0</version>
</Package>

Demo_Credential.namedCredential

<?xml version="1.0" encoding="UTF-8"?>
<NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
    <endpoint>https://www.demo.com</endpoint>
    <label>Demo Credential</label>
    <principalType>Anonymous</principalType>
    <protocol>NoAuthentication</protocol>
    <oauthToken>TestToken</oauthToken>
</NamedCredential>

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.