If you’ve found your way here, you are probably close to losing hope. Do not fear, because the answer to all your problems lies within.
In the 3 years this post has been up, I have received many comments telling me how useful it is and how people finally got their solutions working with my help. At the time of original writing, OAuth 1.0 (SHA1 based) was basically your only option if you wanted to access RESTlets using an external application. Now with NetSuite supporting OAuth 2.0, OAuth 1.0 is old and dirty, and really should not be deployed for use in new projects, especially not those using modern application frameworks e.g .NET Core. At least, that is what you would assume…
Nope! While using OAuth 2.0 would be the “right” thing to do, and that is certainly what I try to do in the first instance, it is nothing short of a soul-draining nightmare to get this set up with NetSuite. Not because OAuth 2.0 is hard - certainly not, OAuth 2.0 and OpenID are even pleasant to work with outside of a NetSuite context. But, as usual, NetSuite’s implementation is… rough. As I typically work on integration / automated applications, the flavour of OAuth 2.0 I am talking about here is the Client Credentials Flow. I can’t comment on the ease of setup of the other authorization flows, though they do appear to be a bit simpler. So, despite it being out of date, OAuth 1.0 still works and is much more straightforward to implement.
So instead of retiring this post, I have cleaned it up to more modern standards. Enjoy!
If you want to implement OAuth 2.0 instead - see my post on that here.
In NetSuite, head to Setup > Integration > Manage Integrations > New.
Just set a name for your integration, and ensure State = Enabled, and Token-Based Authentication is checked.
After clicking save, your Client ID and Client Secret will be displayed at the bottom of the screen. Copy them! You will never see them again.
In NetSuite, head to Setup > Users/Roles > Access Tokens > New.
Select your integration record, and the User and Role that the integration will use.
After clicking save, your Token ID and Token Secret will be displayed at the bottom of the screen. Copy them! You will never see them again.
- Realm – You can find it under Setup > Integration > Web Services Preferences, where it is labelled Account ID.
- The URL of your RESTlet
You’ll be needing this class. It generates your signature for you. I originally found this on the now-deleted MSDN forums. I reworked it to remove some of the manual parts, and now this may not work for non-NetSuite applications. Let me know in the comments how you go if you try.
It is a static class with a single method that we care about:
public static string GenerateAuthorizationHeaderValue(Uri uri, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string realm)
Time to generate the header. If you want to learn the full details of this process, this article is really good.
All we have to do is feed everything into the GenerateAuthorizationHeaderValue method.
Uri uri = new Uri("https://[accountId].restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=[scriptId]&deploy=[deployId]")
Then all you have to do is provide your header value as the Authorization header when you make the request.
In modern .NET you would create an HttpRequestMessage. You may use a different technique if you are working with older systems.
var message = new HttpRequestMessage
If you’re designing a brand new application, you may want to consider an architecture similar to this:
- A NetsuiteService to make requests to NetSuite
- An OAuthService to create OAuth-compatible HttpRequestMessage objects
- A DelegatingHandler to automatically apply OAuth 1.0 signatures to any request made to NetSuite.
Set up a NetsuiteOptions class to hold all of the NetSuite connection settings:
This is how my appsettings.json looks:
Notes on the above:
- Get and Post are generic methods so we can specify the input and output classes at the call site
- Injected are a NetsuiteOptions and an IHttpClientFactory
- On lines 30 and 56 we are creating named HttpClients. This allows us to pull in previously-defined custom behaviour based on the name alone
Notes on the above:
- SignRequest accepts an HttpRequestMessage and returns an HttpRequestMessage that has a valid OAuth 1.0 Authorization header
- In its current state it could be a static class instead of a service, but this is more flexible in case it needs further dependencies
- The headers from the original message are replaced / lost. You should adapt this if you care about the original headers
Notes on the above:
- We inherit from the default DelegatingHandler (basically what an HttpClient is)
- We override the SendAsync method to first turn the request into a signed request, and then pass it along to the standard SendAsync method
- We log any exceptions manually because .NET suppresses them here
You’ll need to add all of the below to the ConfigureServices method in Startup.cs:
- We are manually binding the Netsuite section of our appsettings to a NetsuiteOptions object, which is added to the service collection
- This lets us use NetsuiteOptions as a dependency in our services instead of IOptions
- AddHttpClient() adds the default IHttpClientFactory to the service collection, plus dependencies
- AddTransient<OAuthService>() adds our OAuthService to the service collection
- AddTransient<INetsuiteService, NetsuiteService>() adds our NetsuiteService to the service collection
- AddHttpClient(“netsuite”) adds a named HttpClient to the service collection. When we use _httpClientFactory.CreateClient(“netsuite”) in our services, it will use the configuration provided here
- AddHttpMessageHandler wires up our custom DelegatingHandler to instances of this named HttpClient
Now when you inject NetsuiteService elsewhere, you can use it to make requests to NetSuite just like this:
var parameters = new Dictionary<string, string>
And all of the OAuth is done quietly for you. Nice!
Please leave a comment if you found this helpful or have any questions.
The full source code of this example is available on GitHub.