基于 Netsuite 令牌的 API 调用中的身份验证不明确

问题描述 投票:0回答:8

我正在尝试使用基于令牌的身份验证对 Netsuite API 进行 SOAP 调用。我有一个从 WDSL 生成的 C# 客户端,它正在发送以下请求(已替换机密)。

<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>

我收到以下回复

<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>

我尝试了很多不同的方法来生成签名、随机数和时间戳。目前我有以下内容:

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;
}

有人有什么想法吗?谢谢!

c# .net netsuite suitetalk
8个回答
9
投票

在切换到 TBA 后,我也在努力解决这个无用的错误。结果我仍在发送

ApplicationInfo
属性以及新的
Consumer Key
Consumer Secret

我在 NetSuite 的“SuiteAnswers”网站上找到了此内容,并想在此引用它以供其他仍然遇到此问题的人使用。

不明确的身份验证错误

当您在 Web 服务中使用基于令牌的身份验证 (TBA) 时,如果您将其他身份验证机制与 TBA 标头一起使用,则会返回不明确的身份验证错误响应。

如果除了 TBA 标头之外,您的请求还包含应用程序 ID、带有电子邮件地址和密码的护照对象或有效的 JSESSIONID,您会收到此错误。

以下情况会出现错误:

  • 如果单个 Web 服务请求包含 Passport、TokenPassport 和 SsoPassport 复杂类型的组合。

  • 如果单个 Web 服务请求同时包含 tokenPassport 和 ApplicationInfo 复杂类型,因此在 SOAP 标头中包含应用程序 ID。

来源:Web 服务中基于令牌的身份验证错误


7
投票

getDataCenter 调用不需要护照。我刚刚对 mapSso 函数遇到了同样的问题。看来2017.1发布后他们对不接受护照更加严格了


6
投票

我知道这是一个老问题,但我在同样的问题上苦苦挣扎,并找到了一个可行的解决方案。

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
    };
}

实用方法:

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

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

2
投票

我必须修改 XML 并删除 tokenpassport(account,comsumer key,token,nonce,timestamp) 标签,它起作用了。


1
投票

我不知道在 C# 中使用 HMAC-SHA1 是如何完成的,但在 Javascript 中使用 CryptoJS HMAC-SHA256 你首先对字符串进行签名,然后用 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);

然后你输出它:

'<platformCore:signature algorithm = "HMAC_SHA256">' + HMAC256_Sig_Base64 + '</platformCore:signature>'

1
投票

取出护照。遗憾的是,如果您在使用令牌身份验证时在代码中包含此内容,NetSuite 会失败。 :/


0
投票

根据 Netsuite SuiteTalk SOAP API 文档:

不明确的身份验证错误 当您在 SOAP Web 服务中使用基于令牌的身份验证 (TBA) 时,会出现不明确的身份验证 如果您将其他身份验证机制与 TBA 标头一起使用,则会返回错误响应。 如果除了 TBA 标头之外,您的请求还包含应用程序 ID、护照,您会收到此错误 具有电子邮件地址和密码或有效的 JSESSIONID 的对象。 在以下情况下会出现错误:

■ 如果单个 SOAP Web 服务请求包含 Passport、TokenPassport 和 SsoPassport 复杂类型。

■ 如果单个 SOAP Web 服务请求同时包含 tokenPassport 和 ApplicationInfo 复杂类型,因此在 SOAP 标头中包含应用程序 ID。


0
投票

以下是针对 NetSuite 2023_2 Web 服务更新的完整代码。 (SOAP API)。我更新到 .NET 8 并利用了新功能。还将所有这些身份验证代码重构为自己的服务,很容易像这样调用:

    var authenticator = new TokenBasedAuthenticator();
    _tokenPassport = authenticator.GetTokenPassport(settings.TokenInfo);

基于令牌的身份验证器

internal class TokenBasedAuthenticator : ITokenBasedAuthenticator
{
    public TokenPassport GetTokenPassport(TokenInfo tokenInfo)
    {
        ArgumentNullException.ThrowIfNull(tokenInfo, nameof(tokenInfo));
        ArgumentNullException.ThrowIfNull(tokenInfo.Account, nameof(tokenInfo.Account));
        ArgumentNullException.ThrowIfNull(tokenInfo.ConsumerKey, nameof(tokenInfo.ConsumerKey));
        ArgumentNullException.ThrowIfNull(tokenInfo.ConsumerSecret, nameof(tokenInfo.ConsumerSecret));
        ArgumentNullException.ThrowIfNull(tokenInfo.Token, nameof(tokenInfo.Token));
        ArgumentNullException.ThrowIfNull(tokenInfo.TokenSecret, nameof(tokenInfo.TokenSecret));

        var tokenPassport = new TokenPassport
        {
            account = tokenInfo.Account,
            consumerKey = tokenInfo.ConsumerKey,
            token = tokenInfo.Token,
            nonce = ComputeNonce(),
            timestamp = ComputeTimestamp(),
        };

        var keyBytes = GetSecretBytes(tokenInfo);
        var baseStringBytes = GetIdentificationBytes(tokenPassport);

        using var hmac = new HMACSHA256(keyBytes);
        var hashBaseString = hmac.ComputeHash(baseStringBytes);

        tokenPassport.signature = new TokenPassportSignature
        {
            algorithm = "HMAC-SHA256",
            Value = Convert.ToBase64String(hashBaseString),
        };

        return tokenPassport;
    }

    private static byte[] GetIdentificationBytes(TokenPassport tokenPassport)
    {
        var baseString = tokenPassport.account
                         + "&" + tokenPassport.consumerKey
                         + "&" + tokenPassport.token
                         + "&" + tokenPassport.nonce
                         + "&" + tokenPassport.timestamp;
        var encoding = new ASCIIEncoding();
        return encoding.GetBytes(baseString);
    }

    private static byte[] GetSecretBytes(TokenInfo tokenInfo)
    {
        var key = tokenInfo.ConsumerSecret + "&" + tokenInfo.TokenSecret;
        var encoding = new ASCIIEncoding();
        return encoding.GetBytes(key);
    }

    private string ComputeNonce()
    {
        var randomNumberGenerator = RandomNumberGenerator.Create();
        var data = new byte[20];
        randomNumberGenerator.GetBytes(data);
        var value = Math.Abs(BitConverter.ToInt32(data, 0));
        return value.ToString();
    }

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

代币信息

public class TokenInfo
{
    public string? Account { get; set; }
    public string? ConsumerKey { get; set; }
    public string? ConsumerSecret { get; set; }
    public string? Token { get; set; }
    public string? TokenSecret { get; set; }
}

这是一个很好的对象,您可以从 appsettings.json 加载。

© www.soinside.com 2019 - 2024. All rights reserved.