fix(api): Use /internal for API entry
This commit is contained in:
parent
2be227e873
commit
ef18f5a931
@ -17,13 +17,14 @@ import _ from 'lodash';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import generate from 'nanoid/generate';
|
||||
|
||||
import { homeLocation } from '../../config/env';
|
||||
|
||||
import { fixCompletedChallengeItem } from '../utils';
|
||||
import { themes } from '../utils/themes';
|
||||
import { saveUser, observeMethod } from '../../server/utils/rx.js';
|
||||
import { blacklistedUsernames } from '../../server/utils/constants.js';
|
||||
import { wrapHandledError } from '../../server/utils/create-handled-error.js';
|
||||
import {
|
||||
getServerFullURL,
|
||||
getEmailSender
|
||||
} from '../../server/utils/url-utils.js';
|
||||
import {
|
||||
@ -255,7 +256,7 @@ module.exports = function(User) {
|
||||
throw wrapHandledError(
|
||||
new Error('user already exists'),
|
||||
{
|
||||
redirectTo: '/signin',
|
||||
redirectTo: `${homeLocation}/signin`,
|
||||
message: dedent`
|
||||
The ${user.email} email address is already associated with an account.
|
||||
Try signing in with it here instead.
|
||||
@ -595,7 +596,7 @@ module.exports = function(User) {
|
||||
}
|
||||
const { id: loginToken, created: emailAuthLinkTTL } = token;
|
||||
const loginEmail = this.getEncodedEmail(newEmail ? newEmail : null);
|
||||
const host = getServerFullURL();
|
||||
const host = homeLocation;
|
||||
const mailOptions = {
|
||||
type: 'email',
|
||||
to: newEmail ? newEmail : this.email,
|
||||
|
3
config/env.js
Normal file
3
config/env.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
homeLocation: process.env.HOME_LOCATION
|
||||
};
|
@ -3,7 +3,7 @@ module.exports = {
|
||||
title: 'Gatsby Default Starter'
|
||||
},
|
||||
proxy: {
|
||||
prefix: '/external',
|
||||
prefix: '/internal',
|
||||
url: 'http://localhost:3000'
|
||||
},
|
||||
plugins: [
|
||||
|
@ -18,3 +18,5 @@ PEER=stuff
|
||||
DEBUG=true
|
||||
|
||||
IMAGE_BASE_URL='https://s3.amazonaws.com/freecodecamp/images/'
|
||||
|
||||
HOME_LOCATION='http://localhost:8000'
|
||||
|
@ -16,4 +16,5 @@ export default function bootServices(app) {
|
||||
const middleware = Fetchr.middleware();
|
||||
app.use('/services', middleware);
|
||||
app.use('/external/services', middleware);
|
||||
app.use('/internal/services', middleware);
|
||||
}
|
||||
|
@ -5,13 +5,14 @@ import dedent from 'dedent';
|
||||
import { isEmail } from 'validator';
|
||||
import { check } from 'express-validator/check';
|
||||
|
||||
import { homeLocation } from '../../config/env';
|
||||
|
||||
import {
|
||||
ifUserRedirectTo,
|
||||
ifNoUserRedirectTo,
|
||||
createValidatorErrorHandler
|
||||
} from '../utils/middleware';
|
||||
import { wrapHandledError } from '../utils/create-handled-error.js';
|
||||
import { homeURL } from '../../common/utils/constantStrings.json';
|
||||
|
||||
const isSignUpDisabled = !!process.env.DISABLE_SIGNUP;
|
||||
// const debug = debugFactory('fcc:boot:auth');
|
||||
@ -24,43 +25,21 @@ module.exports = function enableAuthentication(app) {
|
||||
// loopback.io/doc/en/lb2/Authentication-authorization-and-permissions.html
|
||||
app.enableAuth();
|
||||
const ifUserRedirect = ifUserRedirectTo();
|
||||
const ifNoUserRedirectHome = ifNoUserRedirectTo(homeURL);
|
||||
const router = app.loopback.Router();
|
||||
const ifNoUserRedirectHome = ifNoUserRedirectTo(homeLocation);
|
||||
const api = app.loopback.Router();
|
||||
const { AuthToken, User } = app.models;
|
||||
|
||||
router.get('/signup', (req, res) => res.redirect(301, '/signin'));
|
||||
router.get('/email-signin', (req, res) => res.redirect(301, '/signin'));
|
||||
router.get('/login', (req, res) => res.redirect(301, '/signin'));
|
||||
router.get('/deprecated-signin', (req, res) => res.redirect(301, '/signin'));
|
||||
api.get('/signin', ifUserRedirect, (req, res) => res.redirect('/auth/auth0'));
|
||||
|
||||
router.get('/logout', (req, res) => res.redirect(301, '/signout'));
|
||||
|
||||
router.get('/signin',
|
||||
ifUserRedirect,
|
||||
(req, res) => res.redirect('/auth/auth0')
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/update-email',
|
||||
ifNoUserRedirectHome,
|
||||
(req, res) => res.render('account/update-email', {
|
||||
title: 'Update your email'
|
||||
})
|
||||
);
|
||||
|
||||
router.get('/signout', (req, res) => {
|
||||
api.get('/signout', (req, res) => {
|
||||
req.logout();
|
||||
req.session.destroy( (err) => {
|
||||
req.session.destroy(err => {
|
||||
if (err) {
|
||||
throw wrapHandledError(
|
||||
new Error('could not destroy session'),
|
||||
{
|
||||
type: 'info',
|
||||
message: 'Oops, something is not right.',
|
||||
redirectTo: '/'
|
||||
}
|
||||
);
|
||||
throw wrapHandledError(new Error('could not destroy session'), {
|
||||
type: 'info',
|
||||
message: 'Oops, something is not right.',
|
||||
redirectTo: homeLocation
|
||||
});
|
||||
}
|
||||
const config = {
|
||||
signed: !!req.signedCookies,
|
||||
@ -70,8 +49,8 @@ module.exports = function enableAuthentication(app) {
|
||||
res.clearCookie('access_token', config);
|
||||
res.clearCookie('userId', config);
|
||||
res.clearCookie('_csrf', config);
|
||||
res.redirect('/');
|
||||
});
|
||||
res.redirect(homeLocation);
|
||||
});
|
||||
});
|
||||
|
||||
const defaultErrorMsg = dedent`
|
||||
@ -93,112 +72,105 @@ module.exports = function enableAuthentication(app) {
|
||||
|
||||
function getPasswordlessAuth(req, res, next) {
|
||||
const {
|
||||
query: {
|
||||
email: encodedEmail,
|
||||
token: authTokenId,
|
||||
emailChange
|
||||
} = {}
|
||||
query: { email: encodedEmail, token: authTokenId, emailChange } = {}
|
||||
} = req;
|
||||
|
||||
const email = User.decodeEmail(encodedEmail);
|
||||
if (!isEmail(email)) {
|
||||
return next(wrapHandledError(
|
||||
new TypeError('decoded email is invalid'),
|
||||
{
|
||||
return next(
|
||||
wrapHandledError(new TypeError('decoded email is invalid'), {
|
||||
type: 'info',
|
||||
message: 'The email encoded in the link is incorrectly formatted',
|
||||
redirectTo: '/signin'
|
||||
}
|
||||
));
|
||||
redirectTo: `${homeLocation}/signin`
|
||||
})
|
||||
);
|
||||
}
|
||||
// first find
|
||||
return AuthToken.findOne$({ where: { id: authTokenId } })
|
||||
.flatMap(authToken => {
|
||||
if (!authToken) {
|
||||
throw wrapHandledError(
|
||||
new Error(`no token found for id: ${authTokenId}`),
|
||||
{
|
||||
type: 'info',
|
||||
message: defaultErrorMsg,
|
||||
redirectTo: '/signin'
|
||||
}
|
||||
);
|
||||
}
|
||||
// find user then validate and destroy email validation token
|
||||
// finally retun user instance
|
||||
return User.findOne$({ where: { id: authToken.userId } })
|
||||
.flatMap(user => {
|
||||
if (!user) {
|
||||
throw wrapHandledError(
|
||||
new Error(`no user found for token: ${authTokenId}`),
|
||||
{
|
||||
type: 'info',
|
||||
message: defaultErrorMsg,
|
||||
redirectTo: '/signin'
|
||||
}
|
||||
);
|
||||
}
|
||||
if (user.email !== email) {
|
||||
if (!emailChange || (emailChange && user.newEmail !== email)) {
|
||||
return (
|
||||
AuthToken.findOne$({ where: { id: authTokenId } })
|
||||
.flatMap(authToken => {
|
||||
if (!authToken) {
|
||||
throw wrapHandledError(
|
||||
new Error(`no token found for id: ${authTokenId}`),
|
||||
{
|
||||
type: 'info',
|
||||
message: defaultErrorMsg,
|
||||
redirectTo: `${homeLocation}/signin`
|
||||
}
|
||||
);
|
||||
}
|
||||
// find user then validate and destroy email validation token
|
||||
// finally retun user instance
|
||||
return User.findOne$({ where: { id: authToken.userId } }).flatMap(
|
||||
user => {
|
||||
if (!user) {
|
||||
throw wrapHandledError(
|
||||
new Error('user email does not match'),
|
||||
new Error(`no user found for token: ${authTokenId}`),
|
||||
{
|
||||
type: 'info',
|
||||
message: defaultErrorMsg,
|
||||
redirectTo: '/signin'
|
||||
redirectTo: `${homeLocation}/signin`
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
return authToken.validate$()
|
||||
.map(isValid => {
|
||||
if (!isValid) {
|
||||
if (user.email !== email) {
|
||||
if (!emailChange || (emailChange && user.newEmail !== email)) {
|
||||
throw wrapHandledError(
|
||||
new Error('token is invalid'),
|
||||
new Error('user email does not match'),
|
||||
{
|
||||
type: 'info',
|
||||
message: defaultErrorMsg,
|
||||
redirectTo: `${homeLocation}/signin`
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
return authToken
|
||||
.validate$()
|
||||
.map(isValid => {
|
||||
if (!isValid) {
|
||||
throw wrapHandledError(new Error('token is invalid'), {
|
||||
type: 'info',
|
||||
message: `
|
||||
Looks like the link you clicked has expired,
|
||||
please request a fresh link, to sign in.
|
||||
`,
|
||||
redirectTo: '/signin'
|
||||
}
|
||||
);
|
||||
}
|
||||
return authToken.destroy$();
|
||||
})
|
||||
.map(() => user);
|
||||
});
|
||||
})
|
||||
// at this point token has been validated and destroyed
|
||||
// update user and log them in
|
||||
.map(user => user.loginByRequest(req, res))
|
||||
.do(() => {
|
||||
req.flash(
|
||||
'success',
|
||||
'Success! You have signed in to your account. Happy Coding!'
|
||||
);
|
||||
return res.redirect('/');
|
||||
})
|
||||
.subscribe(
|
||||
() => {},
|
||||
next
|
||||
);
|
||||
redirectTo: `${homeLocation}/signin`
|
||||
});
|
||||
}
|
||||
return authToken.destroy$();
|
||||
})
|
||||
.map(() => user);
|
||||
}
|
||||
);
|
||||
})
|
||||
// at this point token has been validated and destroyed
|
||||
// update user and log them in
|
||||
.map(user => user.loginByRequest(req, res))
|
||||
.do(() => {
|
||||
req.flash(
|
||||
'success',
|
||||
'Success! You have signed in to your account. Happy Coding!'
|
||||
);
|
||||
return res.redirect(homeLocation);
|
||||
})
|
||||
.subscribe(() => {}, next)
|
||||
);
|
||||
}
|
||||
|
||||
router.get(
|
||||
api.get(
|
||||
'/passwordless-auth',
|
||||
ifUserRedirect,
|
||||
passwordlessGetValidators,
|
||||
createValidatorErrorHandler('errors', '/signin'),
|
||||
createValidatorErrorHandler('errors', `${homeLocation}/signin`),
|
||||
getPasswordlessAuth
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/passwordless-change',
|
||||
(req, res) => res.redirect(301, '/confirm-email')
|
||||
api.get('/passwordless-change', (req, res) =>
|
||||
res.redirect(301, '/confirm-email')
|
||||
);
|
||||
router.get(
|
||||
|
||||
api.get(
|
||||
'/confirm-email',
|
||||
ifNoUserRedirectHome,
|
||||
passwordlessGetValidators,
|
||||
@ -214,21 +186,18 @@ module.exports = function enableAuthentication(app) {
|
||||
const { body: { email } = {} } = req;
|
||||
|
||||
return User.findOne$({ where: { email } })
|
||||
.flatMap(_user => Observable.if(
|
||||
.flatMap(_user =>
|
||||
Observable.if(
|
||||
// if no user found create new user and save to db
|
||||
_.constant(_user),
|
||||
Observable.of(_user),
|
||||
User.create$({ email })
|
||||
)
|
||||
.flatMap(user => user.requestAuthEmail(!_user))
|
||||
).flatMap(user => user.requestAuthEmail(!_user))
|
||||
)
|
||||
.do(msg => {
|
||||
let redirectTo = '/';
|
||||
let redirectTo = homeLocation;
|
||||
|
||||
if (
|
||||
req.session &&
|
||||
req.session.returnTo
|
||||
) {
|
||||
if (req.session && req.session.returnTo) {
|
||||
redirectTo = req.session.returnTo;
|
||||
}
|
||||
|
||||
@ -242,10 +211,10 @@ module.exports = function enableAuthentication(app) {
|
||||
'/passwordless-auth',
|
||||
ifUserRedirect,
|
||||
passwordlessPostValidators,
|
||||
createValidatorErrorHandler('errors', '/signin'),
|
||||
createValidatorErrorHandler('errors', `${homeLocation}/signin`),
|
||||
postPasswordlessAuth
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
app.use(api);
|
||||
app.use('/internal', api);
|
||||
};
|
||||
|
@ -169,6 +169,7 @@ export default function(app) {
|
||||
|
||||
app.use(api);
|
||||
app.use('/external', api);
|
||||
app.use('/internal', api);
|
||||
app.use(router);
|
||||
|
||||
function modernChallengeCompleted(req, res, next) {
|
||||
|
@ -3,6 +3,8 @@ import { Observable } from 'rx';
|
||||
import debugFactory from 'debug';
|
||||
import dedent from 'dedent';
|
||||
|
||||
import { homeLocation } from '../../config/env';
|
||||
|
||||
import nonprofits from '../utils/commit.json';
|
||||
import {
|
||||
commitGoals,
|
||||
@ -23,7 +25,7 @@ import {
|
||||
} from '../utils/middleware';
|
||||
|
||||
const sendNonUserToSignIn = ifNoUserRedirectTo(
|
||||
'/signin',
|
||||
`${homeLocation}/signin`,
|
||||
'You must be signed in to commit to a nonprofit.',
|
||||
'info'
|
||||
);
|
||||
|
@ -175,7 +175,7 @@ export default function settingsController(app) {
|
||||
refetchCompletedChallenges
|
||||
);
|
||||
api.post('/update-flags', ifNoUser401, updateFlags);
|
||||
api.post(
|
||||
api.put(
|
||||
'/update-my-email',
|
||||
ifNoUser401,
|
||||
updateMyEmailValidators,
|
||||
@ -190,7 +190,7 @@ export default function settingsController(app) {
|
||||
updateMyCurrentChallenge
|
||||
);
|
||||
api.post(
|
||||
'/external/update-my-current-challenge',
|
||||
'/update-my-current-challenge',
|
||||
ifNoUser401,
|
||||
updateMyCurrentChallengeValidators,
|
||||
createValidatorErrorHandler(alertTypes.danger),
|
||||
@ -209,5 +209,6 @@ export default function settingsController(app) {
|
||||
api.post('/update-my-username', ifNoUser401, updateMyUsername);
|
||||
|
||||
app.use('/external', api);
|
||||
app.use('/internal', api);
|
||||
app.use(api);
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ module.exports = function bootUser(app) {
|
||||
getReportUserProfile
|
||||
);
|
||||
|
||||
|
||||
app.use(router);
|
||||
app.use('/external', api);
|
||||
app.use('/internal', api);
|
||||
};
|
||||
|
||||
function readSessionUser(req, res, next) {
|
||||
|
@ -11,7 +11,7 @@ export default function() {
|
||||
return function csrf(req, res, next) {
|
||||
|
||||
const path = req.path.split('/')[1];
|
||||
if (/(api|external|^p$)/.test(path)) {
|
||||
if (/(^api$|^external$|^internal$|^p$)/.test(path)) {
|
||||
return next();
|
||||
}
|
||||
return protection(req, res, next);
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { inspect } from 'util';
|
||||
import _ from 'lodash/fp';
|
||||
import accepts from 'accepts';
|
||||
|
||||
import { homeLocation } from '../../config/env';
|
||||
|
||||
import { unwrapHandledError } from '../utils/create-handled-error.js';
|
||||
|
||||
const isDev = process.env.NODE_ENV !== 'production';
|
||||
@ -57,7 +60,7 @@ export default function prodErrorHandler() {
|
||||
const accept = accepts(req);
|
||||
const type = accept.type('html', 'json', 'text');
|
||||
|
||||
const redirectTo = handled.redirectTo || '/';
|
||||
const redirectTo = handled.redirectTo || `${homeLocation}/`;
|
||||
const message = handled.message ||
|
||||
'Oops! Something went wrong. Please try again later';
|
||||
|
||||
|
@ -2,11 +2,13 @@ import loopback from 'loopback';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { isBefore } from 'date-fns';
|
||||
|
||||
import { homeLocation } from '../../config/env';
|
||||
|
||||
import { wrapHandledError } from '../utils/create-handled-error';
|
||||
|
||||
export default () => function authorizeByJWT(req, res, next) {
|
||||
const path = req.path.split('/')[1];
|
||||
if (/external/.test(path)) {
|
||||
if (/^external$|^internal$/.test(path)) {
|
||||
const cookie = req.signedCookies && req.signedCookies['jwt_access_token'] ||
|
||||
req.cookie && req.cookie['jwt_access_token'];
|
||||
if (!cookie) {
|
||||
@ -14,7 +16,7 @@ export default () => function authorizeByJWT(req, res, next) {
|
||||
new Error('Access token is required for this request'),
|
||||
{
|
||||
type: 'info',
|
||||
redirect: '/signin',
|
||||
redirect: `${homeLocation}/signin`,
|
||||
message: 'Access token is required for this request',
|
||||
status: 403
|
||||
}
|
||||
@ -28,7 +30,7 @@ export default () => function authorizeByJWT(req, res, next) {
|
||||
new Error(err.message),
|
||||
{
|
||||
type: 'info',
|
||||
redirct: '/signin',
|
||||
redirect: `${homeLocation}/signin`,
|
||||
message: 'Your access token is invalid',
|
||||
status: 403
|
||||
}
|
||||
@ -41,7 +43,7 @@ export default () => function authorizeByJWT(req, res, next) {
|
||||
new Error('Access token is no longer vaild'),
|
||||
{
|
||||
type: 'info',
|
||||
redirect: '/signin',
|
||||
redirect: `${homeLocation}/signin`,
|
||||
message: 'Access token is no longer vaild',
|
||||
status: 403
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||
|
||||
<%= host %>/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
<%= host %>/internal/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
|
||||
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
Welcome to the freeCodeCamp community!
|
||||
Welcome to the freeCodeCamp community!
|
||||
|
||||
We have created a new account for you.
|
||||
|
||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||
|
||||
<%= host %>/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
<%= host %>/internal/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||
|
||||
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Please confirm this address for freeCodeCamp:
|
||||
|
||||
<%= host %>/confirm-email?email=<%= loginEmail %>&token=<%= loginToken %>&emailChange=<%= emailChange %>
|
||||
<%= host %>/internal/confirm-email?email=<%= loginEmail %>&token=<%= loginToken %>&emailChange=<%= emailChange %>
|
||||
|
||||
Happy coding!
|
||||
|
||||
|
@ -1,15 +1,17 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const base = '/internal';
|
||||
|
||||
function get(path) {
|
||||
return axios.get(`/external${path}`);
|
||||
return axios.get(`${base}${path}`);
|
||||
}
|
||||
|
||||
function post(path, body) {
|
||||
return axios.post(`/external${path}`, body);
|
||||
return axios.post(`${base}${path}`, body);
|
||||
}
|
||||
|
||||
function put(path, body) {
|
||||
return axios.put(`/external${path}`, body);
|
||||
return axios.put(`${base}${path}`, body);
|
||||
}
|
||||
|
||||
function sniff(things) {
|
||||
@ -22,5 +24,9 @@ export function getSessionUser() {
|
||||
}
|
||||
|
||||
export function putUserAcceptsTerms(quincyEmails) {
|
||||
return put('/update-privacy-terms', {quincyEmails})
|
||||
return put('/update-privacy-terms', { quincyEmails });
|
||||
}
|
||||
|
||||
export function putUserUpdateEmail(email) {
|
||||
return put('/update-my-email', { email });
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user