Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restrict login to specific domain using Node Passport with Google Auth

I am implementing Google Auth on an internal service at work. It is a JS client heavy application with a Node backend. I am choosing to use the Node module Passport.js with the passport-google-oauth strategy.

I have successfully got it working but one thing is still confusing me. I want to ensure my application allows only company employees to login. I understand that you can restrict the login by domain using a parameter called "hd", according to the official documentation.

Firstly, where do you send that parameter in the context of Passport.js? I just don't understand where in the code that is put. If it helps, I have been mostly following the example passport-google-oauth provides.

Secondly, in theory how does this all work? Is it on the Google side, where they reject anyone trying to access the app with a domain outside of our company. Or is it on my side, that I need to check what domain the user is logging in from?

like image 536
James Morris Avatar asked Apr 14 '14 16:04

James Morris


People also ask

How do I use node with Google Authenticator?

Enter an email and click Sign Up. You'll then be redirected to add the 2FA with the authenticator app. You'll see a QRCode with a code input to enter the code after scanning the QRCode. After you scan the QRCode in Google's Authenticator app or any other authenticator app, you'll see a 6-digit code in the app.

What is Id_token Google?

Google ID Token helpers. Provides support for verifying OpenID Connect ID Tokens, especially ones generated by Google infrastructure. To parse and verify an ID Token issued by Google's OAuth 2.0 authorization server use verify_oauth2_token() .

What is Passport authentication in node JS?

Passport is Express-compatible authentication middleware for Node. js. Passport's sole purpose is to authenticate requests, which it does through an extensible set of plugins known as strategies.


2 Answers

Here's an example:

// first make sure you have access to the proper scope on your login route
app.get("/login", passport.authenticate("google", {
    scope: ["profile", "email"]
}));

// set up your Google OAuth strategy elsewhere...
passport.use(new GoogleStrategy({
    clientID: "something",
    clientSecret: "something",
    callbackURL: "/something"
}, function(token, refreshToken, profile, done){
    if(profile._json.hd === "yourdomain.com"){
        // find or create user in database, etc
        User.find({ id: profile.id }).done(done);
    }else{
        // fail        
        done(new Error("Invalid host domain"));
    }
});

And for good measure here's a full variable dump of what the "profile" variable looks like.

{ 
    provider: 'google',
    id: '12345678987654321',
    displayName: 'Don Draper',
    name: { familyName: 'Whitman', givenName: 'Richard' },
    emails: [ { value: '[email protected]' } ],
    _raw: 'a bunch of stringified json',
    _json: { 
        id: '123456789',
        email: '[email protected]',
        verified_email: true,
        name: 'Don Draper',
        given_name: 'Don',
        family_name: 'Draper',
        link: 'https://plus.google.com/123456789',
        picture: 'https://lh3.googleusercontent.com/XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/123456789/photo.jpg',
        gender: 'male',
        locale: 'en',
        hd: 'yourdomain.com' 
    } 
}

Here are some detailed tutorials that should answer your question about the theory behind all of this. You'll want some combination of the two.

  1. Local authentication and basic setup
  2. Google authentication
like image 189
aembke Avatar answered Sep 23 '22 03:09

aembke


I recommend a 2 step approach to this. Would love to hear feedback if this is overcomplicating it.

1) Help your users pick the right account

passport.authenticate('google', {
    // Only show accounts that match the hosted domain.
    hd: 'example.com',
    // Ensure the user can always select an account when sent to Google.
    prompt: 'select_account',
    scope: [
        'https://www.googleapis.com/auth/plus.login',
        'https://www.googleapis.com/auth/plus.profile.emails.read'
    ]
})(req, res, next);

2) Validate their profile

When a user is sent to accounts.google.com to authenticate, there is a simple hd=example.com query parameter in the URL. You can remove this and authenticate with any account (Passport will successfully verify the Oauth code regardless of the domain of the chosen account), so it should only be considered sugar for the end user and not security for the server.

When Passport does resolve the authentication, just check the hosted domain as in aembke's answer:

passport.use(new google_strategy({
    clientID: ...
    clientSecret: ...
    callbackURL: ...
}, function(token, tokenSecret, profile, done) {

    if (profile._json.domain !== 'example.com') {
        done(new Error("Wrong domain!"));
    } else {
        done(null, profile);
    }

}));
like image 38
Charlie Schliesser Avatar answered Sep 19 '22 03:09

Charlie Schliesser