4

Hi Stack Overflow community, thanks in advance for your help:

Question for Community: Instead of reinventing the oauth wheel, are there any tips / best practices for creating my own oauth signature/nonce within my python GET requests for Netsuite's new API (REST Web Services; see below for context of question)? It seems that other folks who have been successful at this have done it through trial and error which is my plan as well, but ideally I'd like to have fewer errors and again, not reinvent the wheel. Any tips, tricks, ideas are greatly welcome. See context below

What: Attempting to make a GET request using Netsuite's brand new REST API (REST Web Services). This is a different API than their SOAP/ RESTlets.

How: Through writing Python script in Visual Studio Code. I am successful at making the request in Postman. I copied the code into Visual Studio Code that Postman used to make the successful GET request and received a 401 response (see below).

Problem Encountered: I receive a 401 response, invalid login. There is no official Netsuite documentation on how make a successful interaction with this new REST API outside of Postman, so after reading through StackOverflow and other blogs/publications it seems that I need to create my own oauth_signature, oauth_timestamp, and oauth_nonce.

Postman GET Request Code:

import requests

url = "https://123456-sb1.suitetalk.api.netsuite.com/services/rest/query/v1/workbook/custworkbook12345/result"

payload = {}
headers = {
  'Authorization': 'OAuth realm="123456_SB1",oauth_consumer_key="123456789101112131415",oauth_token="123456789101112131415",oauth_signature_method="HMAC-SHA256",oauth_timestamp="123456789",oauth_nonce="123456789",oauth_version="1.0",oauth_signature="123456789101112131415"',
  'Cookie': 'NS_ROUTING_VERSION=LAGGING'
}

response = requests.request("GET", url, headers=headers, data = payload)

print(response.text.encode('utf8'))


Thanks in advance!

trevoraaron
  • 93
  • 2
  • 10
  • 2
    is there a python oauth library that can you use? i would start there instead of building the header yourself. this oauth header value has some complexity to it. something like https://oauthlib.readthedocs.io/en/latest/oauth1/client.html ? – Josh Oct 01 '20 at 23:44
  • Thanks Josh, I'll take a look at oauthlib and get familiar with how it works/give it a try. It looks like it supports HMAC-SHA256, is there anything I should pay attention to when signing with HMAC-SHA256 vs. HMAC-SHA1? – trevoraaron Oct 02 '20 at 18:36
  • 1
    i just looked at my code, i am using HMAC-SHA256. and there was something very funky about oauth1, like you have to specify `signature_method` as 'HMAC-SHA256' and then also use the python hashing library call and actually do the hmac using 256. hopefully whatever python lib you end up using does this all secretly for you, it gets confusing. – Josh Oct 02 '20 at 19:39
  • One fun note, in case it helps you: nonce must be generated close in time to request call. if you do something like generate it then wait 5 minutes, you will get a 401 because the nonce "expires" after something like 30-60 seconds. – Josh Oct 02 '20 at 19:41
  • And netsuite just released some update about realm being optional, i still have not bothered to look into it, but just fyi, realm might actually be optional now, and also, netsuite docs may be hilariously out of date or wrong as usual. – Josh Oct 02 '20 at 19:43
  • Realm is annoying as it is in the header but needs to be excluded from what is signed. Some oauth libraries don't support it and you can't add it as a regular auth parameter because it will then get included in what is signed and cause an auth failure. – Brian Oct 05 '20 at 20:15
  • Testing in Postman - dropping realm gets authorization failure so it still wants/needs it. – Brian Oct 05 '20 at 21:02
  • Brian, I can also validate that Netsuite does still require the realm. Although that could be in part to how I setup the integration. – trevoraaron Oct 09 '20 at 06:44
  • Josh, thanks for the suggestion of oauthlib, I'm still reading the documentation and getting familiar with how to use this in my Netsuite API requests. I'm having a bit of a difficult time understanding the part referenced in the docs regarding callback URL. I never needed a callback URL during setup, and obtained the client key/secret, and token key/secret. Any tips on how to navigate the docs / implement this or other libraries? – trevoraaron Oct 09 '20 at 06:55

1 Answers1

2

Thanks to Josh's recommendation (see comments to my original question) I successfully used oauthlib's oauth1 client to send a request in Visual Studio Code. The nonce and signature look a little different than what Postman shows in their code snippet, but it did work. For anyone attempting the same thing with Netsuite's REST Web Services I suggest going this route.

My code that sent a successful GET request:

import requests
import oauthlib.oauth1
import json


url = "https://12345-sb1.suitetalk.api.netsuite.com/services/rest/query/v1/dataset/custdataset1/result"

payload = {}

client = oauthlib.oauth1.Client('consumer key', client_secret='12345',
    resource_owner_key='12345', resource_owner_secret='12345', realm='12345_SB1',signature_method="HMAC-SHA256")
url, headers, body = client.sign('https://4635201-sb4.suitetalk.api.netsuite.com/services/rest/query/v1/dataset/custdataset1/result')


response = requests.request("GET", url, headers=headers, data = payload)

print(response.text.encode('utf8'))

A Few Additional Helpful Notes -

  • I'm testing this in Netsuite Sandbox, hence the realm "12345_SB1". If you aren't in sandbox you shouldn't need the underscore SB. Just use your account ID.
  • I'm pulling Netsuite Analytics Report, which at this time is still in beta for the new API (REST Web Services).
  • I used the Python oauthlib that Josh recommended and I recommend you do the same, link here
trevoraaron
  • 93
  • 2
  • 10
  • 2
    How were you able to get your consumer_key, client_secret, resource_owner_key and resource_owner_secret? I used SHA1 for years and the keys/secrets that came with the integration were used. But now Im trying to switch to SHA256 and the same keys give invalid login errors. – Peter Andreoli Jul 20 '21 at 18:26
  • When using params in the GET request, do those not need to be incorporated into the url you use to get the headers? I get unauthorized response ONLY when a q param (query) is present (in a journalEntry list call). Of course, when I provide *no* param, I get no results either (but login audit trail *does* show successful login in that instance). – HaPsantran May 27 '22 at 07:12