Gammer
Gammer

Reputation: 5628

NestJS : Auth guard flow

I am implementing linkedin login strategy with passport in nestJS. What I have right now is that I have a button "Login with linkedin" points to auth/linkedin.

@Get('auth/linkedin')
@UseGuards(AuthGuard('linkedin'))
async linkedinAuth(@Req() req) {
    
}

Which works great and takes me to the linkedin login page and hits back my callback URL which is auth/linkedin/callback with code query string of token and this is where I am unable to figure out what todo and how to return linkedin user

@Get('auth/linkedin/callback')
@UseGuards(AuthGuard('linkedin'))
linkedinCallBack(@Req() req) {
  console.log(req)
  return 'handle callback!';
}

Linkedin passport strategy :

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
const LinkedInAuthStrategy = require('passport-linkedin-oauth2').Strategy;

@Injectable()
export class LinkedinStrategy extends PassportStrategy(LinkedInAuthStrategy) {
  constructor(
  ) {
    super({
      clientID: 'abcd123',
      clientSecret: 'abcd123',
      callbackURL: 'http://localhost:5000/auth/linkedin/callback',
      scope: ['r_emailaddress', 'r_liteprofile'],
    }, function(accessToken, refreshToken, profile, done) {
      process.nextTick(function () {
        console.log(profile);
        return done(null, profile);
      });
    })
  }
}

Note: I am using this package for linkedin passport strategy

Question : How can I handle callback further with @UseGuards, and return LinkedIn user?

Upvotes: 2

Views: 4641

Answers (2)

Uroš Anđelić
Uroš Anđelić

Reputation: 1174

You should adapt the LinkedinStrategy class a bit. You can't use the done function directly. It will be called by nest. You should have a validate method and return a user object from it. That object will be set to the request object so in the controller you will be able to access it with req.user. This is approximately how your class should look:

import { Strategy } from 'passport-linkedin-oauth2';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LinkedinStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
        clientID: 'abcd123',
        clientSecret: 'abcd123',
        callbackURL: 'http://localhost:5000/auth/linkedin/callback',
        scope: ['r_emailaddress', 'r_liteprofile'],
      });
  }

  async validate(accessToken: string, refreshToken: string, profile: object): Promise<any> {
    const user = await this.authService.validateUser(profile);

    return user;
  }
}

Upvotes: 3

thisismydesign
thisismydesign

Reputation: 25162

Check this article: OAuth2 in NestJS for Social Login (Google, Facebook, Twitter, etc) and this repo: https://github.com/thisismydesign/nestjs-starter

In the validate method of LinkedinStrategy you need to find or create the user (probably store in your DB), e.g.:

export class LinkedinStrategy extends PassportStrategy(Strategy) {
  // constructor...

  async validate(accessToken, refreshToken, profile) {
    let user = await this.usersService.findOneByProvider('linkedin', profile.id);
    if (!user) {
      user = await this.usersService.create({
        provider: 'linkedin',
        providerId: id,
        name: profile.name,
        username: profile.email,
      });
    }

    return user;
  }
}

In the controller's callback endpoint you can issue a JWT token to handle the User's session within the app:

@Get('auth/linkedin/callback')
@UseGuards(AuthGuard('linkedin'))
linkedinCallBack(@Req() req) {
  const { accessToken } = this.jwtAuthService.login(req.user);
  res.cookie('jwt', accessToken);
  return res.redirect('/profile');
}

Upvotes: 1

Related Questions