[ ] 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.
I don't understand how to implement passport sessions with session storage in nest. I read all the documentation three times and searched all the Google - no info. But it's the most common task.
Basically all i need is save current user in session using req.logIn()
and then check it with req.isAuthenticated()
in, maybe, guard.
Now i have simple google stategy
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: '',
clientSecret: '',
callbackURL: 'http://localhost:3000/auth/google/callback',
passReqToCallback: true,
scope: ['profile'],
});
}
async validate(request: any, accessToken: string, refreshToken: string, profile) {
return profile;
}
}
By the way, shoud i add implements AbstractStrategy
to this? Without it WebStorm says
Method can be static
Unused method validate
But there is nothing in docs about AbstractStrategy.
And auth.controller.ts
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {
}
@Get('google')
@UseGuards(AuthGuard('google'))
googleLogin() {
// initiates the Google OAuth2 login flow
}
@Get('google/callback')
@UseGuards(AuthGuard('google'))
async googleLoginCallback(@Req() req, @Res() res, @Session() session) {
// here i have req.user with profile
}
}
How am i supposed to save authentication in session with req.logIn()
?
P.S. i will try to imrove docs if we will find the answers
One more thing. I created custom guard as described in https://docs.nestjs.com/techniques/authentication
And even include it in providers
section of module, no console logs.
@Injectable()
export class GoogleAuthGuard extends AuthGuard('google'){
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
console.log('Custom google guard');
}
handleRequest(err, user, info) {
console.log('Custom google guard');
}
}
Just wanted to give a heads up, the default option for sessions is false, so this may be why you are not getting sessions to create for you.
https://github.com/nestjs/passport/blob/master/lib/options.ts
May need to add PassportModule.register({ session: true })
in the module import
Also, your GoogleAuthGuard needs to follow the canActivate
signature
@Injectable()
export class GoogleAuthGuard extends AuthGuard('google') {
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
super.logIn(request);
return super.canActivate(context);
}
}
But my GoogleAuthGuard
follows this signature, you can see it in my last message, the problem that it is not called.
It seems like i did the things right, after 3 days of googling and reading nest sources. Now i can say that Nest docs needs a huge improvements in Authentication
section 😄
Let's track this issue here https://github.com/nestjs/docs.nestjs.com/issues/99
但我
GoogleAuthGuard
遵循这个签名,你可以在我的上一条消息中看到它,它没有被调用的问题。
经过3天的谷歌搜索和阅读巢源,似乎我做的事情是正确的。现在我可以说Nest文档需要对Authentication
部分进行大幅改进😄
解决了吗?我也遇到了这个问题。
Here is how i got passport with a local strategy and sessions to work.
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// add 'express-session' (npm install express-session)
app.use(session({
secret: 'hello-world',
}));
app.use(cookieParser());
// init 'passport' (npm install passport)
app.use(passport.initialize());
app.use(passport.session());
await app.listen(3000);
}
// app.module.ts
@Module({
imports: [
// configure default options for passport
PassportModule.register({
defaultStrategy: 'local',
session: true,
}),
],
controllers: [
UserController,
],
providers: [
AuthService,
// add our providers
LocalStrategy, // simply by importing them will register them to passport (under the hood it calls `passport.use(...)`)
LocalSerializer,
LocalAuthGuard,
],
})
export class AppModule {}
import { Strategy } from 'passport-local';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly authService: AuthService,
) {
super();
}
async validate(email: string, password: string, done: CallableFunction) {
// add your custom login check here. and return the User object.
return await this.authService.signIn(email, password)
.then(user => {
done(null, user);
})
.catch(error => done(error));
}
}
Create logic on how our user
object should be serialized / deserialized. Under the hood, this will setup the passport.serializeUser(...)
and passport.deserializeUser(...)
@Injectable()
export class LocalSerializer extends PassportSerializer {
constructor(
private readonly authService: AuthService,
private readonly userRepository: UserRepository,
) {
super();
}
serializeUser(user: User, done: CallableFunction) {
done(null, user.id);
}
async deserializeUser(userId: string, done: CallableFunction) {
return await this.userRepository.findOneOrFail({ id: Number(userId) })
.then(user => done(null, user))
.catch(error => done(error));
}
}
By default the AuthGuard
does not call req.logIn(...)
to fix this we extend the AuthGuard
and simply call it ourself if successfully logged in. (this will trigger the session storage)
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext): Promise<boolean> {
const can = await super.canActivate(context);
if (can) {
const request = context.switchToHttp().getRequest();
super.logIn(request);
}
return true;
}
}
// user.controller.ts
export class UserController {
@Get('/signin')
@UseGuards(new LocalAuthGuard()) // notice we use our customized AuthGuard
signIn(@Query() signInDTO: UserSignInDTO) {
return {
message: 'You are now signed in',
};
}
}
@madsleejensen nice example, thank you. I have one fix for your code - we shoud
await super.logIn(request);
Because if you do some async stuff in serializeUser
- the session won't establish
For those following/landing on this thread, there are a couple of recent documents that should help
1) The authentication chapter was completely re-written (as of July 2019) with an end-to-end example
2) A recent (July 2019) article on dev.to on sessions covers sessions and was reviewed by the Nest core team.
Still no examples with session in docs 😄 But second article looks pretty useful, thank you!
Guys here is what you are looking for - boilerplate with local/google auth, sessions, and ACL
https://github.com/TrejGun/nest-authorization
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
Here is how i got passport with a local strategy and sessions to work.
Create logic on how our
user
object should be serialized / deserialized. Under the hood, this will setup thepassport.serializeUser(...)
andpassport.deserializeUser(...)
By default the
AuthGuard
does not callreq.logIn(...)
to fix this we extend theAuthGuard
and simply call it ourself if successfully logged in. (this will trigger the session storage)