Populating the user object with passport.js and Salesforce OAuth

Using passport.js is a great option for doing authentication in node.js applications with great strategies for authenticating through just about anything on the planet including Salesforce. Using passport.js with Salesforce involves using the OAuth2Strategy but the user object in the session is not usable really as I really want actual information about the user to be there. The solution I came up with was overriding the userProfile-method and adding a call to the Salesforce userinfo endpoint as shown below.

// configure authentication using oauth2
app.use(passport.initialize());
passport.serializeUser(function(user, done) {
    done(null, user.username);
});
passport.deserializeUser(function(login, done) {
    done(undefined, {
        "username": login
    });
});

OAuth2Strategy.prototype.userProfile = function(accessToken, done) {
    this._oauth2.get(`https://${process.env.SF_LOGIN_URL || "login.salesforce.com"}/services/oauth2/userinfo`, accessToken, function (err, body, res) {
        if (err) { return done(new InternalOAuthError('Failed to fetch user profile', err)); }
        try {
            let json = JSON.parse(body);
            let profile = {
                "provider": "Salesforce.com",
                "username": json.preferred_username,
                "name": json.name,
                "email": json.email,
                "firstname": json.given_name,
                "lastname": json.family_name,
                "payload": json
            };
            
            done(null, profile);
        } catch(e) {
            done(e);
        }
    });
}

passport.use(new OAuth2Strategy({
        authorizationURL: `https://${process.env.SF_LOGIN_URL || "login.salesforce.com"}/services/oauth2/authorize`,
        tokenURL: `https://${process.env.SF_LOGIN_URL || "login.salesforce.com"}/services/oauth2/token`,
        clientID: process.env.SF_CLIENT_ID,
        clientSecret: process.env.SF_CLIENT_SECRET,
        callbackURL: process.env.SF_CALLBACK_URL
    },
    function(accessToken, refreshToken, profile, cb) {
        cb(undefined, profile);
    }
));

The interesting piece is really the code in bold where I inject a call to /services/oauth2/userinfo to get information about the user and then add that as the user object.

Of course after having done all this I found passport-salesforce which is a strategy that does exactly the same thing – duh!!! Anyways it was fun to code it up.

Authentication vs. Authorization

When ever I talk to customers and partners about single-sign-on (SSO) and the concepts of “authentication” I’m quite often baffled by the level of misunderstanding, misconception and lack of knowledge about just how “authentication” works. Now the reason I put “authentication” is quotes is that when we talk about authentication it’s really not just authentication we’re talking about. When we talk about confirming the identity of a user and confirming that the user is allowed to access a given resource we actually talk about two related concepts which are always performed – 1) authentication and 2) authorization.

Authentication is the process by which we authenticate the user i.e. find out that the user is authentic and is who he or she claims he or she is. This is most often done using a username and a password but there are numerous ways of doing this. The authentication step is most often followed immediately by authorizing the user to perform a certain task or access a certain resource. A user may be authenticated but not authorized i.e. we know who the user is with certainty but the user is not allowed to perform the operation or access the resource he or she set out to perform or access. A user may also be authorized without being authenticated in which case we allow an non-authenticated user (e.g. Anonymous in IBM Domino) to have certain rights in the system. The latter is the case where an Anonymous user is allowed to open a database, browse a web site etc.

The failure to distinguish between these two steps can lead to a lot of SSO issues and errors not being easily understood and/or debugged. Consider the case where a user signs into System A (i.e. authenticates and the user account authorized to perform the task at hand) and then moves on to System B to access another resource or perform a task which the user is “suddenly” unable to perform. Why? The user was able to work in System A so why not in System B?

This is the time where the person debugging this should stop and think…

Is the reason not because the user isn’t authenticated but because he or she isn’t authorized? Could it be that the user is authenticated both by System A and System B but that System B wasn’t able to authorize the user? Maybe because the username was in another format or the particular username permutation not listed in the ACL or group authorizing the user to work in System B. Knowing about the steps performed can make it a lot easier (or just possible) to diagnose.

Using a good old Notes database with a wide open ACL and a simple page displaying the username of the current user (i.e. using @UserName in a piece of computed text) can make all the difference. It easily shows issues with the user being known by a LDAP username (e.g. cn=Mikkel Flindt Heisterberg,ou=Users,dc=intravision,dc=dk) and not by a Domino distinguished name (CN=Mikkel Flindt Heisterberg/O=IntraVision) or SSO not working due to misconfigured domain names, no shared secrets etc. by the user being known as “Anonymous” because the LtpaToken(2) was never sent to the server due to the cookie rules not being fulfilled. Same goes for Websphere Application Server – a simple WAR based application with a servlet displaying the current username is great for debugging.

Hope it clarifies matters and is helpful to someone.