0

Google suggests to ask a user for a minimal set of permission when first signing up a user, and to then ask to extend those permissions when/if needed by the user. So, I have used token based authentication to start off with, but if a user want to perform some functions on my website, they will need to grant app specific drive permissions. Many users will never need this, so I really tend to agree with google, only ask for those that need it when they need it. So, I went ahead and implemented this code to do it:


app.use(session({
  secret: vars.SESSIONSECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    sameSite: true,
    secure: true,
    httpOnly: true,
    maxAge: 86400000 // 1 day in milliseconds
  },
  store: new RedisStore({ client: redisClient }),
}));
const extendedGoogleStrategy = new GoogleStrategy(
    {
        clientID: vars.CLIENTID,
        clientSecret: vars.CLIENTSECRET,
        callbackURL: vars.EXTENDCALLBACKURL,
        scope: ['openid', 'profile', 'email', 'https://www.googleapis.com/auth/drive.file'],
        accessType: 'offline', // Request a refresh token
        prompt: 'consent', // Show consent screen for refresh token
        display: 'popup',
        authorizationParams: function () {
            return {
                include_granted_scopes: true,
            };
        },
        failureRedirect: '/oops',
        passReqToCallback: true, // Pass the request object to the callback
    },
    (req, accessToken, refreshToken, profile, done) => {
        // Now we have the access token, you can use it to make authorized API requests
        // save that access token and refresh token to profile and send it back to the callback
        // so they can be processed in the session and user db space
        dp.debugPrint(["Google Strategy accessToken"], dp.DEBUG_LEVEL.MEDIUM);
        profile.accessToken = accessToken;
        profile.refreshToken = refreshToken;
        profile.googleDriveEnabled = true;
        return done(null, profile);
    } 

app.use(passport.initialize());
app.use(passport.session());
passport.use('googleExtended', authentication.extendedGoogleStrategy);
// Google OAuth2 authentication routes
router.get('/google/extend-scope',
  (req, res, next) => {
  // Redirect the user to initiate authentication with the new scope
  passport.authenticate('googleExtended', {
    scope: ['openid', 'profile', 'email', 'https://www.googleapis.com/auth/drive.file'],
    accessType: 'offline', // Add this line
    prompt: 'consent', // Add this line
    display: 'popup',
    authorizationParams: function () {
      return {
        include_granted_scopes: true,
      };
    },
   failureRedirect: '/oops',
   keepSessionInfo: true

  })(req, res, next);
});

router.get('/google/extend-scope/callback',
    passport.authenticate('googleExtended', {
        failureRedirect: '/oops',
        keepSessionInfo: true
    }),
    async (req, res) => {
        //... store new accessToken in the session and store the renewToken in the user account db ...
});

The code seemed to work, until I figured out that my session was somehow being overwritten (at one point setting session: false seemed to fix the issue, but that was before I refactored by starting auth code to be token based vs oath2 - a long story on why I changed it but I did). Perhaps something else changed, or it never worked right but I didn't have a dependency that would be impacted by the session being over-written until later. Regardless, my session gets over-written.

keepSessionInfo: true

(an undocumented feature introduced in passport 0.6.0 - which always makes me nervous to use) is supposed to prevent this, but it doesn't. I have posted a ticket with the git repository on that, but it got me thinking.

A) has anyone run across this session overwrite problem and if so, what is the fix?

B) Am I even approaching this the right way? and if I am, what should I be doing to best protect the refreshToken (yeah, google has some verbage on this, but that figured it is best to ask for some advise)

C) this flow seems to ask and re-ask about the permissions, is that normal? I am still dev mode, so I get the idea it warns about the app being dev and not production but gee, there are a lot of confirms one has to make beyond that)

I expected to extended the user granted permissions for google to include google drive. I expected to get an accessToken and a renewToken, I epxected to be able to store those in user session/db (respectively). I got the extended grant, but the session was over-written with a new one missing the other data that was in the session that I am not interested in storing to a non-session based db to retrieve. (It is state info and not appropriate to store outside of the session store)

0