Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous Authentication in Netsuite Token Based API call

I am trying to make SOAP calls to the Netsuite API using Token Based Authentication. I have a C# client that is generated from WDSL and it is sending the following request (with the secrets replaced).

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:messages_2016_2.platform.webservices.netsuite.com" xmlns:urn1="urn:core_2016_2.platform.webservices.netsuite.com">
   <soapenv:Header>
      <urn:partnerInfo>
         <urn:partnerId>[MyAccountId]</urn:partnerId>
      </urn:partnerInfo>
      <urn:applicationInfo>
         <urn:applicationId>[MyApplicationId]</urn:applicationId>
      </urn:applicationInfo>
      <urn:tokenPassport>
         <urn1:account>[MyAccountId]</urn1:account>
         <urn1:consumerKey>[MyConsumerKey]</urn1:consumerKey>
         <urn1:token>[MyTokenId]</urn1:token>
         <urn1:nonce>1574515852</urn1:nonce>
         <urn1:timestamp>1499135589</urn1:timestamp>
         <urn1:signature algorithm="HMAC-SHA1">Ll8DbLvTWsBh/G7UtenErR03OrM=</urn1:signature>
      </urn:tokenPassport>
   </soapenv:Header>
   <soapenv:Body>
      <urn:getDataCenterUrls>
         <urn:account>[MyAccountId]</urn:account>
      </urn:getDataCenterUrls>
   </soapenv:Body>
</soapenv:Envelope>

I am getting the following response

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <soapenv:Fault>
         <faultcode>soapenv:Server.userException</faultcode>
         <faultstring>Ambiguous authentication</faultstring>
         <detail>
            <platformFaults:invalidCredentialsFault xmlns:platformFaults="urn:faults_2016_2.platform.webservices.netsuite.com">
               <platformFaults:code>USER_ERROR</platformFaults:code>
               <platformFaults:message>Ambiguous authentication</platformFaults:message>
            </platformFaults:invalidCredentialsFault>
            <ns1:hostname xmlns:ns1="http://xml.apache.org/axis/">partners-java20004.sea.netledger.com</ns1:hostname>
         </detail>
      </soapenv:Fault>
   </soapenv:Body>
</soapenv:Envelope>

I have tried lots of different ways of generating the signature, nonce and timestamp. Currently I have the following:

private string computeNonce()
{
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] data = new byte[20];
    rng.GetBytes(data);
    int value = Math.Abs(BitConverter.ToInt32(data, 0));
    return value.ToString();
}

private long computeTimestamp()
{
    return ((long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
}

private TokenPassportSignature computeSignature(string accountId, string consumerKey, string consumerSecret, string tokenId, string tokenSecret, string nonce, long timestamp)
{
    string baseString = accountId + "&" + consumerKey + "&" + tokenId + "&" + nonce + "&" + timestamp;
    string key = consumerSecret + "&" + tokenSecret;
    string signature = "";
    var encoding = new System.Text.ASCIIEncoding();
    byte[] keyBytes = encoding.GetBytes(key);
    byte[] baseStringBytes = encoding.GetBytes(baseString);
    using (var hmacSha1 = new HMACSHA1(keyBytes))
    {
        byte[] hashBaseString = hmacSha1.ComputeHash(baseStringBytes);
        signature = Convert.ToBase64String(hashBaseString);
    }
    TokenPassportSignature sign = new TokenPassportSignature();
    sign.algorithm = "HMAC-SHA1";
    sign.Value = signature;
    return sign;
}

Does anyone have any ideas? Thanks!

like image 511
ChrisBellew Avatar asked Jul 04 '17 02:07

ChrisBellew


People also ask

What is token based authentication NetSuite?

Token based authentication is a per user authentication and requires certain permissions in NetSuite. An existing role can be used (recommended) or a new role can be created. The relevant role permissions are under the 'Setup' subtab the following details were gleaned from SuiteAnswer(41898). Access Token Management.

How do I find my NetSuite token ID?

Click Setup > Users/Roles > Access Tokens > New.Select the User that you created in the previous step. Select the role that you created in Step 2. Click Save. The Token ID / Secret are displayed.


5 Answers

I was also struggling with this unhelpful error after switching to TBA. Turns out I was still sending the ApplicationInfo property along with the new Consumer Key and Consumer Secret.

I found this on NetSuite's "SuiteAnswers" site and wanted to quote it here for anyone else still having this problem.

Ambiguous Authentication Errors

When you use token-based authentication (TBA) in web services, an ambiguous authentication error response is returned if you use another authentication mechanism together with the TBA header.

You receive this error if besides the TBA header, your request contains an Application ID, a passport object with an email address and a password, or a valid JSESSIONID.

The error occurs in the following cases:

  • If a single web services request contains a combination of the Passport, TokenPassport and SsoPassport complex types.

  • If a single web services request contains both the tokenPassport and the ApplicationInfo complex types, and therefore contains the application ID in the SOAP header.

Source: Token-Based Authentication Errors in Web Services

like image 157
crgolden Avatar answered Oct 17 '22 14:10

crgolden


The getDataCenter calls don't need the passport. I just had the same issue with the mapSso function. It looks like the 2017.1 release made them stricter on not accepting the passport

like image 40
bknights Avatar answered Oct 17 '22 15:10

bknights


I had to modify the XML and remove the tokenpassport(account,comsumer key,token,nonce,timestamp) tag and it worked.

like image 2
Amol Avatar answered Oct 17 '22 16:10

Amol


I know this is an old question, but I struggled with the same issue, and found a working solution.

private static void CreateTokenPassport()
{
    // Initialize the netsuite web service proxy.
    _netSuiteService = new NetSuiteService();

    // A valid Token passport signature consists of the following:
    // Create a base string.
    //     The base string is variable created from concatenating a series of values specific to the request.Use an ampersand as a delimiter between values.
    //     The values should be arranged in the following sequence:
    // NetSuite account ID
    // Consumer key
    // Token
    // Nonce(a unique, randomly generated alphanumeric string, with a minimum of six characters and maximum of 64)
    // Timestamp
    // See: https://system.na1.netsuite.com/app/help/helpcenter.nl?fid=section_4395630653.html#bridgehead_4398049137

    string consumerSecret = "";
    string tokenSecret = "";
    string accountId = "";
    string consumerKey = "";
    string tokenId = "";
    string nonce = ComputeNonce();
    long timestamp = ComputeTimestamp();

    string baseString = string.Format("{0}&{1}&{2}&{3}&{4}", accountId, consumerKey, tokenId, nonce, timestamp);
    string secretKey = string.Format("{0}&{1}", consumerSecret, tokenSecret);

    // Initialize the keyed hash object using the secret key as the key
    HMACSHA256 hashObject = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));

    // Computes the signature by hashing the data with the secret key as the key
    byte[] signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(baseString));

    // Base 64 Encode
    string encodedSignature = Convert.ToBase64String(signature);

    TokenPassport tokenPassport = new TokenPassport
    {
        signature = new TokenPassportSignature
        {
            Value = encodedSignature,
            algorithm = "HMAC_SHA256"
        },
        account = accountId,
        consumerKey = consumerKey,
        token = tokenId,
        nonce = nonce,
        timestamp = timestamp
    };

    _netSuiteService = new NetSuiteService
    {
        tokenPassport = tokenPassport
    };
}

Utility Methods:

private static string ComputeNonce()
{
    return Guid.NewGuid().ToString("N");
}

private static long ComputeTimestamp()
{
    return ((long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
}
like image 6
Michael G Avatar answered Oct 17 '22 16:10

Michael G


I don't know how it's done in C# with HMAC-SHA1 but in Javascript with CryptoJS HMAC-SHA256 you first sign the string and then encode it in Base64:

var baseString = ACCOUNT_ID + "&" + NETSUITE_CONSUMER_KEY + "&" + NETSUITE_TOKEN_ID + "&" + NONCE + "&" + TIMESTAMP;
var key = NETSUITE_CONSUMER_SECRET + '&' + NETSUITE_TOKEN_SECRET;
var HMAC256_Sig = cryptoJS.HmacSHA256(baseString, key);
var HMAC256_Sig_Base64 = cryptoJS.enc.Base64.stringify(HMAC256_Sig);

Then you output it like:

'<platformCore:signature algorithm = "HMAC_SHA256">' + HMAC256_Sig_Base64 + '</platformCore:signature>'
like image 1
Adolfo Garza Avatar answered Oct 17 '22 14:10

Adolfo Garza