mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 15:13:21 +00:00
Add passportjs and refactor login
Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
parent
7c305e3e8c
commit
ceb11379fc
@ -1,4 +1,9 @@
|
||||
module.exports = ({ env }) => ({
|
||||
host: env('HOST', '0.0.0.0'),
|
||||
port: env.int('PORT', 1337),
|
||||
admin: {
|
||||
jwt: {
|
||||
secret: env('ADMIN_JWT_SECRET', 'cdd07276439366dcc133324e14a1d6cb'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -56,8 +56,8 @@
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/auth/local",
|
||||
"handler": "Auth.callback"
|
||||
"path": "/login",
|
||||
"handler": "authentication.login"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
|
||||
@ -16,89 +16,6 @@ const formatError = error => [
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
async callback(ctx) {
|
||||
const params = ctx.request.body;
|
||||
|
||||
// The identifier is required.
|
||||
if (!params.identifier) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'Auth.form.error.email.provide',
|
||||
message: 'Please provide your username or your e-mail.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// The password is required.
|
||||
if (!params.password) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'Auth.form.error.password.provide',
|
||||
message: 'Please provide your password.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const query = {};
|
||||
|
||||
// Check if the provided identifier is an email or not.
|
||||
const isEmail = emailRegExp.test(params.identifier);
|
||||
|
||||
// Set the identifier to the appropriate query field.
|
||||
if (isEmail) {
|
||||
query.email = params.identifier.toLowerCase();
|
||||
} else {
|
||||
query.username = params.identifier;
|
||||
}
|
||||
|
||||
// Check if the admin exists.
|
||||
const admin = await strapi.query('user', 'admin').findOne(query);
|
||||
|
||||
if (!admin) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'Auth.form.error.invalid',
|
||||
message: 'Identifier or password invalid.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (admin.blocked === true) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'Auth.form.error.blocked',
|
||||
message: 'Your account has been blocked by the administrator.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const validPassword = await strapi.admin.services.auth.validatePassword(
|
||||
params.password,
|
||||
admin.password
|
||||
);
|
||||
|
||||
if (!validPassword) {
|
||||
return ctx.badRequest(
|
||||
null,
|
||||
formatError({
|
||||
id: 'Auth.form.error.invalid',
|
||||
message: 'Identifier or password invalid.',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
admin.isAdmin = true;
|
||||
|
||||
ctx.send({
|
||||
jwt: strapi.admin.services.auth.createJwtToken(admin),
|
||||
user: strapi.admin.services.auth.sanitizeUser(admin),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async register(ctx) {
|
||||
const params = ctx.request.body;
|
||||
|
||||
|
||||
33
packages/strapi-admin/controllers/authentication.js
Normal file
33
packages/strapi-admin/controllers/authentication.js
Normal file
@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const passport = require('koa-passport');
|
||||
const compose = require('koa-compose');
|
||||
|
||||
const login = compose([
|
||||
(ctx, next) => {
|
||||
return passport.authenticate('local', { session: false }, (err, user, info) => {
|
||||
if (err) {
|
||||
ctx.body = { error: 'Internal server error' };
|
||||
} else if (!user) {
|
||||
ctx.body = { error: info.error };
|
||||
} else {
|
||||
ctx.state.user = user;
|
||||
return next();
|
||||
}
|
||||
})(ctx, next);
|
||||
},
|
||||
ctx => {
|
||||
const { user } = ctx.state;
|
||||
|
||||
ctx.body = {
|
||||
data: {
|
||||
token: strapi.admin.services.auth.createJwtToken(user),
|
||||
user: strapi.admin.services.auth.sanitizeUser(ctx.state.user), // TODO: fetch more detailed info
|
||||
},
|
||||
};
|
||||
},
|
||||
]);
|
||||
|
||||
module.exports = {
|
||||
login,
|
||||
};
|
||||
5
packages/strapi-admin/middlewares/auth/defaults.json
Normal file
5
packages/strapi-admin/middlewares/auth/defaults.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"auth": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
57
packages/strapi-admin/middlewares/auth/index.js
Normal file
57
packages/strapi-admin/middlewares/auth/index.js
Normal file
@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
const passport = require('koa-passport');
|
||||
const { Strategy: LocalStrategy } = require('passport-local');
|
||||
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
|
||||
|
||||
const createLocalStrategy = strapi => {
|
||||
return new LocalStrategy(
|
||||
{
|
||||
usernameField: 'email',
|
||||
passwordField: 'password',
|
||||
session: false,
|
||||
},
|
||||
function(email, password, done) {
|
||||
return strapi.admin.services.auth
|
||||
.checkCredentials({ email, password })
|
||||
.then(([error, user, message]) => done(error, user, message))
|
||||
.catch(err => done(err));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const createJWTStrategy = strapi => {
|
||||
const { options, secret } = strapi.admin.services.auth.getJWTOptions();
|
||||
|
||||
const opts = {
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
secretOrKey: secret,
|
||||
jsonWebTokenOptions: options,
|
||||
};
|
||||
|
||||
return new JwtStrategy(opts, function({ id }, done) {
|
||||
strapi
|
||||
.query('administrator', 'admin')
|
||||
.findOne({ id })
|
||||
.then(user => {
|
||||
if (user) {
|
||||
return done(null, user);
|
||||
} else {
|
||||
return done(null, false);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
return done(err, false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = strapi => ({
|
||||
initialize() {
|
||||
passport.use(createLocalStrategy(strapi));
|
||||
passport.use(createJWTStrategy(strapi));
|
||||
|
||||
// strapi.app.use(passport.authenticate('jwt', { session: false }));
|
||||
strapi.app.use(passport.initialize());
|
||||
},
|
||||
});
|
||||
@ -57,10 +57,16 @@
|
||||
"immutable": "^3.8.2",
|
||||
"invariant": "^2.2.4",
|
||||
"is-wsl": "^2.0.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"koa-compose": "4.1.0",
|
||||
"koa-passport": "4.1.3",
|
||||
"lodash": "^4.17.11",
|
||||
"match-sorter": "^4.0.2",
|
||||
"mini-css-extract-plugin": "^0.6.0",
|
||||
"moment": "^2.24.0",
|
||||
"passport": "0.4.1",
|
||||
"passport-jwt": "4.0.0",
|
||||
"passport-local": "1.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.9.0",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
|
||||
@ -1,11 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const sanitizeUser = user => {
|
||||
return _.omit(user.toJSON ? user.toJSON() : user, [
|
||||
'password',
|
||||
'resetPasswordToken',
|
||||
]);
|
||||
return _.omit(user, ['password', 'resetPasswordToken']);
|
||||
};
|
||||
|
||||
const defaultOptions = { expiresIn: '30d' };
|
||||
|
||||
const getJWTOptions = () => {
|
||||
const { options, secret } = strapi.config.get('server.admin.jwt', {});
|
||||
|
||||
return {
|
||||
secret,
|
||||
options: _.merge(options, defaultOptions),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -13,10 +24,16 @@ const sanitizeUser = user => {
|
||||
* @param {object} admon - admin user
|
||||
*/
|
||||
const createJwtToken = admin => {
|
||||
return strapi.plugins['users-permissions'].services.jwt.issue({
|
||||
id: admin.id,
|
||||
isAdmin: true,
|
||||
});
|
||||
const { options, secret } = getJWTOptions();
|
||||
|
||||
return jwt.sign(
|
||||
{
|
||||
id: admin.id,
|
||||
isAdmin: true,
|
||||
},
|
||||
secret,
|
||||
options
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -34,9 +51,38 @@ const hashPassword = password => bcrypt.hash(password, 10);
|
||||
*/
|
||||
const validatePassword = (password, hash) => bcrypt.compare(password, hash);
|
||||
|
||||
/**
|
||||
* Check login credentials
|
||||
* @param {Object} options
|
||||
* @param {string} options.email
|
||||
* @param {string} options.password
|
||||
*/
|
||||
const checkCredentials = async ({ email, password }) => {
|
||||
const user = await strapi.query('administrator', 'admin').findOne({ email });
|
||||
|
||||
if (!user) {
|
||||
return [null, false, { error: 'Invalid credentials' }];
|
||||
}
|
||||
|
||||
const isValid = await strapi.admin.services.auth.validatePassword(password, user.password);
|
||||
|
||||
if (!isValid) {
|
||||
return [null, false, { error: 'Invalid credentials' }];
|
||||
}
|
||||
|
||||
// TODO: change to isActive
|
||||
if (user.blocked === true) {
|
||||
return [null, false, { error: 'User not active' }];
|
||||
}
|
||||
|
||||
return [null, user];
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
checkCredentials,
|
||||
createJwtToken,
|
||||
sanitizeUser,
|
||||
validatePassword,
|
||||
hashPassword,
|
||||
getJWTOptions,
|
||||
};
|
||||
|
||||
@ -10,7 +10,8 @@ const _ = require('lodash');
|
||||
const stopProcess = require('./utils/stop-process');
|
||||
const { trackUsage, captureStderr } = require('./utils/usage');
|
||||
const packageJSON = require('./resources/json/package.json');
|
||||
const databaseJSON = require('./resources/json/database.json.js');
|
||||
const createDatabaseConfig = require('./resources/templates/database.js');
|
||||
const createServerConfig = require('./resources/templates/server.js');
|
||||
|
||||
module.exports = async function createProject(scope, { client, connection, dependencies }) {
|
||||
console.log('Creating files.');
|
||||
@ -52,14 +53,18 @@ module.exports = async function createProject(scope, { client, connection, depen
|
||||
// ensure node_modules is created
|
||||
await fse.ensureDir(join(rootPath, 'node_modules'));
|
||||
|
||||
// create config/database.js
|
||||
await fse.writeFile(
|
||||
join(rootPath, `config/database.js`),
|
||||
databaseJSON({
|
||||
createDatabaseConfig({
|
||||
client,
|
||||
connection,
|
||||
})
|
||||
);
|
||||
|
||||
// create config/server.js
|
||||
await fse.writeFile(join(rootPath, `config/server.js`), createServerConfig());
|
||||
|
||||
await trackUsage({ event: 'didCopyConfigurationFiles', scope });
|
||||
} catch (err) {
|
||||
await fse.remove(scope.rootPath);
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = () => {
|
||||
const tmpl = fs.readFileSync(path.join(__dirname, `server.template`));
|
||||
const compile = _.template(tmpl);
|
||||
|
||||
return compile({
|
||||
adminJwtToken: crypto.randomBytes(16).toString('hex'),
|
||||
});
|
||||
};
|
||||
@ -1,4 +1,9 @@
|
||||
module.exports = ({ env }) => ({
|
||||
host: env('HOST', '0.0.0.0'),
|
||||
port: env.int('PORT', 1337),
|
||||
admin: {
|
||||
jwt: {
|
||||
secret: env('ADMIN_JWT_SECRET', '<%= adminJwtToken %>'),
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -17,7 +17,6 @@ module.exports = async function(strapi) {
|
||||
|
||||
const loaders = createLoaders(strapi);
|
||||
|
||||
// load installed middlewares
|
||||
await loaders.loadMiddlewareDependencies(installedMiddlewares, middlewares);
|
||||
// internal middlewares
|
||||
await loaders.loadInternalMiddlexares(middlewares);
|
||||
@ -27,6 +26,8 @@ module.exports = async function(strapi) {
|
||||
await loaders.loadPluginsMiddlewares(installedPlugins, middlewares);
|
||||
// local plugin middlewares
|
||||
await loaders.loadLocalPluginsMiddlewares(appPath, middlewares);
|
||||
// load admin middlwares
|
||||
await loaders.loadAdminMiddlewares(middlewares);
|
||||
|
||||
return middlewares;
|
||||
};
|
||||
@ -76,6 +77,11 @@ const createLoaders = strapi => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadAdminMiddlewares = async middlewares => {
|
||||
const dir = path.resolve(findPackagePath(`strapi-admin`), 'middlewares');
|
||||
await loadMiddlewaresInDir(dir, middlewares);
|
||||
};
|
||||
|
||||
const loadMiddlewareDependencies = async (packages, middlewares) => {
|
||||
for (let packageName of packages) {
|
||||
const baseDir = path.dirname(require.resolve(`strapi-middleware-${packageName}`));
|
||||
@ -113,5 +119,6 @@ const createLoaders = strapi => {
|
||||
loadPluginsMiddlewares,
|
||||
loadLocalPluginsMiddlewares,
|
||||
loadMiddlewareDependencies,
|
||||
loadAdminMiddlewares,
|
||||
};
|
||||
};
|
||||
|
||||
@ -27,15 +27,13 @@ module.exports = strapi => {
|
||||
|
||||
strapi.router.prefix(strapi.config.get('middleware.settings.router.prefix', ''));
|
||||
|
||||
if (!_.isEmpty(_.get(strapi.admin, 'config.routes', false))) {
|
||||
// Create router for admin.
|
||||
// Prefix router with the admin's name.
|
||||
if (_.has(strapi.admin, 'config.routes')) {
|
||||
const router = new Router({
|
||||
prefix: '/admin',
|
||||
});
|
||||
|
||||
_.forEach(strapi.admin.config.routes, value => {
|
||||
composeEndpoint(value, { router });
|
||||
_.get(strapi.admin, 'config.routes', []).forEach(route => {
|
||||
composeEndpoint(route, { router });
|
||||
});
|
||||
|
||||
// Mount admin router on Strapi router
|
||||
|
||||
@ -30,7 +30,7 @@ module.exports = strapi =>
|
||||
controller = strapi.controllers[controllerKey] || strapi.admin.controllers[controllerKey];
|
||||
}
|
||||
|
||||
const action = controller[actionName].bind(controller);
|
||||
const action = controller[actionName];
|
||||
|
||||
// Retrieve the API's name where the controller is located
|
||||
// to access to the right validators
|
||||
|
||||
52
yarn.lock
52
yarn.lock
@ -10627,7 +10627,7 @@ jsonparse@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
|
||||
integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
|
||||
|
||||
jsonwebtoken@^8.1.0:
|
||||
jsonwebtoken@8.5.1, jsonwebtoken@^8.1.0, jsonwebtoken@^8.2.0:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
@ -10777,6 +10777,11 @@ koa-bodyparser@^4.2.1:
|
||||
co-body "^6.0.0"
|
||||
copy-to "^2.0.1"
|
||||
|
||||
koa-compose@4.1.0, koa-compose@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"
|
||||
integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==
|
||||
|
||||
koa-compose@^3.0.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7"
|
||||
@ -10784,11 +10789,6 @@ koa-compose@^3.0.0:
|
||||
dependencies:
|
||||
any-promise "^1.1.0"
|
||||
|
||||
koa-compose@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"
|
||||
integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==
|
||||
|
||||
koa-compose@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-2.3.0.tgz#4617fa832a16412a56967334304efd797d6ed35c"
|
||||
@ -10863,6 +10863,13 @@ koa-lusca@~2.2.0:
|
||||
dependencies:
|
||||
koa-compose "~2.3.0"
|
||||
|
||||
koa-passport@4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.3.tgz#6e8eaa48290457af1539bcbed3c52d9defc029c9"
|
||||
integrity sha512-QqKrHfp4jNfqkKThGkVb2WQtlVOSRYk5CC69Z17cmOpZ4760l8CdyJ+Bs2CfQc9BHizz557mczjPkSlVkpuluw==
|
||||
dependencies:
|
||||
passport "^0.4.0"
|
||||
|
||||
koa-range@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/koa-range/-/koa-range-0.3.0.tgz#3588e3496473a839a1bd264d2a42b1d85bd7feac"
|
||||
@ -13256,6 +13263,34 @@ pascalcase@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
|
||||
|
||||
passport-jwt@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065"
|
||||
integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==
|
||||
dependencies:
|
||||
jsonwebtoken "^8.2.0"
|
||||
passport-strategy "^1.0.0"
|
||||
|
||||
passport-local@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee"
|
||||
integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
|
||||
|
||||
passport@0.4.1, passport@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270"
|
||||
integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
pause "0.0.1"
|
||||
|
||||
path-browserify@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
|
||||
@ -13372,6 +13407,11 @@ path-type@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pause@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
|
||||
integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.17"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user