mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-02-17 16:00:32 -05:00
committed by
GitHub
parent
335044fece
commit
9b6042e44d
@@ -45,6 +45,7 @@
|
||||
"dedent": "0.7.0",
|
||||
"dotenv": "6.2.0",
|
||||
"express-flash": "0.0.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"express-session": "1.17.3",
|
||||
"express-validator": "6.14.1",
|
||||
"helmet": "3.23.3",
|
||||
@@ -60,12 +61,14 @@
|
||||
"mongodb": "3.6.9",
|
||||
"morgan": "1.10.0",
|
||||
"nanoid": "3.3.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"nodemailer-ses-transport": "1.5.1",
|
||||
"passport": "0.4.1",
|
||||
"passport-auth0": "1.4.2",
|
||||
"passport-local": "1.0.0",
|
||||
"passport-mock-strategy": "2.0.0",
|
||||
"query-string": "6.14.0",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rx": "4.1.0",
|
||||
"stripe": "8.205.0",
|
||||
"uuid": "3.4.0",
|
||||
|
||||
@@ -162,6 +162,8 @@ export default function initializeUser(User) {
|
||||
User.definition.properties.rand.default = getRandomNumber;
|
||||
// increase user accessToken ttl to 900 days
|
||||
User.settings.ttl = 900 * 24 * 60 * 60 * 1000;
|
||||
// Sets ttl to 900 days for mobile login created access tokens
|
||||
User.settings.maxTTL = 900 * 24 * 60 * 60 * 1000;
|
||||
|
||||
// username should not be in blocklist
|
||||
User.validatesExclusionOf('username', {
|
||||
@@ -341,6 +343,21 @@ export default function initializeUser(User) {
|
||||
);
|
||||
};
|
||||
|
||||
User.prototype.mobileLoginByRequest = function mobileLoginByRequest(
|
||||
req,
|
||||
res
|
||||
) {
|
||||
return new Promise((resolve, reject) =>
|
||||
this.createAccessToken({}, (err, accessToken) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
setAccessTokenToResponse({ accessToken }, req, res);
|
||||
return resolve(accessToken);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
User.afterRemote('logout', function ({ req, res }, result, next) {
|
||||
removeCookies(req, res);
|
||||
next();
|
||||
|
||||
@@ -2,10 +2,9 @@ import dedent from 'dedent';
|
||||
import { check } from 'express-validator';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import passport from 'passport';
|
||||
import fetch from 'node-fetch';
|
||||
import { isEmail } from 'validator';
|
||||
|
||||
import { jwtSecret } from '../../../../config/secrets';
|
||||
|
||||
import { decodeEmail } from '../../common/utils';
|
||||
import {
|
||||
createPassportCallbackAuthenticator,
|
||||
@@ -14,7 +13,11 @@ import {
|
||||
} from '../component-passport';
|
||||
import { wrapHandledError } from '../utils/create-handled-error.js';
|
||||
import { removeCookies } from '../utils/getSetAccessToken';
|
||||
import { ifUserRedirectTo, ifNoUserRedirectHome } from '../utils/middleware';
|
||||
import {
|
||||
ifUserRedirectTo,
|
||||
ifNoUserRedirectHome,
|
||||
ifNotMobileRedirect
|
||||
} from '../utils/middleware';
|
||||
import { getRedirectParams } from '../utils/redirection';
|
||||
import { createDeleteUserToken } from '../middlewares/user-token';
|
||||
|
||||
@@ -34,6 +37,7 @@ module.exports = function enableAuthentication(app) {
|
||||
// enable loopback access control authentication. see:
|
||||
// loopback.io/doc/en/lb2/Authentication-authorization-and-permissions.html
|
||||
app.enableAuth();
|
||||
const ifNotMobile = ifNotMobileRedirect();
|
||||
const ifUserRedirect = ifUserRedirectTo();
|
||||
const ifNoUserRedirect = ifNoUserRedirectHome();
|
||||
const devSaveAuthCookies = devSaveResponseAuthCookies();
|
||||
@@ -87,6 +91,8 @@ module.exports = function enableAuthentication(app) {
|
||||
createGetPasswordlessAuth(app)
|
||||
);
|
||||
|
||||
api.get('/mobile-login', ifNotMobile, ifUserRedirect, mobileLogin(app));
|
||||
|
||||
app.use(api);
|
||||
};
|
||||
|
||||
@@ -188,3 +194,53 @@ function createGetPasswordlessAuth(app) {
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function mobileLogin(app) {
|
||||
const {
|
||||
models: { User }
|
||||
} = app;
|
||||
return async function getPasswordlessAuth(req, res, next) {
|
||||
try {
|
||||
const auth0Res = await fetch(
|
||||
`https://${process.env.AUTH0_DOMAIN}/userinfo`,
|
||||
{
|
||||
headers: { Authorization: req.headers.authorization }
|
||||
}
|
||||
);
|
||||
|
||||
if (!auth0Res.ok) {
|
||||
return next(
|
||||
wrapHandledError(new Error('Invalid Auth0 token'), {
|
||||
type: 'danger',
|
||||
message: 'We could not log you in, please try again in a moment.',
|
||||
status: auth0Res.status
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const { email } = await auth0Res.json();
|
||||
|
||||
if (!isEmail(email)) {
|
||||
return next(
|
||||
wrapHandledError(new TypeError('decoded email is invalid'), {
|
||||
type: 'danger',
|
||||
message: 'The email is incorrectly formatted',
|
||||
status: 400
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
User.findOne$({ where: { email } })
|
||||
.do(async user => {
|
||||
if (!user) {
|
||||
user = await User.create({ email });
|
||||
}
|
||||
await user.mobileLoginByRequest(req, res);
|
||||
res.end();
|
||||
})
|
||||
.subscribe(() => {}, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
"./middlewares/constant-headers": {},
|
||||
"./middlewares/csp": {},
|
||||
"./middlewares/flash-cheaters": {},
|
||||
"./middlewares/passport-login": {}
|
||||
"./middlewares/passport-login": {},
|
||||
"./middlewares/rate-limit": {
|
||||
"paths": ["/mobile-login"]
|
||||
}
|
||||
},
|
||||
"files": {},
|
||||
"final:after": {
|
||||
|
||||
19
api-server/src/server/middlewares/rate-limit.js
Normal file
19
api-server/src/server/middlewares/rate-limit.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import MongoStore from 'rate-limit-mongo';
|
||||
|
||||
const url = process.env.MONGODB || process.env.MONGOHQ_URL;
|
||||
|
||||
// Rate limit for mobile login
|
||||
// 10 requests per 15 minute windows
|
||||
export default function rateLimitMiddleware() {
|
||||
return rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 10,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
store: new MongoStore({
|
||||
uri: url,
|
||||
expireTimeMs: 15 * 60 * 1000
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -26,6 +26,7 @@ const updateHooksRE = /^\/hooks\/update-paypal$/;
|
||||
// note: this would be replaced by webhooks later
|
||||
const donateRE = /^\/donate\/charge-stripe$/;
|
||||
const submitCoderoadChallengeRE = /^\/coderoad-challenge-completed$/;
|
||||
const mobileLoginRE = /^\/mobile-login\/?$/;
|
||||
|
||||
const _pathsAllowedREs = [
|
||||
authRE,
|
||||
@@ -41,7 +42,8 @@ const _pathsAllowedREs = [
|
||||
unsubscribeRE,
|
||||
updateHooksRE,
|
||||
donateRE,
|
||||
submitCoderoadChallengeRE
|
||||
submitCoderoadChallengeRE,
|
||||
mobileLoginRE
|
||||
];
|
||||
|
||||
export function isAllowedPath(path, pathsAllowedREs = _pathsAllowedREs) {
|
||||
|
||||
@@ -77,6 +77,20 @@ export function ifUserRedirectTo(status) {
|
||||
};
|
||||
}
|
||||
|
||||
export function ifNotMobileRedirect() {
|
||||
return (req, res, next) => {
|
||||
//
|
||||
// Todo: Use the below check once we have done more research on usage
|
||||
//
|
||||
// const isMobile = /(iPhone|iPad|Android)/.test(req.headers['user-agent']);
|
||||
// if (!isMobile) {
|
||||
// res.json({ error: 'not from mobile' });
|
||||
// } else {
|
||||
// next();
|
||||
// }
|
||||
next();
|
||||
};
|
||||
}
|
||||
// for use with express-validator error formatter
|
||||
export const createValidatorErrorHandler =
|
||||
(...args) =>
|
||||
|
||||
65
package-lock.json
generated
65
package-lock.json
generated
@@ -131,6 +131,7 @@
|
||||
"dedent": "0.7.0",
|
||||
"dotenv": "6.2.0",
|
||||
"express-flash": "0.0.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"express-session": "1.17.3",
|
||||
"express-validator": "6.14.1",
|
||||
"helmet": "3.23.3",
|
||||
@@ -146,12 +147,14 @@
|
||||
"mongodb": "3.6.9",
|
||||
"morgan": "1.10.0",
|
||||
"nanoid": "3.3.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"nodemailer-ses-transport": "1.5.1",
|
||||
"passport": "0.4.1",
|
||||
"passport-auth0": "1.4.2",
|
||||
"passport-local": "1.0.0",
|
||||
"passport-mock-strategy": "2.0.0",
|
||||
"query-string": "6.14.0",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rx": "4.1.0",
|
||||
"stripe": "8.205.0",
|
||||
"uuid": "3.4.0",
|
||||
@@ -24594,6 +24597,17 @@
|
||||
"version": "1.2.0",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
|
||||
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
|
||||
"engines": {
|
||||
"node": ">= 12.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4 || ^5"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session": {
|
||||
"version": "1.17.3",
|
||||
"license": "MIT",
|
||||
@@ -42419,6 +42433,21 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/rate-limit-mongo": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/rate-limit-mongo/-/rate-limit-mongo-2.3.2.tgz",
|
||||
"integrity": "sha512-dLck0j5N/AX9ycVHn5lX9Ti2Wrrwi1LfbXitu/mMBZOo2nC26RgYKJVbcb2mYgb9VMaPI2IwJVzIa2hAQrMaDA==",
|
||||
"dependencies": {
|
||||
"mongodb": "^3.6.7",
|
||||
"twostep": "0.4.2",
|
||||
"underscore": "1.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/rate-limit-mongo/node_modules/underscore": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
|
||||
"integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"license": "MIT",
|
||||
@@ -50191,6 +50220,11 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/twostep": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/twostep/-/twostep-0.4.2.tgz",
|
||||
"integrity": "sha512-O/wdPYk9ey04qcCiw8AQN74DbvLFZLAgnryrNTpV7T/sxB4lcGkCMHynx5xCcA6fCh739ZAqp3HcGhy770X1qA=="
|
||||
},
|
||||
"node_modules/type": {
|
||||
"version": "1.2.0",
|
||||
"license": "ISC"
|
||||
@@ -55889,6 +55923,7 @@
|
||||
"dedent": "0.7.0",
|
||||
"dotenv": "6.2.0",
|
||||
"express-flash": "0.0.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"express-session": "1.17.3",
|
||||
"express-validator": "6.14.1",
|
||||
"helmet": "3.23.3",
|
||||
@@ -55905,6 +55940,7 @@
|
||||
"mongodb": "3.6.9",
|
||||
"morgan": "1.10.0",
|
||||
"nanoid": "3.3.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"nodemailer-ses-transport": "1.5.1",
|
||||
"nodemon": "2.0.16",
|
||||
"passport": "0.4.1",
|
||||
@@ -55912,6 +55948,7 @@
|
||||
"passport-local": "1.0.0",
|
||||
"passport-mock-strategy": "2.0.0",
|
||||
"query-string": "6.14.0",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rx": "4.1.0",
|
||||
"smee-client": "1.2.3",
|
||||
"stripe": "8.205.0",
|
||||
@@ -71687,6 +71724,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"express-rate-limit": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz",
|
||||
"integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==",
|
||||
"requires": {}
|
||||
},
|
||||
"express-session": {
|
||||
"version": "1.17.3",
|
||||
"requires": {
|
||||
@@ -83153,6 +83196,23 @@
|
||||
"range-parser": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"rate-limit-mongo": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/rate-limit-mongo/-/rate-limit-mongo-2.3.2.tgz",
|
||||
"integrity": "sha512-dLck0j5N/AX9ycVHn5lX9Ti2Wrrwi1LfbXitu/mMBZOo2nC26RgYKJVbcb2mYgb9VMaPI2IwJVzIa2hAQrMaDA==",
|
||||
"requires": {
|
||||
"mongodb": "^3.6.7",
|
||||
"twostep": "0.4.2",
|
||||
"underscore": "1.12.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
|
||||
"integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.5.1",
|
||||
"requires": {
|
||||
@@ -88146,6 +88206,11 @@
|
||||
"version": "1.5.0",
|
||||
"dev": true
|
||||
},
|
||||
"twostep": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/twostep/-/twostep-0.4.2.tgz",
|
||||
"integrity": "sha512-O/wdPYk9ey04qcCiw8AQN74DbvLFZLAgnryrNTpV7T/sxB4lcGkCMHynx5xCcA6fCh739ZAqp3HcGhy770X1qA=="
|
||||
},
|
||||
"type": {
|
||||
"version": "1.2.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user