docs.nestjs.com: NestJS Authenticated sessions documentation has major gaps and is seemingly wrong
I’m submitting a…
[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Current behavior
“Session” instructions in “Authentication” section of documentation is erroneous and this functionality is otherwise undocumented. (And non-obvious.)
Expected behavior
It should be clear how to implement AuthGuard with a cookie-based sessions.
Please see my comment from Sept. 14 here.
At this point I DO have my browser at least storing a cookie after adding stuff directly to my main.ts, which I think is bad form:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as session from 'express-session';
import * as passport from 'passport';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(session({
secret: 'a secret',
name: 'abcd',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
await app.listen(3000);
}
bootstrap();
This at least gets me a cookie with key “abcd” in my Chrome Developer Tools “Application” > “Cookies” section, with a long value (s%3Ac1xPblaYplJQL2DLmfIUVoLdLIbkjsYQ.e8Q69kfkdn1Mm96Clk6BMg4Ea0k647RnIkT2bP60Lwc).
In my custom Guard, I can actually inspect the request and see that it has a session property with a Session object in it. The Session object has an id RUHHx_IvcDIPFWt-9MHeZHruw1bSO7-O which is different from what’s stored in my cookie – but that may be normal. The request object also has a sessionID property, with the same ID… And a sessionStore property with a MemoryStore object that has as property of sessions in it, that’s just an empty object.
Going back to the documentation’s recommended code: just calling super.logIn(theRequestObject) sends me directly to my Google Auth strategy’s serializeUser function, which is supposed to callback with the user that gets passed to it and/or an error.
Unfortunately, there is never any user that gets passed to it. I’m not sure how the User is intended to be retrieved from the session memory store, or where it’s intended to be set to the memory store…
So there’s a ton of stuff missing here. 😦
Environment
Nest version: 5.4.0
For Tooling issues:
- Node version: 8.11.3
- Platform: Fedora 25; Windows 10 x64
Other: @kamilmysliwiec It seems at least 3 other people are confused about this. It’s very frustrating. I think NestJS is so cool, but I took a break from it largely because I was spinning my wheels with this very issue. It would be one thing if I felt like I was just confused, but as it stands the docs are clearly wrong (or at least misleading), so I don’t feel like I can move forward.
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 36
- Comments: 44 (9 by maintainers)
I was able to get this working – here’s the solution I came up with:
configure sessions and passport to use sessions
In
main.ts(or wherever you’re settting up nest):sessionfrom theexpress-sessionpackage.guards
I’m using
passport-localso myLocalAuthGuardclass looks like this:configure the serializer
If you look at the implementation of
PassportSerializer(from@nestjs/passport) you’ll notice that bothserializeUseranddeserializeUserare abstract…so they need to be implemented by you. Take a look: https://github.com/nestjs/passport/blob/master/lib/passport/passport.serializer.tsMy barebones serializer looks like this:
update auth module
Then in the module (for example I’m using
AuthModule), this needs to be included. So myAuthModuleclass looks like this:session guard
Create a guard to check to see if the user is in the session.
putting it all together
Here is how this can be used in a controller:
You can test this out with cURL/Postman:
Logging in:
Then to test it out:
Thanks @kamilmysliwiec for all your work on NestJS – seems very slick!
Hi all,
After digging into whole Nestjs framework, I find it really cool. But, the security aspects are very painful. Implementing a simple local-strategy session should not be as difficult as the code @TrejGun has provided (thanx for that btw). We have to provide :
Look at this snippet from Spring Security for example :
This code can be used for LDAP, local strategy, Google/Facebook and whatever you want without changing any single line of code in your controllers.
With UserGuard/Auth annotations, we tie the controllers code with custom annotations. There’s no notion of Principal, only req.session.passport user which is very low level.
We should be able to say in app.module that we want a Passport local strategy (session/basic or other) and all the boilerplate code that we generally see should be generated or implemented in NestJS for us.
My 2 cents
Sami
Would be great to split the documentation example into two paths after setting up everthing: 1. Session, 2. JWT. Session is industry-standard for authentication IMO.
I’ll make sure to update this chapter as soon as I can. It’s indeed way too complicated right now
This article covers sessions in detail. Has been reviewed by the NestJS core team. https://dev.to/nestjs/authentication-and-sessions-for-mvc-apps-with-nestjs-55a4
I’ll wait a few days to see if there are any further comments before closing.
I love NestJS but I can’t believe how hard it’s to have a simple session auth setup correctly in 2022. I agree with @mikehaertl. Sessions should be the most basic auth way to implement. Even if JWT is more popular lately, in Rails for example you can get session auth running in a few minutes.
@mikehaertl honestly, I’m with you. I find that, at least for login and signup with a local username and password, passport is total overkill. I personally opt not to use it in cases that I really don’t need to, and instead prefer to implement that logic myself. If you know what you’re doing with a hashing library, it’s a pretty straightforward integration and you can either set up a session or a JWT to be checked in subsequent requests (either by passport or by your own guard).
This is only if you want passport to manage sessions, (which I realize is what this thread is about but still pointing that fact out). We can’t outright bind them because if someone wants to use fastify (even though it doesn’t play the best with passport) those middleware won’t work, which is really annoying. It also makes adding your own session store easier, though there should probably be better ways around that
Yeah, this is kind of unfortunate, though if you implement the login logic yourself (i.e. without passport) then this shouldn’t be a problem because the
AuthGuard()won’t be executed. Also, if the email in invalid the guard should return a 401/403 depending on how you have passport set upThis is why I wrote the article a few comments above that should help demystify what is happening between Nest and passport
You can, and in fact I suggest you do. passport-local is incredibly cumbersome for what it does and causes so many headaches for new devs.
Part of the reason this all feels so wonky is because passport is old (it was originally designed for
connect, express’s predecessor) but it is also tried and true. It’s documentation is also iffy at best, which is another pain point for a lot of people.If I can find the time I’d love to rip it out and make a
@nestjs/authwhich can handle local, jwt, cookie, and session based authentication, and maybe help with oauth, so that passport isn’t necessary anymore and we can just have something that is transport agnostic as that’s really what Nest is striving to be. Unfortunately, designing something like that is going to take a lot of time and testing, which in turn means that it’s going to really need a lot of work and dedication that I can’t always give to it.To add another resource, there’s this article by me that does a deep dive on passport with Nest and shows how to set up a session based authentication with passport, Nest, and Redis
you can find my version of solution in my blog, there are also other articles about nest.js https://trejgun.github.io/
Is it just me or does the documented solution feel very strange?
passport.initialize()andpassport.session()login()even though passport usually does that automatically under the hood. Why?req.bodyto let passport-local do it’s jobGiven all this I wonder why we can’t use a simple controller action or - as I’m using GraphQL - a mutation as usual. Something like this:
And use AuthGuard only for those actions that I want to protect from unauthenticated access.
I tried this but
req.login()is not defined - even though I’ve initialized passport in the app module.As a newcomer to nestjs all this makes me raise my eyebrows. It would be great to get some feedback from the developers about why the design decision was made that way.
EDIT: I made it work (forgot to call
forRoutes('*')afterconsumer.apply(...)in the app module’sconfigure()). So the question remains: Why all the fuss with guards and what-not if a simple setup like this just seems to work?Hey, really helpful this! 😃 But, only thing i am still bothered about is why do i need to bootstrap the initialize and session methods, especially when we can do that in the module initialisation like so:
PassportModule.register({ session: true });
I know, without the initialisation the solution doesn’t work but then why is there an option to register a session in the module?
Thanks again!
Example project how to use session in NestJS https://github.com/Insidexa/nestjs-session-example
The solutions provided in the issue make absolutely zero sense to me as a new user of NestJS.
It would be great if someone could update the documentation with how sessions work together with NestJS, express, express-session, and passport with the local guard.
@ejhayes You don’t happen to have this example in a public git repo? I don’t understand where CookieSerializer is used