Search code examples
typescriptoauthnetsuiteaccess-token

NetSuite OAuth 1.0 signature REST Api


I have read through the docs and every post, on Stack Overflow, I could find on creating a signature for NetSuite OAuth1.0 with TBA. From what I can see I am inputting all of the requirements and correctly creating the key however when I make the call to the server I am still getting

"Refused to set unsafe header "Cookie"

I have done the following:

  • made a successful connection through postman
  • disabled cors policy in chrome and am making calls there
  • I have tried every possible combination I can as far as the parameters added to the base string.

I can confirm I have been unable to replicate the signature from postman (using the same parameter values) however I did read a post that, that does not necessarily indicate I am wrong since postman uses some other PM specific parameters/variables (not sure how valid that is.)

I have followed the documentation here: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_157652390285.html as well as all of the related docs.

the following post is the closest to answering the questions however NetSuite no longer supports HMAC-SHA1, but other than me changing to HMAC-SHA256 I am providing all of the same parameters.(and then some per the docs.)

similar question with answer that I believe I am matching minus some additions from NS docs: Netsuite Rest Web Services Signature

One problem I am having is that all of the docs use PHP so I am trying to match that with the javascript equivalent. but I have never worked in php so I am no sure if I am using the wrong encoding methods or not.

        import axios from 'axios';
        import { nanoid } from 'nanoid';
        import hmacSHA256 from 'crypto-js/hmac-sha256'
        import base64 from 'crypto-js/enc-base64'

        function encodeRFC3986URIComponent(str: string) {
            return encodeURIComponent(str)
                .replace(
                    /[!'()*]/g,
                    (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
                );
        }

        const parameters = {
            grant_type: "client_credentials",
            oauth_consumer_key: consumerKey,              
            oauth_signature_method: "HMAC-SHA256",
            oauth_timestamp: timeStamp,
            oauth_token: accessToken,
            oauth_nonce: nonce,
            oauth_version: "1.0",
        };

        const signatureKey = encodeURIComponent(consumerSecret) + '&' + encodeURIComponent(tokenSecret)
        console.log('signatureKey: ', signatureKey)

        var baseString = httpMethod + '&' + requestURL;

        for (const key in parameters) {
            baseString += "&" + key + "=" + parameters[key as keyof object];
        }

        const encodedBaseString = encodeRFC3986URIComponent(baseString)

        const signature = base64.stringify(hmacSHA256(encodedBaseString, signatureKey));

        let data = JSON.stringify({
            "q": "SELECT id, entityid FROM employee WHERE custentity_time_badge = '12345'"
        });

        let authorizationHeader = 'OAuth realm=' + OauthRealm

        for (const key in parameters) {
            authorizationHeader += ',' + key + "=" + parameters[key as keyof object];
        }
        authorizationHeader += ',oauth_signature=' + signature
        authorizationHeader = JSON.stringify(authorizationHeader)

        let config = {
            method: 'post',
            maxBodyLength: Infinity,
            url: 'https://5283244-sb2.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql',
            headers: {
                'prefer': 'transient',
                'Content-Type': 'application/json',
                'Authorization': authorizationHeader,
                'Cookie': 'NS_ROUTING_VERSION=LAGGING'
            },
            data: data
        };

        axios.request(config)
            .then((response) => {
                console.log(JSON.stringify(response.data));
            })
            .catch((error) => {
                console.log(error);
            });

Solution

  • Thanks to Krypton for getting me pointed in the right direction. after he helped me isolate the problem to truly being the wrong credentials, I went to the following doc and started from scratch to create a signature that matched the signature in the doc.

    Doc: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_1534941088.html

    After I worked out the issues that were causing the signature to be incorrect I had a working example.

    and now my code is as follows

            const parameters = {
                oauth_consumer_key: consumerKey,
                oauth_nonce: nonce,
                oauth_signature_method: "HMAC-SHA256",
                oauth_timestamp: timeStamp,
                oauth_token: tokenKey,
                oauth_version: "1.0",
            };
    
            let e_baseString = httpMethod + '&' + encodeURIComponent(requestURL) + '&'
            let firstiteration = 0
            for (const key in parameters) {
                if (firstiteration === 0) {
                    e_baseString += key + "%3D" + parameters[key as keyof object];
                    firstiteration = 1
                } else {
                    e_baseString += "%26" + key + "%3D" + parameters[key as keyof object];
                }
            }
            
            const e_key = encodeURIComponent(consumerSecret) + '&' + encodeURIComponent(tokenSecret)
            let signature = base64.stringify(hmacSHA256(e_baseString, e_key))
            signature = encodeURIComponent(signature)
    
            let authorizationHeader = 'OAuth realm="' + OauthRealm + '"'
            for (const key in parameters) {
                authorizationHeader += ',' + key + '="' + parameters[key as keyof object] + '"';
            }
            authorizationHeader += ',oauth_signature="' + signature + '"' 
    
            console.log('authorizationHeader: ', authorizationHeader)
    
            let data = JSON.stringify({
                "q": "SELECT id, entityid FROM employee WHERE custentity_time_badge = '123456'"
            });
    
            let config = {
                method: 'post',
                maxBodyLength: Infinity,
                url: 'https://5283244-sb2.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql',
                headers: {
                    'prefer': 'transient',
                    'Content-Type': 'application/json',
                    'Authorization': authorizationHeader,
                },
                data: data
            };
    
            axios.request(config)
                .then((response) => {
                    console.log(JSON.stringify(response.data));
                })
                .catch((error) => {
                    console.log(error);
                });
    

    Once again, Thanks Krypton I am working with multiple new frameworks/libraries ect. you helping me to isolate the problem was a huge help.