I'm trying to implement JWT authentication using passport with nestJS, the default implementation works flawlessly.
In my case i would like to use "email" instead of username but unfortunately the validate function which resides inside local.strategy.ts accepts only "username" and will not even invoke without username parameter.
Please advice.
reference to the implementation I've used, I followed the guide step by step
https://docs.nestjs.com/security/authentication
I would agree with Micael Levi. Just provide these parameters in the constructor to the parent class
constructor(private authService: AuthService) {
super({
usernameField: 'email',
passwordField: 'password',
});
}
You can further read the docs here https://docs.nestjs.com/security/authentication#customize-passport
Hope This Helps
Related
I have a scenario where I need to implement an authentication mechanism for admin and for normal users in my application using the Passport local strategy. I implemented the strategy for the normal users as described here. It is working perfectly fine.
However, now I need to implement the same local strategy for Admin login. I feel like it would have been much easier if both the type of users(admin and normal user) are on the same entity/table because a single validate function would be capable enough to handle the case but my application design has separate entities for Admins and normal users and hence are the separate services.
My local strategy looks something like this:
#Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private userService: UserService) {
super();
}
async validate(username: string, password: string): Promise<any> {
const user = await this.userService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException("Incorrect credentials!");
}
return user;
}
}
As I went through the documentation, it is said that a Local Strategy can have only one validate function(that works as a verify callback), if this is the case how do I differentiate a logic inside this single validate function to behave differently for the requests coming in from the normal user controller and from the admin controller? Because in the admin login case, I'll be using a different route something like(admin/login), and for the user, it could be something like(user/login).
What's the best approach for this? Do I need to create a separate local strategy for admin? If yes, any hints will be appreciated. Otherwise, how can I incorporate logic inside this single validate function?
One of the alternatives could be checking if data exists in both the tables for every login payload each time. This approach doesn't look quite right to me.
If this provides more insight, the auth guard is simple as this:
#Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
}
You could make a second strategy based on passport-local but give it a specified name like admin by following the named strategies part of the docs. Something like
#Injectable()
export class LocalAdminStrategy extends PassportStrategy(Strategy, 'admin') {
validate(username: string, password: string) {
return validateAdminInfo({ username, password });
}
}
And now you can use this strategy by using #UseGuards(AuthGuard('admin'))
Hi I'm trying to build a NestJS API and wanna learn the best practices to do it.
So I was wondering why the documentation of Nest JS
https://docs.nestjs.com/security/authentication
is providing an example of authentication with coupling passport-local and passport-jwt strategies?
Couldn't it be possible to just use the passport-jwt?
If the answer is, yes but it's not a good practice, could you provide explanations.
And if the answer is yes and it's the best practice, do you have any idea why they put an example that is not using best practices on such an important subject like authentication?
If I remember well the documentation, from the time I did my own authentication with Nest, the jwt and the local strategies are 2 ways to validate your users access but can be used in complementary.
The local strategy allow your server to validate the user with its username and password. So it will work well for a login endpoint, where the user fill the login form of your app.
Using this first step, you can use the jwt token api to create a token that will identify the user. You can set for example this token in a cookie, to be able to access it on your server when the user will make other requests.
Now, the jwt strategy will be perfect to validate the user by checking its datas and its signature in the token.
So to answer your question, you can of course use only one of this 2 strategies, but you will need to implement what they do on your own.
For example, the local strategy will be really easy to imitate by a simple endpoint in a resolver or a controller which check user information (username and password) in your DB, and set a cookie if succeed or respond an unauthorized if it failed
Hope what I say make sens !
Couldn't it be possible to just use the passport-jwt?
Yes, is possible use login controller without passport-local.
Passport-local only receibe two params (username and password) and you need implement validation, the returned value is pass how "user" propertie in Query object.
You can omit passport-local, implement the login controller with a loginDto in the #Body and validate credentials, are the same.
Example:
#Post('/auth/login')
async login(#Body() loginUserDto: LoginUserDto){
user = validation.....
if !(user) throw new UnauthorizedException();
return generateJwt(user);
}
where LoginUserDto can be
export class LoginUserDto {
#ApiProperty()
#IsString()
#IsLowercase()
#IsNotEmpty()
username: string;
#ApiProperty()
#IsNotEmpty()
#IsString()
password: string;
}
passport-jwt is only for validate the jwtoken sent by the user to privates routes of resources (endpoints).
The answer to the question if it is good practice, i don't know, for me is the same.
I'm trying to Implement Twitter login in my NodeJS app.
As per the documentation from Twitter, I need to pass the below parameters to this url via POST request.
URL:
https://api.twitter.com/oauth/request_token
PARAMETERS:
oauth_nonce=,
oauth_signature=,
oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="currentTimestamp",
oauth_consumer_key=“myKEY”,
oauth_version="1.0"
I'm passing a random string for oauth_nonce parameter. I'm not clear on how to create a oauth_signature?
I keep getting 215 error whenever I make the POST request beacuse of incorrect oauth_nonce and oauth_signature values I guess.
How do I generate oauth_nonce and oauth_signature values while making the request in NodeJS.
Those parameters need to be passed in your authorization header:
OAuth oauth_nonce="K7ny27JTpKVsTgdyLdDfmQQWVLERj2zAK5BslRsqyw",
oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1300228849",
oauth_consumer_key="OqEqJeafRSF11jBMStrZz",
oauth_signature="Pc%2BMLdv028fxCErFyi8KXFM%2BddU%3D",
oauth_version="1.0"
But before that, you need to get a signature, which will then give you all of the parameters above. Check the documentation here.
A recommendation would be to use a 3rd party library like passport, which heavily simplifies this process if needed.
To answer your question:
How do I generate oauth_nonce and oauth_signature values while making the request in NodeJS.
Going through these post will be of help to you Creating a signature and How to generate an OAuth nonce.
Twitter makes a whole lot of sense if used correctly. I have seen some tutorials that has been quite useful to those around me, that I feel maybe you might want to check Node Authentication: Twitter and Implementing Sign in with Twitter for Node.js
Or you might want to check passportjs
You will have to install it in your project by running:
npm install passport-twitter
Configuration
const passport = require('passport')
, TwitterStrategy = require('passport-twitter').Strategy;
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: "http://www.example.com/auth/twitter/callback"
},
function(token, tokenSecret, profile, done) {
User.findOrCreate(..., function(err, user) {
if (err) { return done(err); }
done(null, user);
});
}
));
This Github repo might be a great reference to you.
If you're using reactjs, this (npm i react-twitter-auth) might be useful to you too.
can't comment, that's why answering here.
the docu sais, oauth_nonce:
is a unique token your application should generate for each unique request
it is like the salt of the encryption. you can find oauth_signature under the same link. how to generate it is described here.
Finally I could get Access Token using this library
I'm writing in Node using passport.js to authenticate.
console.log(req.user);
returns
{ group: 'CuManager',
password: 'password',
username: 'administrator',
cu_id: 2,
_id: 569fd3f4328ef124be533caf }
but
console.log(req.user.cu_id);
returns
undefined
I was thinking that cu_id property maybe is not available due to serialization/deserialization, but it's there. I just can't access it.
How can I do that?
I need it for
find({cu_id : req.user.cu_id})
BTW I can't help thinking passport.js is little overcomplicated (maybe we just don't get along). What else can I use to authenticate users in Node (I use express)?
If req.user is a Mongoose document, you should probably add cu_id to the proper schema.
Otherwise you can use something like req.user.toObject().cu_id to access the property, or make sure that somehow the result of doc.toObject() is assigned to req.user.
As for a Passport-replacement: this depends on how exactly you want to authenticate your users. If you're POST'ing a login form to an Express route, you can perform the password validation and session setup yourself.
try parsing it in JSON.
JSON.parse(req.user)['cu_id']
I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)
I stumbled across:
https://github.com/jaredhanson/passport-facebook/issues/26
&
https://github.com/drudge/passport-facebook-token
what seems to be an entirely different strategy from passport-facebook.
Am I correct when assuming that:
One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?
Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs
I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.
You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.
If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.
passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.
So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "123-your-app-id",
clientSecret : "ssshhhhhhhhh"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.
To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.
app.get('/my/api/:access_token/endpoint',
passport.authenticate(['facebook-token','other-strategies']),
function (req, res) {
if (req.user){
//you're authenticated! return sensitive secret information here.
res.send(200, {'secrets':['array','of','top','secret','information']});
} else {
// not authenticated. go away.
res.send(401)
}
}
NB. the access_token property is case-sensitive and uses an underscore.
The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.