如何使用 OAuth 2.0 客户端凭据流和带有 .net 核心的 JWT 证书连接到 Oracle Netsuite

Posted

技术标签:

【中文标题】如何使用 OAuth 2.0 客户端凭据流和带有 .net 核心的 JWT 证书连接到 Oracle Netsuite【英文标题】:How do I connect to Oracle Netsuite using OAuth 2.0 Client Credentials Flow and JWT certificate with .net core 【发布时间】:2022-01-06 13:31:29 【问题描述】:

我正在尝试使用 Netsuite Rest api。以下是我采取的步骤。 https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_162730264820.html

    在 Netsuite 中创建了集成记录

    创建自签名证书:

    openssl req -x509 -newkey rsa:4096 -sha256 -keyout auth-key.pem -out auth-cert.pem -nodes -days 730
    

    auth-cert.pem 添加到 Netsuite 的集成中

    尝试调用 TokenUrl 端点获取访问令牌

当我调用 GetNetsuiteJwtAccessToken(string signedJWTAssertion) 从 TokenUrl 获取访问令牌时,我不断收到错误请求(状态代码 400)。

static void Main(string[] args)
    //static string Scope = "rest_webservices"; 
    //static string Aud = "https://<Tenant>-sb1.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token";
    //static string TokenUrl = "https://<Tenant>-sb1.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token";
//static string TenantName = "<Tenant>";

    //static string ClientId = "<ClientId>";
    //static string Issuer = ClientId;
    //static string ClientSecret = "<Client Secret>";
    //static string AppId = "<AppId>";
    //static string Kid = "<Key from the Netsuite for the uploaded Cert">;

      var jwt= GenerateNetsuiteJWTFromPEMFile("auth-key.pem");
      var accessToken = GetNetsuiteJwtAccessToken(signedJWTAssertion: jwt);
               


public static string GenerateNetsuiteJWTFromPEMFile(string PEMFile)

            var tokenHandler = new JwtSecurityTokenHandler();

            var rsaPem = File.ReadAllText(PEMFile);

            var privatekey = RSA.Create();
            privatekey.ImportFromPem(rsaPem);

            var key = new RsaSecurityKey(privatekey);
            //key.KeyId = Kid;

            var signingCredentials = new SigningCredentials(
                key: key,
                algorithm: SecurityAlgorithms.RsaSha256 
            );
            //signingCredentials.Key.KeyId = Kid;


            var Now = DateTimeOffset.UtcNow;
            var Exp = Now.AddMinutes(30).ToUnixTimeSeconds();
            var Iat = Now.ToUnixTimeSeconds();

            var jwt = new SecurityTokenDescriptor
            
                Issuer = Issuer,
                Claims = new Dictionary<string, object>()
                
                    ["iss"] = Issuer,
                    ["scope"] = Scope,
                    ["aud"] = Aud,
                    ["exp"] = Exp,
                    ["iat"] = Iat                
                ,

                SigningCredentials = signingCredentials

            ;
            var jws = tokenHandler.CreateToken(jwt);
            var encoded = new JwtSecurityTokenHandler().WriteToken(jws);
            return encoded;
        

 public static string GetNetsuiteJwtAccessToken(string signedJWTAssertion)
        
            string accessToken;

              HttpClient _httpClient = new HttpClient();

            _httpClient.DefaultRequestHeaders.Clear();

            var requestParams = new List<KeyValuePair<string, string>>
            
                new KeyValuePair<string, string>("grant_type", "client_credentials"),
                new KeyValuePair<string, string>("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
                new KeyValuePair<string, string>("assertion", signedJWTAssertion)
            ;

            using (var content = new FormUrlEncodedContent(requestParams))
            
                var response = _httpClient.PostAsync(TokenUrl, content).Result;
                var responseContent = response.Content.ReadAsStringAsync().Result;
                accessToken = responseContent;
            

            return accessToken;
        

【问题讨论】:

【参考方案1】:

我遇到了完全相同的问题,这就是我的解决方法。

下面的函数实际发送请求:

public async Task GetAccessToken()
        
            string tokenBaseUrl = <token endpoint URL>;
            string consumerKey = <consumer key/client ID from NetSuite>;

           // Don't worry about _configurationService below        
           string assertion = new JwtToken(_configurationService).GetJwtToken(consumerKey);

            var parameters = new Dictionary<string, string>
            
                "grant_type", "client_credentials" ,
                "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" ,
                "client_assertion", assertion  // use client_assertion, not assertion, the example provided in the docs uses the former 
            ;

            var content = new FormUrlEncodedContent(parameters);

            var response = await _httpClient.PostAsync(tokenBaseUrl, content);
        

您可以在最后从响应中提取访问令牌。我还没有做到这一点。

现在神奇的发生在下面的函数中,它创建了 JWT 令牌:

public string GetJwtToken()
        

            try
            
                // Read the content of a private key PEM file, PKCS8 encoded. 
                string privateKeyPem = File.ReadAllText(<file path to private key>);

                // keep only the payload of the key. 
                privateKeyPem = privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "");
                privateKeyPem = privateKeyPem.Replace("-----END PRIVATE KEY-----", "");

                // Create the RSA key.
                byte[] privateKeyRaw = Convert.FromBase64String(privateKeyPem);
                RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
                provider.ImportPkcs8PrivateKey(new ReadOnlySpan<byte>(privateKeyRaw), out _);
                RsaSecurityKey rsaSecurityKey = new RsaSecurityKey(provider);

                // Create signature and add to it the certificate ID provided by NetSuite.
                var signingCreds = new SigningCredentials(rsaSecurityKey, SecurityAlgorithms.RsaSha256);
                signingCreds.Key.KeyId = <certificate ID provided when auth cert uploaded to NetSuite>;

                // Get issuing timestamp.
                var now = DateTime.UtcNow;

                // Create token.
                var handler = new JsonWebTokenHandler();

                string token = handler.CreateToken(new SecurityTokenDescriptor
                
                    Issuer = <consumer key/client ID>,
                    Audience = <token endpoint URL>,
                    Expires = now.AddMinutes(5),
                    IssuedAt = now,
                    Claims = new Dictionary<string, object>   "scope", new[]  "rest_webservices"   ,
                    SigningCredentials = signingCreds
                );

                return token;
            
            catch (Exception e)
            
                throw new <custom exception>("Creating JWT bearer token failed.", e);
            

这会返回状态 200,所以如果它仍然不适合您,我会仔细检查您是否正确设置了所有 NetSuite 0Auth 2.0 设置。

【讨论】:

谢谢鲁哈!工作并可以获得访问令牌。现在得到 403。您是否遇到过任何示例 c# 代码来让它为 GET/POST 返回良好的数据?请指教 这听起来像是权限问题。我没有遇到过这种情况,但我会介绍您为 API 创建的角色及其分配的权限。希望就是这样。

以上是关于如何使用 OAuth 2.0 客户端凭据流和带有 .net 核心的 JWT 证书连接到 Oracle Netsuite的主要内容,如果未能解决你的问题,请参考以下文章

使用 OAuth 2.0 客户端 ID 为不同项目创建一个或多个凭据?

OAuth 2.0客户端凭据获取用户ID

如何保护 Oauth 2.0 客户端 ID 和客户端密码

如何在 Azure 逻辑应用中使用 OAuth 2.0 身份验证?

如何使用带有 AsynceTask 的 Oauth 2.0 向自定义 api 发送 http 请求

Oauth 2.0:客户端 ID 和客户端密码暴露,这是一个安全问题吗?