Jim B.
Jim B.

Reputation: 4694

Using passport to allow two completely different types of "user" to authenticate

So, I have an app that has two types of 'user': humans and devices.

Humans log in using username/password (local strategy). Devices log in using a certificate.

They are very different things, so this isn't the same as having special access for admin users versus normal users. I can't have a single db table for all entities that can log in.

I understand how I can have multiple authentication strategies (I just differentiate by endpoint), but I am a bit stumped on how to handle deserialization of the token for incoming requests. Express/passport seems to allow for only one deserializeUser function I can write.

Furthermore, I'd like to decouple these two types so I can keep devices and humans in their own code rather than have a monolithic deserialize function that sorts them out.

Ideally, I'd be able to have two deserializeUser functions and the right one gets called depending on the token format, but I don't see how best to do that.

Any suggestions?

Upvotes: 1

Views: 2564

Answers (2)

Jim B.
Jim B.

Reputation: 4694

I ended up standardizing on a minimal set of common elements each user type must have:

  • id (using mongo's _id)
  • type (defined as a mongoose virtual)

This allows me to include the type in the serialization and only look up the entity in the correct table upon deserialization.

This is pretty close to what I was hoping to get. I'd have preferred to not have to have central serializer/deserializer functions, but I feel this is the minimum compromise I could make.

Here's the code for serializing and deserializing:

passport.serializeUser(function (entity, done) {
    done(null, { id: entity.id, type: entity.type });
});

passport.deserializeUser(function (obj, done) {
    switch (obj.type) {
        case 'user':
            User.findById(obj.id)
                .then(user => {
                    if (user) {
                        done(null, user);
                    }
                    else {
                        done(new Error('user id not found:' + objid, null));
                    }
                });
            break;
        case 'device':
            Device.findById(obj.id)
                .then(device => {
                    if (device) {
                        done(null, device);
                    } else {
                        done(new Error('device id not found:' + obj.id, null));
                    }
                });
            break;
        default:
            done(new Error('no entity type:', obj.type), null);
            break;
    }
});

Upvotes: 2

Yogesh Patel
Yogesh Patel

Reputation: 314

I understood your requirement that you need a different user type login so you can use request parameter in passport to identify user type so your function will remain common.

    passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password',// this is the virtual field on the model
        passReqToCallback: true
    },
    function (req, email, password, done) {  
       // get req here 
       enter code here
    });

Upvotes: 0

Related Questions