site/docs/tutorials/passport.md
Login authentication is a common business scenario, including "account password login" and "third-party unified login".
Among them, we often use the latter, such as Google, GitHub, QQ unified login, which are based on OAuth specification.
Passport is a highly scalable authentication middleware that supports the Strategy of Github ,Twitter,Facebook, and other well-known service vendors. It also supports login authorization verification via account passwords.
Egg provides an egg-passport plugin which encapsulates general logic such as callback processing after initialization and the success of authentication so that the developers can use Passport with just a few API calls.
The execution sequence of Passport is as follows:
egg-passportBelow, we will use GitHub login as an example to demonstrate how to use it.
$ npm i --save egg-passport
$ npm i --save egg-passport-github
For more plugins, see GitHub Topic - egg-passport .
Enabling the plugin:
// config/plugin.js
module.exports.passport = {
enable: true,
package: 'egg-passport',
};
module.exports.passportGithub = {
enable: true,
package: 'egg-passport-github',
};
Configuration:
Note: The egg-passport standardizes the configuration fields, which are unified as key and secret.
// config/default.js
config.passportGithub = {
key: 'your_clientID',
secret: 'your_clientSecret',
};
Note:
clientID and clientSecret information.callbackURL, such as http://127.0.0.1:7001/passport/github/callback
- You need to update to the corresponding domain name when deploying online
- The path is configured via options.callbackURL, which defaults to /passport/${strategy}/callback// app/router.js
module.exports = (app) => {
const { router, controller } = app; // Mount the authentication route
app.passport.mount('github'); // The mount above is syntactic sugar, which is equivalent to // const github = app.passport.authenticate('github', {}); // router.get('/passport/github', github); // router.get('/passport/github/callback', github);
};
Then we also need:
// 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 information from the 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 service to register a new user
const newUser = await ctx.service.user.register(user);
return newUser;
}); // Serialize and store the user information into session. Generally, only a few fields need to be streamlined/saved.
app.passport.serializeUser(async (ctx, user) => {
// process user
// ...
// return user;
}); // Deserialize the user information from the session, check the database to get the complete information
app.passport.deserializeUser(async (ctx, user) => {
// process user
// ...
// return user;
});
};
At this point, we have completed all the configurations. For a complete example, see: eggjs/examples/passport
egg-passport provides the following extensions:
ctx.user - Get current logged in user informationctx.isAuthenticated() - Check if the request is authorizedctx.login(user, [options]) - Start a login session for the userctx.logout() - Exit and clear user information from sessionctx.session.returnTo= - Set redirect address after authentication page successThe API also be provided for:
app.passport.verify(async (ctx, user) => {}) - Check userapp.passport.serializeUser(async (ctx, user) => {}) - Serialize user information into sessionapp.passport.deserializeUser(async (ctx, user) => {}) - Deserialize user information from the sessionapp.passport.authenticate(strategy, options) - Generate the specified authentication middleware
- options.successRedirect - specifies the redirect address after successful authentication
- options.loginURL - jump login address, defaults to /passport/${strategy}
- options.callbackURL - callback address after authorization, defaults to /passport/${strategy}/callbackapp.passport.mount(strategy, options) - Syntactic sugar for developers to configure routingNote:
app.passport.authenticate, if options.successRedirect or options.successReturnToOrRedirect is null, it will redirect to / by defaultPassport has many middleware and it is impossible to have the second encapsulation. Next, let's look at how to use Passport middleware directly in the framework. We will use passport-local for "account password login" as an example:
$ npm i --save passport-local
// app.js
const LocalStrategy = require('passport-local').Strategy;
module.exports = (app) => {
// Mount strategy
app.passport.use(
new LocalStrategy(
{
passReqToCallback: true,
},
(req, username, password, done) => {
// format user
const user = {
provider: 'local',
username,
password,
};
debug('%s %s get user: %j', req.method, req.url, user);
app.passport.doVerify(req, user, done);
},
),
); // Process user information
app.passport.verify(async (ctx, user) => {});
app.passport.serializeUser(async (ctx, user) => {});
app.passport.deserializeUser(async (ctx, user) => {});
};
// app/router.js
module.exports = (app) => {
const { router, controller } = app;
router.get('/', controller.home.index); // Callback page after successful authentication
router.get('/authCallback', controller.home.authCallback); // Render login page, user inputs account password
router.get('/login', controller.home.login); // Login verification
router.post(
'/login',
app.passport.authenticate('local', { successRedirect: '/authCallback' }),
);
};
In the previous section, we learned how to use a Passport middleware in the framework. We can further encapsulate it as a plugin and give back to the community.
initialization:
$ npm init egg --type=plugin egg-passport-local
Configure dependencies in package.json:
{
"name": "egg-passport-local",
"version": "1.0.0",
"eggPlugin": {
"name": "passportLocal",
"dependencies": ["passport"]
},
"dependencies": {
"passport-local": "^1.0.0"
}
}
Configuration:
// {plugin_root}/config/config.default.js
// https://github.com/jaredhanson/passport-local
exports.passportLocal = {};
Note: egg-passport standardizes the configuration fields, which are unified as key and secret, so if the corresponding Passport middleware attribute names are inconsistent, the developer should do the conversion.
Register the passport middleware:
// {plugin_root}/app.js
const LocalStrategy = require('passport-local').Strategy;
module.exports = (app) => {
const config = app.config.passportLocal;
config.passReqToCallback = true;
app.passport.use(
new LocalStrategy(config, (req, username, password, done) => {
// Cleans up the data returned by the Passport plugin and returns the User object
const user = {
provider: 'local',
username,
password,
}; // This does not process application-level logic and passes it to app.passport.verify for unified processing.
app.passport.doVerify(req, user, done);
}),
);
};