Search code examples
node.jssessionredisnestjspassport.js

NESTJS EXPRESS-SESSION REDIS: cookie per user


I have a NestJs app with PassportJs, Express-Session and Redis as session storage. Actually, I'm trying to implement a remember-me feature and I'm facing a problem and can't see why it's not working. The session created succesfully inside the DB, without any error, except the cookie stored in the DB has cookie.maxAge = null and cookie.expires = null. Any idea ?

app.module.ts

export class AppModule implements NestModule{
    
constructor(@InjectConnection(ConnectionName.DB_ALPHA_REDIS_CONNECTION_NAME) private readonly connection: Redis) {}
      configure(consumer: MiddlewareConsumer) {
        consumer
          .apply(
            session({
              name: process.env.ALPHA_AUTH_SESSION,
              store: new RedisStore({ client: this.connection }),
              saveUninitialized: false,
              secret: process.env.SESSION_SECRET,
              rolling: false,
              resave: false,
              cookie:{ httpOnly: true, secure: false, sameSite: true }
            }),
            passport.initialize(),
            passport.session(),
          )
          .forRoutes('*');
      }  
    }

local-auth.guard.ts

@Injectable()
export class LocalAuthGuard extends AuthGuard('local'){

    async canActivate(context: ExecutionContext){
       
        const REQUEST = context.switchToHttp().getRequest()
        const RESPONSE = context.switchToHttp().getResponse()

        const BODY = plainToClass(LoginBody, REQUEST.body)

        const ERRORS = await validate(BODY)

        const ERR_MESSAGES = ERRORS.flatMap(({ constraints }) =>
            Object.values(constraints),
        );

        if(ERR_MESSAGES.length > 0)
            RESPONSE.status(404).send(buildResponse(404, 'There is some errors', ERR_MESSAGES))

        REQUEST.sessionStore.ttl = BODY.rememberMe ? 60 * 60 : 60
        REQUEST.session.cookie.maxAge = BODY.rememberMe ? 60 * 60 * 1000 : 60 * 1000

        // return 3600 as expected
        console.log( REQUEST.sessionStore.ttl) 

        /**
        return as expected
        cookie: {
          path: '/',
          _expires: 2023-06-20T00:24:26.712Z,
          originalMaxAge: 3600000,
          httpOnly: true,
          secure: false,
          sameSite: true
        }
        Inside redis cookie.originalMaxAge = null
        cookie.expires = null 
        */
        console.log( REQUEST.session.cookie)

        const RESULT = (await super.canActivate(context)) as boolean
        
        await super.logIn(REQUEST)
        return RESULT
    }

}

Inside redis DB:

{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":true,"path":"/","sameSite":true},"passport":{"user":{"id":"648a59999f34ed94915ed3ba"}}}

Solution

  • I think a possible solution is to use REQUEST.session.save() in LocalAuthGuard's canActivate method to ensure that the session is saved correctly after changing maxAge and ttl. You can try the following changes: 

    @Injectable()
    export class LocalAuthGuard extends AuthGuard('local'){
    
        async canActivate(context: ExecutionContext){
           
            const REQUEST = context.switchToHttp().getRequest()
            const RESPONSE = context.switchToHttp().getResponse()
    
            const BODY = plainToClass(LoginBody, REQUEST.body)
    
            const ERRORS = await validate(BODY)
    
            const ERR_MESSAGES = ERRORS.flatMap(({ constraints }) =>
                Object.values(constraints),
            );
    
            if(ERR_MESSAGES.length > 0)
                RESPONSE.status(404).send(buildResponse(404, 'There are some errors', ERR_MESSAGES))
    
            REQUEST.sessionStore.ttl = BODY.rememberMe ? 60 * 60 : 60
            REQUEST.session.cookie.maxAge = BODY.rememberMe ? 60 * 60 * 1000 : 60 * 1000
    
            // Save the session after modifying ttl and maxAge
            REQUEST.session.save(err => {
              if (err) {
                console.error(err);
              }
            });
    
            // return 3600 as expected
            console.log( REQUEST.sessionStore.ttl) 
    
            /**
            return as expected
            cookie: {
              path: '/',
    ',
              _expires: 2023-06-20T00:24:26.712Z,
              originalMaxAge: 3600000,
              httpOnly: true,
              secure: false,
              sameSite: true
            }
            */
            console.log( REQUEST.session.cookie)
    
            const RESULT = (await super.canActivate(context)) as boolean
            
            await super.logIn(REQUEST)
            return RESULT
        }
    
    }
    
    

    another solution, try setting maxAge and ttl after calling super.logIn(REQUEST). :

    @Injectable()
    export class LocalAuthGuard extends AuthGuard('local'){
    
        async canActivate(context: ExecutionContext){
           
            const REQUEST = context.switchToHttp().getRequest()
            const RESPONSE = context.switchToHttp().getResponse()
    
            const BODY = plainToClass(LoginBody, REQUEST.body)
    
            const ERRORS = await validate(BODY)
    
            const ERR_MESSAGES = ERRORS.flatMap(({ constraints }) =>
                Object.values(constraints),
            );
    
            if(ERR_MESSAGES.length > 0)
                RESPONSE.status(404).send(buildResponse(404, 'There is some errors', ERR_MESSAGES))
    
            const RESULT = (await super.canActivate(context)) as boolean
            
            await super.logIn(REQUEST)
    
            REQUEST.sessionStore.ttl = BODY.rememberMe ? 60 * 60 : 60
            REQUEST.session.cookie.maxAge = BODY.rememberMe ? 60 * 60 * 1000 : 60 * 1000
    
            // Save the session after modifying ttl and maxAge
            REQUEST.session.save(err => {
              if (err) {
                console.error(err);
              }
            });
    
            // return 3600 as expected
            console.log( REQUEST.sessionStore.ttl) 
    
            /**
            return as expected
            cookie: {
              path: '/',
              _expires: 2023-06-20T00:24:26.712Z,
              originalMaxAge: 3600000,
              httpOnly: true,
              secure: false,
              sameSite: true
            }
            */
            console.log( REQUEST.session.cookie)
    
            return RESULT
        }
    
    }