Use passport standard error handling

Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
Alexandre Bodin 2020-05-12 14:57:24 +02:00
parent 298dcac271
commit a4f294f1c4
10 changed files with 61 additions and 76 deletions

View File

@ -3,7 +3,7 @@ module.exports = ({ env }) => ({
port: env.int('PORT', 1337),
admin: {
jwt: {
secret: env('ADMIN_JWT_SECRET', 'cdd07276439366dcc133324e14a1d6cb'),
secret: env('ADMIN_JWT_SECRET', 'example-token'),
},
},
});

View File

@ -12,7 +12,7 @@ module.exports = {
}
if (!user) {
return ctx.badRequest(info.error);
return ctx.badRequest(info.message);
}
ctx.state.user = user;

View File

@ -2,7 +2,6 @@
const passport = require('koa-passport');
const { Strategy: LocalStrategy } = require('passport-local');
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
const createLocalStrategy = strapi => {
return new LocalStrategy(
@ -20,38 +19,38 @@ const createLocalStrategy = strapi => {
);
};
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());
strapi.app.use(async (ctx, next) => {
if (
ctx.request.header.authorization &&
ctx.request.header.authorization.split(' ')[0] === 'Bearer'
) {
const token = ctx.request.header.authorization.split(' ')[1];
const { payload, isValid } = strapi.admin.services.auth.decodeToken(token);
if (isValid) {
// request is made by an admin
const admin = await strapi
.query('administrator', 'admin')
.findOne({ id: payload.id }, []);
if (!admin || admin.blocked === true) {
return ctx.forbidden('Invalid credentials');
}
ctx.state.admin = admin;
ctx.state.user = admin;
return next();
}
}
return next();
});
},
});

View File

@ -64,8 +64,6 @@
"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",

View File

@ -8,7 +8,7 @@ const sanitizeUser = user => {
return _.omit(user, ['password', 'resetPasswordToken']);
};
const defaultOptions = { expiresIn: '1s' };
const defaultOptions = { expiresIn: '30d' };
const getJWTOptions = () => {
const { options, secret } = strapi.config.get('server.admin.jwt', {});
@ -61,18 +61,18 @@ const checkCredentials = async ({ email, password }) => {
const user = await strapi.query('administrator', 'admin').findOne({ email });
if (!user) {
return [null, false, { error: 'Invalid credentials' }];
return [null, false, { message: 'Invalid credentials' }];
}
const isValid = await strapi.admin.services.auth.validatePassword(password, user.password);
if (!isValid) {
return [null, false, { error: 'Invalid credentials' }];
return [null, false, { message: 'Invalid credentials' }];
}
// TODO: change to isActive
if (user.blocked === true) {
return [null, false, { error: 'User not active' }];
return [null, false, { message: 'User not active' }];
}
return [null, user];

View File

@ -61,7 +61,7 @@ const buildMutation = (mutationName, config) => {
const buildMutationContext = ({ options, graphqlContext }) => {
const { context } = graphqlContext;
const ctx = context.app.createContext(_.clone(context.req), _.clone(context.res));
const ctx = cloneKoaContext(context);
if (options.input && options.input.where) {
ctx.params = convertToParams(options.input.where || {});
@ -134,11 +134,19 @@ const validateResolverOption = config => {
return true;
};
const cloneKoaContext = ctx => {
return Object.assign(ctx.app.createContext(_.clone(ctx.req), _.clone(ctx.res)), {
state: {
...ctx.state,
},
});
};
const buildQueryContext = ({ options, graphqlContext }) => {
const { context } = graphqlContext;
const _options = _.cloneDeep(options);
const ctx = context.app.createContext(_.clone(context.req), _.clone(context.res));
const ctx = cloneKoaContext(context);
// Note: we've to used the Object.defineProperties to reset the prototype. It seems that the cloning the context
// cause a lost of the Object prototype.

View File

@ -3,38 +3,25 @@ const _ = require('lodash');
module.exports = async (ctx, next) => {
let role;
if (ctx.state.user) {
// request is already authenticated in a different way
return next();
}
if (ctx.request && ctx.request.header && ctx.request.header.authorization) {
try {
const { id, isAdmin = false } = await strapi.plugins[
'users-permissions'
].services.jwt.getToken(ctx);
const { id } = await strapi.plugins['users-permissions'].services.jwt.getToken(ctx);
if (id === undefined) {
throw new Error('Invalid token: Token did not contain required fields');
}
if (isAdmin) {
ctx.state.admin = await strapi.query('user', 'admin').findOne({ id }, []);
} else {
ctx.state.user = await strapi.plugins['users-permissions'].services.user.fetch({ id });
}
ctx.state.user = await strapi.plugins['users-permissions'].services.user.fetch({ id });
} catch (err) {
return handleErrors(ctx, err, 'unauthorized');
}
if (ctx.state.admin) {
if (ctx.state.admin.blocked === true) {
return handleErrors(
ctx,
'Your account has been blocked by the administrator.',
'unauthorized'
);
}
ctx.state.user = ctx.state.admin;
return await next();
}
if (!ctx.state.user) {
return handleErrors(ctx, 'User Not Found', 'unauthorized');
}

View File

@ -21,15 +21,15 @@ const register = async () => {
const login = async () => {
const { body } = await rq({
url: '/admin/auth/local',
url: '/admin/login',
method: 'POST',
body: {
identifier: auth.email,
email: auth.email,
password: auth.password,
},
});
return body;
return body.data;
};
module.exports = {
@ -38,7 +38,7 @@ module.exports = {
await register();
// login
const { jwt } = await login();
return jwt;
const { token } = await login();
return token;
},
};

View File

@ -11,6 +11,7 @@ const createRequest = (defaults = {}) => {
};
const createAuthRequest = token => {
console.log(token);
return createRequest({
headers: {
Authorization: `Bearer ${token}`,

View File

@ -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.5.1, jsonwebtoken@^8.1.0, jsonwebtoken@^8.2.0:
jsonwebtoken@8.5.1, jsonwebtoken@^8.1.0:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
@ -13263,14 +13263,6 @@ 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"
@ -13278,12 +13270,12 @@ passport-local@1.0.0:
dependencies:
passport-strategy "1.x.x"
passport-strategy@1.x.x, passport-strategy@^1.0.0:
passport-strategy@1.x.x:
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:
passport@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270"
integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==