2.1.5 • Published 4 years ago
@zhennann/egg-passport v2.1.5
egg-passport
passport plugin for egg, base on passportjs.
Install
$ npm i egg-passportUsage
enable passport plugin
// config/plugin.js
exports.passport = {
  enable: true,
  package: 'egg-passport',
};Using Github and Twitter strategy
// config/config.default.js
exports.passportGithub = {
  key: 'my oauth2 clientID',
  secret: 'my oauth2 clientSecret',
};
exports.passportTwitter = {
  key: 'my oauth1 consumerKey',
  secret: 'my oauth1 consumerSecret',
};Authenticate Requests
Use app.passport.mount(strategy[, options]), specifying the 'github' and 'twitter' strategy, to authenticate requests.
// app/router.js
module.exports = app => {
  app.get('/', 'home.index');
  // authenticates routers
  app.passport.mount('github');
  // this is a passport router helper, it's equal to the below codes
  //
  // const github = app.passport.authenticate('github');
  // app.get('/passport/github', github);
  // app.get('/passport/github/callback', github);
  // custom options.login url and options.successRedirect
  app.passport.mount('twitter', {
    loginURL: '/account/twitter',
    // auth success redirect to /
    successRedirect: '/',
  });
};Verify and store user
Use app.passport.verify(async (ctx, user) => {}) hook:
// app.js
module.exports = app => {
  app.passport.verify(async (ctx, user) => {
    // check user
    assert(user.provider, 'user.provider should exists');
    assert(user.id, 'user.id should exists');
    // find user from database
    //
    // Authorization Table
    // column   | desc
    // ---      | --
    // provider | provider name, like github, twitter, facebook, weibo and so on
    // uid      | provider unique id
    // user_id  | current application user id
    const auth = await ctx.model.Authorization.findOne({
      uid: user.id,
      provider: user.provider,
    });
    const existsUser = await ctx.model.User.findOne({ id: auth.user_id });
    if (existsUser) {
      return existsUser;
    }
    // call user service to register a new user
    const newUser = await ctx.service.user.register(user);
    return newUser;
  });
};How to develop an egg-passport-${provider} plugin
See example: egg-passport-twitter.
- Plugin dependencies on egg-passport to use app.passportAPIs.
// package.json
{
  "eggPlugin": {
    "name": "passportTwitter",
    "dependencies": [
      "passport"
    ]
  },
}- Define config and set default values
Must use key and secret instead of consumerKey|clientID and consumerSecret|clientSecret.
// config/config.default.js
exports.passportTwitter: {
  key: '',
  secret: '',
  callbackURL: '/passport/twitter/callback',
};- Init Strategyinapp.jsand format user inverify callback
// app.js
const debug = require('debug')('egg-passport-twitter');
const assert = require('assert');
const Strategy = require('passport-twitter').Strategy;
module.exports = app => {
  const config = app.config.passportTwitter;
  // must set passReqToCallback to true
  config.passReqToCallback = true;
  assert(config.key, '[egg-passport-twitter] config.passportTwitter.key required');
  assert(config.secret, '[egg-passport-twitter] config.passportTwitter.secret required');
  // convert to consumerKey and consumerSecret
  config.consumerKey = config.key;
  config.consumerSecret = config.secret;
  // register twitter strategy into `app.passport`
  // must require `req` params
  app.passport.use('twitter', new Strategy(config, (req, token, tokenSecret, params, profile, done) => {
    // format user
    const user = {
      provider: 'twitter',
      id: profile.id,
      name: profile.username,
      displayName: profile.displayName,
      photo: profile.photos && profile.photos[0] && profile.photos[0].value,
      token,
      tokenSecret,
      params,
      profile,
    };
    debug('%s %s get user: %j', req.method, req.url, user);
    // let passport do verify and call verify hook
    app.passport.doVerify(req, user, done);
  }));
};- That's all!
APIs
extent application
- app.passport.mount(strategy, options): Mount the login and the login callback routers to use the given- strategy.
- app.passport.authenticate(strategy, options): Create a middleware that will authorize a third-party account using the given- strategyname, with optional- options.
- app.passport.verify(handler): Verify authenticated user
- app.passport.serializeUser(handler): Serialize user before store into session
- app.passport.deserializeUser(handler): Deserialize user after restore from session
extend context
- ctx.user: get the current authenticated user
- ctx.isAuthenticated(): Test if request is authenticated
- * ctx.login(user[, options]): Initiate a login session for- user.
- ctx.logout(): Terminate an existing login session
Unit Tests
This plugin has includes some mock methods to helper you writing unit tests more conveniently.
app.mockUser([user]): Mock an authenticated user
const mm = require('egg-mock');
describe('mock user demo', () => {
  let app;
  before(() => {
    app = mm.app();
    return app.ready();
  });
  after(() => app.close());
  afterEach(mm.restore);
  it('should show authenticated user info', () => {
    app.mockUser();
    return request(app.callback())
      .get('/')
      .expect(/user name: mock_name/)
      .expect(200);
  });
});app.mockUserContext([user]): Mock a context instance with authenticated user
it('should get authenticated user and call service', async () => {
  const ctx = app.mockUserContext();
  const result = await ctx.service.findUser({ id: ctx.user.id });
  assert(result.user.id === ctx.user.id);
});Questions & Suggestions
Please open an issue here.