Skip to content

Commit dc801cc

Browse files
andrewncatarak
authored andcommitted
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.
1 parent 608ebbf commit dc801cc

File tree

6 files changed

+82
-9
lines changed

6 files changed

+82
-9
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ This project is currently in development! It will be announced when there is a (
3030
9. Open and close the Redux DevTools using `ctrl+h`, and move them with `ctrl+w`
3131

3232
###Testing SSL on your local machine
33-
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.
33+
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.
34+
35+
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.
3436

3537
##Production Installation
3638
1. Clone this repostory and `cd` into it

client/components/forceProtocol.jsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React, { PropTypes } from 'react';
2+
3+
/**
4+
* A Higher Order Component that forces the protocol to change on mount
5+
*
6+
* targetProtocol: the protocol to redirect to on mount
7+
* sourceProtocol: the protocol to redirect back to on unmount
8+
* disable: if true, the redirection will not happen but what should
9+
* have happened will be logged to the console
10+
*/
11+
const forceProtocol = ({ targetProtocol = 'https:', sourceProtocol, disable = false }) => WrappedComponent => (
12+
class ForceProtocol extends React.Component {
13+
static propTypes = {}
14+
15+
componentDidMount() {
16+
this.redirectToProtocol(targetProtocol);
17+
}
18+
19+
componentWillUnmount() {
20+
if (sourceProtocol != null) {
21+
this.redirectToProtocol(sourceProtocol);
22+
}
23+
}
24+
25+
redirectToProtocol(protocol) {
26+
const currentProtocol = window.location.protocol;
27+
28+
if (protocol !== currentProtocol) {
29+
if (disable === true) {
30+
console.info(`forceProtocol: would have redirected from "${currentProtocol}" to "${protocol}"`);
31+
} else {
32+
window.location = window.location.href.replace(currentProtocol, protocol);
33+
}
34+
}
35+
}
36+
37+
render() {
38+
return <WrappedComponent {...this.props} />;
39+
}
40+
}
41+
);
42+
43+
44+
export default forceProtocol;

client/modules/IDE/reducers/project.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const initialState = () => {
55
const generatedString = generate({ words: 2 }).spaced;
66
const generatedName = generatedString.charAt(0).toUpperCase() + generatedString.slice(1);
77
return {
8-
name: generatedName
8+
name: generatedName,
9+
serveSecure: false,
910
};
1011
};
1112

client/routes.jsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Route, IndexRoute } from 'react-router';
22
import React from 'react';
3+
import forceProtocol from './components/forceProtocol';
34
import App from './modules/App/App';
45
import IDEView from './modules/IDE/pages/IDEView';
56
import FullView from './modules/IDE/pages/FullView';
@@ -15,22 +16,38 @@ const checkAuth = (store) => {
1516
store.dispatch(getUser());
1617
};
1718

18-
const routes = store =>
19-
(
19+
const routes = (store) => {
20+
const sourceProtocol = store.getState().project.serveSecure === true ?
21+
'https:' :
22+
'http:';
23+
24+
// If the flag is false, we stay on HTTP
25+
const forceToHttps = forceProtocol({
26+
targetProtocol: 'https:',
27+
sourceProtocol,
28+
// prints debugging but does not reload page
29+
disable: process.env.FORCE_TO_HTTPS === false,
30+
});
31+
32+
return (
2033
<Route path="/" component={App}>
2134
<IndexRoute component={IDEView} onEnter={checkAuth(store)} />
22-
<Route path="/login" component={LoginView} />
23-
<Route path="/signup" component={SignupView} />
24-
<Route path="/reset-password" component={ResetPasswordView} />
25-
<Route path="/reset-password/:reset_password_token" component={NewPasswordView} />
35+
<Route path="/login" component={forceToHttps(LoginView)} />
36+
<Route path="/signup" component={forceToHttps(SignupView)} />
37+
<Route path="/reset-password" component={forceToHttps(ResetPasswordView)} />
38+
<Route
39+
path="/reset-password/:reset_password_token"
40+
component={forceToHttps(NewPasswordView)}
41+
/>
2642
<Route path="/projects/:project_id" component={IDEView} />
2743
<Route path="/full/:project_id" component={FullView} />
2844
<Route path="/sketches" component={IDEView} />
2945
<Route path="/:username/sketches/:project_id" component={IDEView} />
3046
<Route path="/:username/sketches" component={IDEView} />
31-
<Route path="/:username/account" component={AccountView} />
47+
<Route path="/:username/account" component={forceToHttps(AccountView)} />
3248
<Route path="/about" component={IDEView} />
3349
</Route>
3450
);
51+
};
3552

3653
export default routes;

server/routes/server.routes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,10 @@ router.route('/:username/sketches').get((req, res) => {
5454
));
5555
});
5656

57+
router.route('/:username/account').get((req, res) => {
58+
userExists(req.params.username, exists => (
59+
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
60+
));
61+
});
62+
5763
export default router;

webpack.config.dev.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ module.exports = {
3636
'process.env': {
3737
API_URL: '"' + process.env.API_URL + '"',
3838
CLIENT: JSON.stringify(true),
39+
FORCE_TO_HTTPS: process.env.FORCE_TO_HTTPS === 'true' ?
40+
JSON.stringify(true) :
41+
JSON.stringify(false),
3942
'NODE_ENV': JSON.stringify('development'),
4043
'S3_BUCKET': '"' + process.env.S3_BUCKET + '"'
4144
}

0 commit comments

Comments
 (0)