pythonrestoauth-2.0netsuiteazure-synapse

M2M Client Credential Flow between NetSuite and Synapse


I am looking to create a flow somewhere in the Azure stack to allow me to get M2M authentication between Azure Synapse and NetSuite. The goal is to be able to drop the use of the ODBC connector and switch to making REST calls for pulling data from NetSuite into Synapse.

I have followed the documentation as best as I can from Oracles Help. The integration record is created, so the only thing I am missing is what needs to happen on the Azure side to get the two systems to authenticate with each other.

I have reviewed this post but this looks like it would be a setup for an API to access Azure resources, where I want Azure Synapse to get the data from NetSuite.

UPDATE: I was able to get M2M to work within NetSuite using Postman by following this video. I am still trying to figure out how to get this created in Azure to allow me to create a JWT to call NetSuite from Synapse.

UPDATE 2: I am now using a spark pool to sign the JWT and when it is run locally I am able to authenticate and get an access token returned.

    import time
    import jwt
    import http.client
    
    sec = '-----BEGIN PRIVATE KEY-----\nMIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDkW+cE7G05KETS\n#################################################################################\n-----END PRIVATE KEY-----'
    jwtheader = {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "rn9dnIL###############"
    }
    iat = round(time.time(),3)
    
    payload_data = {
        "iss": "6477f8bea7f####################",
        "scope": ["restlets", "rest_webservices", "suite_analytics"],
        "aud": "https://#######.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token",
        "exp": iat + 3600,
        "iat": iat
    }
    token = jwt.encode(payload=payload_data,key=sec,headers = jwtheader)
    conn = http.client.HTTPSConnection("#######.suitetalk.api.netsuite.com")
    payload = 'grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion='+token
    headers = {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
    conn.request("POST", "/services/rest/auth/oauth2/v1/token", payload, headers)
    res = conn.getresponse()
    data = res.read()
    print(data.decode("utf-8"))

However when I use a spark pool in Azure Synapse I am returned with {"error":"invalid_grant"}. With no change in code. I have tried both the requests library as well as the http.client(shown above).


Solution

  • I received the same error when copying your template here. But for me it worked once I (1) changed the algorithm in the jwtheader dict to PS256 instead of RS256, and (2) fetched the certificate key without modification using with-open:

    with open('<path-to-my-cert-key>/auth-key.pem', 'r') as fh:
        sec = fh.read() # <-- just as it is
    
    jwtheader = {
        "alg": "PS256", # <-- RS256 didn't work for me
        "typ": "JWT",
        "kid": "<my-certificate-id-from-netsuite>"
    }
    

    And I can mention that I used this openssl command to generate my certificate, which was the recommended one from Netsuite's docs:

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

    Hope this works for others as well!

    PS. Make sure you download pyjwt and not jwt as I did. It wasn't the same framework, as time would tell me..