Force HTTPS redirection for log in and sign up (#319)
* Higher-order component to force some routes to HTTPS * Force all user-management routes to HTTPS * Redirect to sourceProtocol as route unmounts. By default, no redirection occurs if sourceProtocol is not explicitly defined. * Sets serveSecure flag on new projects and usea after forcing protocol The flag is set to `false` on all projects and as the UI has no way to change this, it always redirects to HTTP after a signup/login action. * Move HoC to be with other top-level components * Server should respond to account page request * Serves AccountView over HTTPS * Turns HTTPS redirection off in development by default Will log to the browser console any redirection that would have happened. Added a line in the README about how to enable this for testing in development.
This commit is contained in:
parent
608ebbf917
commit
dc801ccf7f
6 changed files with 82 additions and 9 deletions
|
@ -30,7 +30,9 @@ This project is currently in development! It will be announced when there is a (
|
||||||
9. Open and close the Redux DevTools using `ctrl+h`, and move them with `ctrl+w`
|
9. Open and close the Redux DevTools using `ctrl+h`, and move them with `ctrl+w`
|
||||||
|
|
||||||
###Testing SSL on your local machine
|
###Testing SSL on your local machine
|
||||||
Please refer to [this gist](https://gist.github.com/andrewn/953ffd5cb17ac2634dc969fc7bdaff3f). This allows you to access the editor using both HTTP and HTTPS. Don't worry about this unless you need to make changes or test HTTPS behavior.
|
Please refer to [this gist](https://gist.github.com/andrewn/953ffd5cb17ac2634dc969fc7bdaff3f). This allows you to access the editor using both HTTP and HTTPS. Don't worry about this unless you need to make changes or test HTTPS behavior.
|
||||||
|
|
||||||
|
The automatic redirection to HTTPS is turned off by default in development. If you need to test this behavior, put `FORCE_TO_HTTPS=true` in your `.env` file.
|
||||||
|
|
||||||
##Production Installation
|
##Production Installation
|
||||||
1. Clone this repostory and `cd` into it
|
1. Clone this repostory and `cd` into it
|
||||||
|
|
44
client/components/forceProtocol.jsx
Normal file
44
client/components/forceProtocol.jsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Higher Order Component that forces the protocol to change on mount
|
||||||
|
*
|
||||||
|
* targetProtocol: the protocol to redirect to on mount
|
||||||
|
* sourceProtocol: the protocol to redirect back to on unmount
|
||||||
|
* disable: if true, the redirection will not happen but what should
|
||||||
|
* have happened will be logged to the console
|
||||||
|
*/
|
||||||
|
const forceProtocol = ({ targetProtocol = 'https:', sourceProtocol, disable = false }) => WrappedComponent => (
|
||||||
|
class ForceProtocol extends React.Component {
|
||||||
|
static propTypes = {}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.redirectToProtocol(targetProtocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (sourceProtocol != null) {
|
||||||
|
this.redirectToProtocol(sourceProtocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectToProtocol(protocol) {
|
||||||
|
const currentProtocol = window.location.protocol;
|
||||||
|
|
||||||
|
if (protocol !== currentProtocol) {
|
||||||
|
if (disable === true) {
|
||||||
|
console.info(`forceProtocol: would have redirected from "${currentProtocol}" to "${protocol}"`);
|
||||||
|
} else {
|
||||||
|
window.location = window.location.href.replace(currentProtocol, protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <WrappedComponent {...this.props} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
export default forceProtocol;
|
|
@ -5,7 +5,8 @@ const initialState = () => {
|
||||||
const generatedString = generate({ words: 2 }).spaced;
|
const generatedString = generate({ words: 2 }).spaced;
|
||||||
const generatedName = generatedString.charAt(0).toUpperCase() + generatedString.slice(1);
|
const generatedName = generatedString.charAt(0).toUpperCase() + generatedString.slice(1);
|
||||||
return {
|
return {
|
||||||
name: generatedName
|
name: generatedName,
|
||||||
|
serveSecure: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Route, IndexRoute } from 'react-router';
|
import { Route, IndexRoute } from 'react-router';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import forceProtocol from './components/forceProtocol';
|
||||||
import App from './modules/App/App';
|
import App from './modules/App/App';
|
||||||
import IDEView from './modules/IDE/pages/IDEView';
|
import IDEView from './modules/IDE/pages/IDEView';
|
||||||
import FullView from './modules/IDE/pages/FullView';
|
import FullView from './modules/IDE/pages/FullView';
|
||||||
|
@ -15,22 +16,38 @@ const checkAuth = (store) => {
|
||||||
store.dispatch(getUser());
|
store.dispatch(getUser());
|
||||||
};
|
};
|
||||||
|
|
||||||
const routes = store =>
|
const routes = (store) => {
|
||||||
(
|
const sourceProtocol = store.getState().project.serveSecure === true ?
|
||||||
|
'https:' :
|
||||||
|
'http:';
|
||||||
|
|
||||||
|
// If the flag is false, we stay on HTTP
|
||||||
|
const forceToHttps = forceProtocol({
|
||||||
|
targetProtocol: 'https:',
|
||||||
|
sourceProtocol,
|
||||||
|
// prints debugging but does not reload page
|
||||||
|
disable: process.env.FORCE_TO_HTTPS === false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
<Route path="/" component={App}>
|
<Route path="/" component={App}>
|
||||||
<IndexRoute component={IDEView} onEnter={checkAuth(store)} />
|
<IndexRoute component={IDEView} onEnter={checkAuth(store)} />
|
||||||
<Route path="/login" component={LoginView} />
|
<Route path="/login" component={forceToHttps(LoginView)} />
|
||||||
<Route path="/signup" component={SignupView} />
|
<Route path="/signup" component={forceToHttps(SignupView)} />
|
||||||
<Route path="/reset-password" component={ResetPasswordView} />
|
<Route path="/reset-password" component={forceToHttps(ResetPasswordView)} />
|
||||||
<Route path="/reset-password/:reset_password_token" component={NewPasswordView} />
|
<Route
|
||||||
|
path="/reset-password/:reset_password_token"
|
||||||
|
component={forceToHttps(NewPasswordView)}
|
||||||
|
/>
|
||||||
<Route path="/projects/:project_id" component={IDEView} />
|
<Route path="/projects/:project_id" component={IDEView} />
|
||||||
<Route path="/full/:project_id" component={FullView} />
|
<Route path="/full/:project_id" component={FullView} />
|
||||||
<Route path="/sketches" component={IDEView} />
|
<Route path="/sketches" component={IDEView} />
|
||||||
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
||||||
<Route path="/:username/sketches" component={IDEView} />
|
<Route path="/:username/sketches" component={IDEView} />
|
||||||
<Route path="/:username/account" component={AccountView} />
|
<Route path="/:username/account" component={forceToHttps(AccountView)} />
|
||||||
<Route path="/about" component={IDEView} />
|
<Route path="/about" component={IDEView} />
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default routes;
|
export default routes;
|
||||||
|
|
|
@ -54,4 +54,10 @@ router.route('/:username/sketches').get((req, res) => {
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.route('/:username/account').get((req, res) => {
|
||||||
|
userExists(req.params.username, exists => (
|
||||||
|
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -36,6 +36,9 @@ module.exports = {
|
||||||
'process.env': {
|
'process.env': {
|
||||||
API_URL: '"' + process.env.API_URL + '"',
|
API_URL: '"' + process.env.API_URL + '"',
|
||||||
CLIENT: JSON.stringify(true),
|
CLIENT: JSON.stringify(true),
|
||||||
|
FORCE_TO_HTTPS: process.env.FORCE_TO_HTTPS === 'true' ?
|
||||||
|
JSON.stringify(true) :
|
||||||
|
JSON.stringify(false),
|
||||||
'NODE_ENV': JSON.stringify('development'),
|
'NODE_ENV': JSON.stringify('development'),
|
||||||
'S3_BUCKET': '"' + process.env.S3_BUCKET + '"'
|
'S3_BUCKET': '"' + process.env.S3_BUCKET + '"'
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue