expressauthenticationpassport.jsmean-stackgoogle-oauth

Rest API to connect (authorize) Google for logged in user


I'm working in an application which uses a REST API using the MEAN stack and Passport JS to manage the authentication.

The authentication, we use JTW tokens for the communication between the backend and frontend. The token is generated based on local username and passwords.

Now I want to 'add' (authorize) the user's Google account to the profile to use with Google calendar API. (using this-> https://github.com/wanasit/google-calendar)

I've already have managed to send the user to the Google authorization page, and get the token back from it. The problem is that when the user gets redirected to the page, it looses the JWT token where I check the user for the request.

Is there any other way to get the current logged in user, or to pass some custom callback authorization header/param when calling the authorize method?

auth.js:

var googleParams = {
        clientID: config.auth.google.clientID,
        clientSecret: config.auth.google.clientSecret,
        callbackURL: config.auth.google.callbackURL
    }
    var googleStrategy = new GoogleStrategy(googleParams, function (token, refreshToken, profile, done) {
        profile.token = token;
        return done(null, profile);
    });

routes:

rotas.get(
    '/google',
    auth.authenticate(), // will check the current user
    auth.isLoggedIn, // make sure the user is really logged in
    auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to Google to get the token
);

rotas.get('/callback/google',
    auth.authorize('google', { scope: googleScope, passReqToCallback: true })
    auth.authRedirect()
);

the auth.authRedirect() function above is the closest solution I've found. It's a Express middleware which redirects the user to a known route in the frontend where the user IS authenticated... but then I would not be able to fetch all his Google profile and information I need...


Solution

  • So what I ended up doing was:

    1. Authenticate the user making the request via JWT access_token

    2. Get the user's ID and set it to the state option's property

    3. The user is redirected to the google authorization page and choose the account (s)he wants to connect

    4. (S)He gets redirected to my callback url with the state query param having the user's id

    5. Now I just have to get that id, search the user in the database, and set the data I need from req.account which contains the user's openid profile.

    var googleScope = ['openid', 'email', 'https://www.googleapis.com/auth/calendar'];
    
    routes.get(
        '/google',
        auth.authenticate(),
        auth.isLoggedIn,
        function (req, res, next) {
            var _id = '' + req.user._id; // convert to String... _id is an mongoose object
            return auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true, state: _id })(req, res, next)
        }
    );
    
    routes.get('/callback/google',
        function (req, res, next) {
            auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true })(req, res, next);
        },
        auth.saveUserData()
    );

    saveUserData= function () {
        return function (req, res, next) {
            if (req.query.state) {
                var _id = req.query.state;
                User.findOne({ _id, deleted: false, active: true })
                    .exec(function (err, user) {
                        if (err) {
                            res.send(err);
                        }
                        if (user) {
                            user.auth.google = {
                                id: req.account.id,
                                token: req.account.token,
                                email: (req.account.emails.length ? req.account.emails[0].value : null),
                                name: req.account.displayName
                            }
                            user.save(function (err, data) {
                                if (err) {
                                    res.send(err);
                                } else {
                                    res.redirect('/')
                                }
                            })
                        } else {
                            res.sendStatus(401);
                        }
                    })
            } else {
                res.sendStatus(400)
            }
        }