Skip to content
This repository was archived by the owner on Sep 26, 2023. It is now read-only.

[Auth]: Guide for using custom passport strategy #787

Closed
2 tasks
ekryski opened this issue Jul 24, 2017 · 6 comments
Closed
2 tasks

[Auth]: Guide for using custom passport strategy #787

ekryski opened this issue Jul 24, 2017 · 6 comments

Comments

@ekryski
Copy link
Member

ekryski commented Jul 24, 2017

In some cases you there might not be a passport strategy that fits the OAuth1, OAuth2 or Local auth mold, so you may need to create a custom passport strategy or wrap an existing passport strategy so that it has a similar interface as the Feathers auth plugins.

Todo

  • Show how to use a vanilla passport strategy
  • Show how to wrap it so that it looks like the other Feathers auth plugins (ie. OAuth, LDAP, Local, etc)
@ekryski
Copy link
Member Author

ekryski commented Jul 25, 2017

It's actually quite easy to use a passport strategy with Feathers. You don't need to wrap it up like we do with our authentication plugins (ie. feathers-authentication-local). The only thing that those plugins are doing is registering the Passport Strategy, and removing some of the boilerplate to look up users on your user service. In the case of OAuth we also set up the express middleware to handle the callback routes.

This is how you can use a vanilla API key Passport Strategy with Feathers:

const apiKeyStrategy = new LocalAPIKeyStrategy({
    entity: 'org' // if you wanted to map the returned entity to a different name
  }, 
  function(apikey, done) {
    const query = { apikey };
    app.service('users').find({ query }).then((err, user) => {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }

      // this will expose the user as req.user in middleware or hook.params.user
      // in Feathers hooks.
      return done(null, user);
    });
  }
);

app.configure(authentication());

app.passport.use('localapikey', apiKeyStrategy);

// in your hooks

app.service('users').hooks({
  before: {
    all: [auth.hooks.authenticate(['localapikey'])]
  }
});

Note: This token strategy is great for basic APIs but it won't work with the Feathers client directly. You would need to exchange the API key for a JWT and then the feathers JWT access token is used to fetch subsequent API calls. In order to do that you would have to modify the example above to be like this:

const apiKeyStrategy = new LocalAPIKeyStrategy(
  function(apikey, done) {
    const query = { apikey };
    app.service('users').find({ query }).then((err, user) => {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      return done(null, user);
    });
  }
);

app.configure(authentication())
  .configure(jwt());

app.passport.use('localapikey', apiKeyStrategy);

// in your hooks
app.service('authentication').hooks({
  before: {
    all: [auth.hooks.authenticate(['localapikey'])]
  }
});

app.service('users').hooks({
  before: {
    all: [auth.hooks.authenticate(['jwt'])]
  }
});

Usually instead of an API key we recommend using feathers-authentication-local with a clientId and clientSecret for added security.

If you wanted to set the entity (or any other passport strategy option) a bit more dynamically you can also do it at the hook level like so:

app.service('users').hooks({
  before: {
    all: [
      auth.hooks.authenticate(['localapikey'], { entity: 'custom' } )]
  }
});

@subodhpareek18
Copy link
Contributor

subodhpareek18 commented Jul 26, 2017

I think an example with passport-custom will be more helpful as it's completely custom and not just a custom implementation of a non custom strategy, though it'll not hurt to have two examples.
Here is what I ended up implementing today (got inspired after reading this issue).

Use Case

Whenever a new user lands on the website authenticate them anonymously by creating a new user in the database with the role 'customer'.

// authService.js
import auth from 'feathers-authentication';
import jwt from 'feathers-authentication-jwt';
import local from 'feathers-authentication-local';
import anonymous from './anonymous';
import { server } from 'config';

export default () => {
  return function() {
    this.configure(auth(server.auth));
    this.configure(jwt());
    this.configure(local(server.auth.local));
    this.configure(anonymous(server.auth.anonymous));
  };
};
// anonymous.js
import Strategy from 'passport-custom';

export default opts => {
  return function() {
    const verifier = async (req, done) => {
      const role = await this.service(opts.roleService).find({
        query: {
          name: 'customer',
          $limit: 1
        }
      });

      const user = await this.service(opts.userService).create({
        roleId: role[0].id
      });

      const jwtPayloadAdditions = {
        userId: user.id
      }

      return done(null, user, jwtPayloadAdditions);
    };

    this.passport.use('anonymous', new Strategy(verifier));
  };
};

@daffl
Copy link
Member

daffl commented Nov 11, 2017

@daffl daffl closed this as completed Nov 11, 2017
@DesignByOnyx
Copy link
Contributor

There is now a feathers-authentication-custom strategy which makes this a little easier:

https://github.com/feathersjs-ecosystem/feathers-authentication-custom

@daffl
Copy link
Member

daffl commented Jan 29, 2018

Maybe someone would be interested in updating the guide to use this.

@DesignByOnyx
Copy link
Contributor

I was planning on updating the guide as well :)

# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants