Question

I am trying to figure out how to use DotNetOpenAuth (DNOA) to interface to NetSuite's SuiteSignOn. I have a java example I am trying to duplicate the function of, but I am new to OAuth. Here is what I have to work with:

This is the high level of what NetSuite wants to happen:

  1. User logs in to NetSuite, initiating a NetSuite session.

  2. User clicks on one of the following in the NetSuite user interface:

    o A subtab that provides SuiteSignOn access
    o A page displaying a portlet that provides SuiteSignOn access
    o A link for a Suitelet that provides SuiteSignOn access
    o An action button that results in the execution of a user event script that provides SuiteSignOn access

  3. NetSuite generates a token, and sends this token to the external application as the value for the oauth_token URL parameter. This outbound HTTP call also includes a dcand an env URL parameter. These values can be mapped to the URL to be used for NetSuite access (see Mappings of dc and env URL Parameter Values). If any data fields were previously defined as required context for the connection, NetSuite sends values for these fields at the same time.

  4. The external application sends back to NetSuite the token, the consumer key, and its shared secret, along with other information such as the timestamp and nonce, in order to verify the user. The consumer key is a unique identifier for the application provider, generated by NetSuite when the application provider sets up a SuiteSignOn connection. The shared secret is a password defined by the application provider during this setup.

  5. NetSuite responds to the verification, sending any user identification information that was previously defined as necessary for the connection, in XML format. This information may include standard fields like email address or name, or custom fields.

  6. The external application sends the HTML for the landing page, and the page displays. Or, if there is a problem, an error is returned instead.

NetSuite HTTP Outbound Call (got this figured out).

When a user accesses a SuiteSignOn connection point, NetSuite issues an outbound call to start the handshake. The following is an example of this call:

GET /SSO/demoApp.php?oauth_token=01046c1211661d6c6b415040422f0daf09310e3ea4ba&dc=001&env=PRODUCTION HTTP/1.1
Host: externalsystem.com 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-US,en;q=0.5 
Accept-Encoding: gzip, deflate 
Connection: keep-alive 

External Application HTTP Verify Call (trying to prepare this with DotNetOpenAuth).

Upon receipt of the NetSuite HTTP outbound call, the external application needs to issue an HTTP verify call. The following is an example of this call:

GET /app/common/integration/ssoapplistener.nl HTTP/1.0
Host: system.netsuite.com
Authorization: OAuth oauth_token="01046c1211661d6c6b415040422f0daf09310e3ea4ba", oauth_consumer_key="3moWE2ukbW4lohz7", oauth_signature_method="PLAINTEXT", oauth_signature="foobar1%26", oauth_timestamp="1364997730", oauth_nonce="392380036"

NetSuite HTTP Verify Call Response (I can code this).

Upon receipt of the verify call from the external application, NetSuite sends a response. The following is an example of this response:

HTTP/1.1 200 OK
Date: Tue, 16 Apr 2013 13:30:41 GMT
Server: Apache/2.2.17
Set-Cookie: lastUser=1326288_79_3; expires=Tuesday, 23-Apr-2013 13:30:42 GMT; path=/
Set-Cookie: NS_VER=2013.1.0; domain=system.netsuite.com; path=/
X-Powered-By: Servlet/2.5 JSP/2.1
P3P: CP="CAO PSAa OUR BUS PUR"
Vary: User-Agent
Connection: close
Content-Type: text/html; charset=utf-8

<?xml version="1.0" encoding="UTF-8"?>
<outboundSso>
    <entityInfo>
        <ENTITYLASTNAME>Smith</ENTITYLASTNAME>
        <ENTITYINTERNALID>79</ENTITYINTERNALID>
        <ENTITYACCOUNT>1326288</ENTITYACCOUNT>
        <ENTITYFIRSTNAME>John</ENTITYFIRSTNAME>
        <ENTITYEMAIL>jsmith@netsuite.com</ENTITYEMAIL>
    </entityInfo>
</outboundSso>

The excerpts of a Java example using OAuth 1.0a that I'm trying to port to .net/DotNetOpenAuth:

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthMessage;
import net.oauth.client.OAuthClient;
import net.oauth.http.HttpMessage;

<<snip>>

OAuthConsumer consumer = new OAuthConsumer(null, CONSUMER_KEY, SHARED_SECRET, null);
consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, "PLAINTEXT");
OAuthAccessor oauthAccessor = new OAuthAccessor(consumer);

//Get the token from NetSuite
oauthAccessor.accessToken = request.getParameter("oauth_token");

<<snip>>

OAuthMessage rqt = null;
rqt = oauthAccessor.newRequestMessage("POST", ssoVerifyUrl, null);
HttpMessage message = 

rqt.toHttpRequest(OAuthClient.ParameterStyle.AUTHORIZATION_HEADER);
                verifyConnection.setRequestProperty("Authorization", 

message.getHeader("Authorization"));

Being new to OAuth and DotNetOpenAuth, I'm fumbling around.

  • What is the proper replacement for OAuthConsumer in DNOA in this situation? WebConsumer? DesktopConsumer?
  • Assuming I need such a consumer, how much of the ServiceProviderDescription do I need to provide? I only have one endpoint (/app/common/integration/ssoapplistener.nl), I'm not sure if that is a Request, Access, or other type of endpoint.
  • What is the proper replacement for OAuthAccessor in DNOA?

Thanks for any assistance, Bo.

Was it helpful?

Solution

Ok, after a lot of digging and experimenting, I got DotNetOpenAuth to work with NetSuite's SuiteSignOn. It may not be perfect, but it does work!

I got my tokenmanager from this post:

https://developer.yahoo.com/forum/Fantasy-Sports-API/Authenticating-with-NET-using-DotNetOpenAuth/1279209867000-4eee22f1-25fd-3589-9115-1a835add3212

using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Extensions.OAuth;

// In my Page_Load method, I receive the GET request from NetSuite:
public partial class sso_page : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
// This is what the NetSuite SuiteSignOn ConnectionPoint sends:
// GET /administratorportal/SSO/sso_page.aspx?oauth_token=08046c1c166a7a6c47471857502d364b0d59415418156f15db22f76dcfe648&dc=001&env=SANDBOX
// see the NetSuite SuiteSignOn doc about dc & env processing to build endpoints

ServiceProviderDescription provider = GetServiceDescription();

// Set up OAuth with our keys and stuff
string token = Request.Params["oauth_token"];
string consumerKey = "yourconsumerkey";    // this has to match what is defined on our NetSuite account - ConnectionPoint to CRMLink
string sharedSecret = "yoursharedsecret";        // this has to match what is defined on our NetSuite account - ConnectionPoint to CRMLink - Careful - NO funny chars like '!'

// I got this InMemoryTokenManager from another DotNetOpenAuth post in SO
InMemoryTokenManager _tokenManager = new InMemoryTokenManager(consumerKey, sharedSecret);
AuthorizationApprovedResponse authApprovedResponse = new AuthorizationApprovedResponse();
authApprovedResponse.RequestToken = token;

_tokenManager.StoreOpenIdAuthorizedRequestToken(consumerKey, authApprovedResponse);

WebConsumer consumer = new WebConsumer(provider, _tokenManager);

// this is the SSO address in netsuite to use.  Should be production or sandbox, based on the values of dc and env
string uri = "https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl";
                MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint(uri, methods);

WebRequest verifyRequest = consumer.PrepareAuthorizedRequest(endpoint, token );
HttpWebResponse responseData = verifyRequest.GetResponse() as HttpWebResponse;

XDocument responseXml;
responseXml = XDocument.Load(responseData.GetResponseStream());

// process the SSO values that come back from NetSuite in the XML  They should look something
// like the following:
/* XML response should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<outboundSso>
    <entityInfo>
         <ENTITYINTERNALID>987654</ENTITYINTERNALID>
         <ENTITYNAME>Fred</ENTITYNAME>
         <ENTITYEMAIL>fred@yourcompany.com</ENTITYEMAIL>
    </entityInfo>
</outboundSso>
*/

// If that data looks good, you can mark the user as logged in, and redirect to whatever
// page (like SSOLandingPage.aspx) you want, which will be shown inside a frame on the NetSuite page.

Response.Redirect("~/SSOLandingPage.aspx", false);

// If that data looks bad, invalid user/login?  Then you could respond with an error or redirect to a login.aspx page or something.

There is some other error handling and different returns depending on what happens, but the above is the basics of receiving an SSO login from NetSuite SuiteSignOn.

This was a hardcoded ServiceProviderDescription I used. You need to read the NetSuite SuiteSignOn doc to understand how to dynamically build these endpoints based on values of dc and env, I did not do that here yet.

// I'm not completely sure why I need all these endpoints below, and since I provide an endpoint as such:
//                 MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint(uri, methods );
// these don't seem like I need them.  But I need a ServiceProviderDescription to create a consumer, so...
private ServiceProviderDescription GetServiceDescription()
{
    return new ServiceProviderDescription
    {
        AccessTokenEndpoint = new MessageReceivingEndpoint("https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl", HttpDeliveryMethods.GetRequest),
        RequestTokenEndpoint = new MessageReceivingEndpoint("https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl", HttpDeliveryMethods.GetRequest),
        UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://system.sandbox.netsuite.com/app/common/integration/ssoapplistener.nl", HttpDeliveryMethods.GetRequest),
        ProtocolVersion = ProtocolVersion.V10a,
        TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new PlaintextSigningBindingElement() }
    };
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top