NetSuite offers various methods for authenticating and integrating with its system. Among these are:
- Token-based Authentication (TBA)
- OAuth 2.0
In this article we are going to focus on the Three-step authorization flow for setting up Token-based Authentication (TBA) in a Laravel application.
Enable Features
First, we enable Token Based Authentication (TBA) feature in our NetSuite Account.
- Log into your NetSuite account
- Navigate to:
Setup
>Company
>Enable Features
.
- Under the Analytics tab, check SuiteAnalytics Workbook.
- Under the SuiteCloud tab, check all options for SuiteScript.
- Under the SuiteCloud tab, check all options for SuiteTalk (Web Services).
- Under the SuiteCloud tab, Manage Authentication sub tab check Token-based Authentication.
- Save your changes
Create an Integration
After successfully enabling the TBA feature, we can then proceed to create an integration if you do not have an existing integration.
Navigate to: Setup
> Integration
> Manage Integrations
> New
Fill out the form, selecting only
Token-Based Authentication
andTBA: Authorization Flow
under theAuthentication
tab.Add a callback URL that matches the URL in your application that will handle NetSuite redirection during the authentication process.
Save the integration and note down the
Consumer ID
andConsumer Secret
.
When you click the save button, do not immediately leave the page because the Consumer ID
and Consumer Secret
will be displayed under the client credentials tab after you click on save. Copy these values and store somewhere safe as we will be needing it when during integration in our application.
Create a TBA Role
After successfully creating an integration, next we need to create a role that would use the token based authentication feature.
Navigate to:
Setup
>Users/Roles
>Manage Roles
>New
.Fill in the name and leave the authentication fields blank.
In the
Permissions
tab, click on theReports
sub tab, add theSuiteAnalytics Workbook
permission set the access level toEdit
.-
In the
Permissions
tab, click on theSetup
sub tab, add the following permissions and set the access level tofull
- Log in using access tokens
- REST web services
- SOAP web services
- User Access Tokens
Save the changes.
Assign User to Role
After creating the TBA role, assign a user to the TBA role you just created.
- Navigate to:
Setup
>Users/Roles
>Manage Users
.
In the list of users that is displayed click on the users name, then click on edit to edit the user details.
Go to the
Access
tab, ensure that the user has access. if the user doesn't, click on the give access checkbox to give the user access and also enter a default password for the user.In the
Roles
sub tab of theAccess
tab, look for the TBA role we created in previous steps in the select dropdown, add the role to the user.Save the changes.
After assigning the TBA role we just created to the user, we can now start our implementation in Laravel using the following credentials we created in previous steps.
Account ID - NetSuite's account ID is crucial for authentication,
and you can find it in your NetSuite URL. For example, in the URL
https://12345.app.netsuite.com, the number 12345 is your account
ID.Client ID (also known as Consumer Key) - We got this when we
created the integration.Client Secret (also known as Consumer Secret) - We got this when
we created the integration.
Using Laravel to Authenticate NetSuite REST API
We can do this in many ways, but our approach here would be to allow different users connect there different NetSuite accounts to our platform as long as they have the TBA role assigned to that user using the credentials obtained (Account ID
,
Client ID
, and Client Secret
).
To set up this authentication flow in your Laravel application, create a form to collect these credentials and configure routes and controllers to handle the authentication flow.
Create Form
Create a form that would collect the credentials we got from previous steps.
Integrating NetSuite API
First, create the callback URL route to handle the response NetSuite returns when the connection is successful. Ensure this callback URL matches the one you specified during the integration setup in NetSuite.
Route::get('/netsuite/callback', [NetSuiteController::class, 'handleCallback'])->name('netsuite.callback');
Next we need to make a POST request to obtain an unauthorized request token from NetSuite. This request returns an oauth_token
and oauth_token_secret
. We will save the oauth_token_secret
to use it a later request while we will use the oauth_token in the next request to redirect the user to NetSuite UI login page using the code below.
public function getRequestToken($formData)
{
// Get details from the form
$callback = route('netsuite.callback'); // Define the callback URL
$accountId = $formData['account_id']; // NetSuite account ID
$consumerId = $formData['consumer_id']; // Consumer ID from NetSuite
$consumerSecret = $formData['consumer_secret']; // Consumer secret from NetSuite
// Structure the parameters that will be passed to the request
$params = [
'oauth_callback' => $callback, // Callback URL
'oauth_consumer_key' => $consumerId, // Consumer key (ID)
'oauth_nonce' => Str::random(32), // Unique nonce for this request
'oauth_signature_method' => 'HMAC-SHA256', // Signature method
'oauth_timestamp' => time(), // Current timestamp
'oauth_version' => '1.0', // OAuth version
];
// Sort parameters by key
ksort($params);
// Encode each key-value pair and join them with '&'
$paramString = [];
foreach ($params as $key => $value) {
$paramString[] = $key . '=' . rawurlencode($value);
}
// Create the base string by combining HTTP method, URL, and parameters
$baseString = strtoupper('POST') . '&' . rawurlencode("https://{$accountId}.restlets.api.netsuite.com/rest/requesttoken") . '&' . rawurlencode(implode('&', $paramString));
// Create the composite key for HMAC-SHA256 hashing
$compositeKey = $consumerSecret . '&';
// Generate the OAuth signature and add it to the parameters
$params['oauth_signature'] = base64_encode(hash_hmac('sha256', $baseString, $compositeKey, true));
// Build the Authorization header for the OAuth request
$header = [];
// Sort parameters by key
ksort($params);
// Encode each key-value pair and format them for the header
foreach ($params as $key => $value) {
$header[] = $key . '="' . rawurlencode($value) . '"';
}
// Combine all parts of the header
$authorizationHeader = 'OAuth ' . implode(', ', $header);
try {
// Send the request to NetSuite to get the request token
$response = Http::withHeaders(['Authorization' => $authorizationHeader])
->post("https://{$accountId}.restlets.api.netsuite.com/rest/requesttoken");
// Handle failed response
if ($response->failed()) {
$responseBody = $response->body();
$decodedResponse = json_decode($responseBody, true) ?? [];
return [ 'error' => $decodedResponse['error']['message'] ];
}
// Get the response
$responseBody = $response->body();
// Parse the response into an array
parse_str($responseBody, $parsedResponse);
// Extract necessary data from the response and form
$oauthToken = $parsedResponse['oauth_token'];
$oauthTokenSecret = $parsedResponse['oauth_token_secret'];
// We are storing the data in session because we will need it to handle the callback when the user is redirect back. We will clear the session data when handling the callback from NetSuite
session([
'oauth_token' => $oauthToken,
'oauth_token_secret' => $oauthTokenSecret,
'account_id' => $accountId,
'consumer_id' => $consumerId,
'consumer_secret' => $consumerSecret,
]);
// Redirect the user to NetSuite for authorization
return redirect("https://{$accountId}.app.netsuite.com/app/login/secure/authorizetoken.nl?oauth_token={$oauthToken}");
} catch (\Exception $e) {
// Handle exceptions and return an error message
return ['error' => $e->getMessage()];
}
}
After we get the Unauthorized Request Token from NetSuite, we redirect the user to NetSuite login UI to login and authorize the token.
The user might be required to connect an authenticator app for 2FA.
Upon entering the verification code and successfully authenticating, the user will be prompted to grant or deny access to the application attempting to connect to NetSuite. By clicking Allow
, the user will be redirected to the callback URL specified during the NetSuite integration setup. This callback URL corresponds to the /netsuite/callback
route we defined in earlier.
When NetSuite redirects the user to the callback URL, it indicates that the connection was successful. At this point, our application needs to handle this callback in our NetSuiteController
. This involves making a follow-up request to NetSuite to obtain the access tokens. These access tokens, specifically the oauth_token
and oauth_token_secret
, are dynamically generated by NetSuite and are essential for authenticating subsequent API requests to NetSuite on behalf of the user. Using the code below we can handle the callback and make another POST request to NetSuite to retrieve the access tokens.
public function handleCallback(Request $request)
{
// Retrieve the tokens we saved in the session in the first step
$sessionOauthToken = session('oauth_token');
$sessionOauthTokenSecret = session('oauth_token_secret');
$sessionAccountId = session('account_id');
$sessionConsumerId = session('consumer_id');
$sessionConsumerSecret = session('consumer_secret');
// Retrieve the query params from the callback request which includes the account Id as company, oauth_verifier and new oauth_token that should match the oauth_token we saved in session for the firs step.
$token = $request->query('oauth_token');
$oauthVerifier = $request->query('oauth_verifier');
$accountId = $request->query('company');
// The oauth token from step one should match the oauth token from step two same as the accountId we sent from the form should match the company query param returned
if ($sessionOauthToken != $token || $sessionAccountId !== $accountId) {
return 'Invalid Request';
}
$params = [
'oauth_token' => $token,
'oauth_verifier' => $oauthVerifier,
'oauth_consumer_key' => $sessionConsumerId,
'oauth_nonce' => Str::random(32),
'oauth_signature_method' => 'HMAC-SHA256',
'oauth_timestamp' => time(),
'oauth_version' => '1.0',
];
// Sort parameters by key
ksort($params);
// Encode each key-value pair and join them with '&'
$paramString = [];
foreach ($params as $key => $value) {
$paramString[] = $key.'='.rawurlencode($value);
}
// Create the base string by combining HTTP method, URL, and parameters
$baseString = strtoupper('POST').'&'.rawurlencode("https://{$accountId}.restlets.api.netsuite.com/rest/accesstoken").'&'.rawurlencode(implode('&', $paramString));
// Create the composite key for HMAC-SHA256 hashing
$compositeKey = $sessionConsumerSecret.'&'.$sessionOauthTokenSecret;
$signedBasedString = hash_hmac('sha256', $baseString, $compositeKey, true);
$signature = base64_encode($signedBasedString);
// Generate the OAuth signature and add it to the parameters
$params['oauth_signature'] = $signature;
// We need the realm to build the header but we do not need it to create the base string
$params['realm'] = $accountId;
// Build the Authorization header for the OAuth request
$header = [];
// Sort parameters by key
ksort($params);
// Encode each key-value pair and format them for the header
foreach ($params as $key => $value) {
$header[] = $key.'="'.rawurlencode($value).'"';
}
// Combine all parts of the header
$authorizationHeader = 'OAuth '.implode(', ', $header);
try {
// Send the request to NetSuite to get the access tokens
$response = Http::withHeaders(['Authorization' => $authorizationHeader])
->post("https://{$accountId}.restlets.api.netsuite.com/rest/accesstoken");
// Handle failed response
if ($response->failed()) {
$responseBody = $response->body();
$decodedResponse = json_decode($responseBody, true) ?? [];
return ['error' => $decodedResponse['error']['message']];
}
// Get the response
$responseBody = $response->body();
// Parse the response into an array
parse_str($responseBody, $parsedResponse);
// Extract the access tokens the response, at this point we have handled the callback and retrieved the access tokens. From here you should save the access tokens somewhere safe and retreievable you will be using these tokens to be subsequent requests to pull in data from NetSuite to your application.
$oauthToken = $parsedResponse['oauth_token'];
$oauthTokenSecret = $parsedResponse['oauth_token_secret'];
// Dump the response to see the data returned
dd($parsedResponse);
// Since we have successfully retrieved the tokens, we can now remove this values from the session as it is no longer needed.
session()->forget([
'oauth_token',
'oauth_token_secret',
'account_id',
'consumer_id',
'consumer_secret',
]);
// Redirect the user to the appropriate page.
return redirect('redirect user to appropriate page after successful login and access token retrieval')
} catch (\Exception $e) {
// Handle exceptions and return an error message
return ['error' => $e->getMessage()];
}
}
Conclusion
After handling the callback and obtaining the access tokens, you can make authenticated requests to the NetSuite API using these tokens. Since the tokens do not expire, it is advisable to store them securely in your database. Re-authenticating will generate new tokens, so saving the tokens helps avoid unnecessary duplications. You can confirm the successful creation of access tokens by visiting the "Manage Access Tokens" page in NetSuite. By saving and reusing these tokens, you can maintain a stable and efficient connection with NetSuite, allowing your application to seamlessly access and manipulate data as needed. This approach leverages the security and convenience of OAuth 1.0 while providing the robustness required for enterprise-level integrations. Happy Coding!
Top comments (0)