How to handle Facebook login using NestJS
NestJS is a great Node.js framework and its documentation explains server concepts quite well. They also have an Authentication section which you can follow if you want to implement a basic username/password login strategy.
But it has been a little bit difficult for me to understand how to adapt to other sign up providers such as Facebook. And I couldn’t find explicit tutorials so… Here I am!
NestJS is using Passport.js to handle authentication which is the most popular authentication library for Node.js
Who is this for?
This tutorial is made for people who have a front application that asks for the user to log into Facebook. The login workflow is handled front-end side and the user get an access_token
specific to his account. We’ll use this access token in our back end in order to:
- Check if the token is correct
- Get Facebook profile information
- Create the user if he doesn’t exist
So we want a GET /auth/Facebook
endpoint in our back end that sends us back “OK I created a new user”
Authentication Requirements
First, you’ll need to download the following packages:
$ npm install --save @nestjs/passport passport passport-facebook-token
$ npm install --save-dev @types/passport-facebook-token
nestjs/passport
: NestJS module that wraps the passport librarypassport
: Passport.js library because the NestJS module will have to access itpassport-Facebook-token
: The Passport.js Facebook strategy using access_token. There is also apassport-Facebook
package that handles authentication workflow with callback URL and stuff. This isn’t what we want.
Now, let’s generate the components we’ll need:
$ nest g module auth
$ nest g controller auth
$ touch auth/facebook.strategy.ts
Configure the Facebook strategy
We need to give passport.js a specific FacebookTokenStrategy
instance.
facebook.strategy.ts:
@Injectable()
export class FacebookStrategy {
constructor(
private readonly userService: UserService,
) {
this.init();
} init() {
use(
new FacebookTokenStrategy(
{
clientID: <YOUR_APP_CLIENT_ID>,
clientSecret: <YOUR_APP_CLIENT_SECRET>,
fbGraphVersion: 'v3.0',
},
async (
accessToken: string,
refreshToken: string,
profile: any,
done: any,
) => {
const user = await this.userService.findOrCreate(
profile,
);
return done(null, user);
},
),
);
}
}
passport object expects that we call its use()
method. This is what this class basically does. Also, here I assume you already have a User service with a function that either finds or creates a user.
I can give you a peek of what mine does:
async findOrCreate(profile): Promise<User> {
const user = await this.userModel
.findOne({ 'facebook.id': profile.id })
.exec();
if (user) {
return user;
}
const createdUser = new this.userModel({
email: profile.emails[0].value,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
Facebook: {
id: profile.id,
avatar: profile.photos[0].value,
},
});
return createdUser.save();
}
Either way, it will return a user object.
Implement the controller endpoint
auth.controller.ts:
@Controller('auth')
export class AuthController { @UseGuards(AuthGuard('facebook-token'))
@Get('facebook')
async getTokenAfterFacebookSignIn(@Req() req) {
console.log(req.user);
}
}
This configures the /auth/Facebook
endpoint of our server. It just says it exists. The request, after going through the facebook-token middleware, contains the user.
Putting in all together
auth.module.ts:
@Module({
imports: [UserModule],
providers: [FacebookStrategy],
controllers: [AuthController],
})
export class AuthModule {}
Now if we call the route with a session_token
as a query param:
GET auth/Facebook?session_token=<SESSION_TOKEN>
It should persist the newly created user in your database. Then, you can implement JWT functionality to handle user sessions.
Let me know if you have any questions :)
Source code : https://github.com/baptisteArnaud/facebook-login-nestjs-example