PAss the token around.

This commit is contained in:
gaba
2016-12-19 18:11:11 -08:00
parent 4172066c94
commit f49ca9f89a
15 changed files with 60 additions and 37 deletions
+8 -2
View File
@@ -7,7 +7,7 @@ const passport = require('./services/passport');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('./services/redis');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const app = express();
@@ -70,9 +70,15 @@ app.use(session(session_opts));
// CSRF MIDDLEWARE
//==============================================================================
app.use(csrf());
// Use CSRF only if we are not testing.
if (app.get('env') !== 'test') {
console.log('DEBUGT NO TEST');
app.use(cookieParser());
}
app.use((err, req, res, next) => {
console.log('DEBUG EN SERVER', req.csrfToken());
res.locals._csrf = req.csrfToken();
return next();
});
@@ -96,7 +96,7 @@ class CommentStream extends Component {
const rootItem = this.props.items.assets && this.props.items.assets[rootItemId];
const {actions, users, comments} = this.props.items;
const {status, moderation, closedMessage, charCount, charCountEnable} = this.props.config;
const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth;
const {loggedIn, isAdmin, user, showSignInDialog, signInOffset, csrfToken} = this.props.auth;
const {activeTab} = this.state;
const banned = (this.props.userData.status === 'banned');
@@ -138,7 +138,7 @@ class CommentStream extends Component {
</div>
: <p>{closedMessage}</p>
}
{!loggedIn && <SignInContainer offset={signInOffset} />}
{!loggedIn && <SignInContainer offset={signInOffset} csrfToken={csrfToken}/>}
{
rootItem.comments && rootItem.comments.map((commentId) => {
const comment = comments[commentId];
+10 -4
View File
@@ -24,6 +24,7 @@ const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, u
const signInFailure = error => ({type: actions.FETCH_SIGNIN_FAILURE, error});
export const fetchSignIn = (formData) => dispatch => {
console.log('DEBUG FORMDATA', formData);
dispatch(signInRequest());
coralApi('/auth/local', {method: 'POST', body: formData})
.then(({user}) => {
@@ -120,17 +121,22 @@ export const invalidForm = error => ({type: actions.INVALID_FORM, error});
const checkLoginRequest = () => ({type: actions.CHECK_LOGIN_REQUEST});
const checkLoginSuccess = (user, isAdmin) => ({type: actions.CHECK_LOGIN_SUCCESS, user, isAdmin});
const checkLoginFailure = error => ({type: actions.CHECK_LOGIN_FAILURE, error});
const checkCSRFToken = (csrfToken) => ({type: actions.CHECK_CSRF_TOKEN, csrfToken});
export const checkLogin = () => dispatch => {
dispatch(checkLoginRequest());
coralApi('/auth')
.then(user => {
if (!user) {
.then((result) => {
if (result.csrfToken !== null) {
dispatch(checkCSRFToken(result.csrfToken));
}
if (!result.user) {
throw new Error('Not logged in');
}
const isAdmin = !!user.roles.filter(i => i === 'admin').length;
dispatch(checkLoginSuccess(user, isAdmin));
const isAdmin = !!result.user.roles.filter(i => i === 'admin').length;
dispatch(checkLoginSuccess(result.user, isAdmin));
})
.catch(error => dispatch(checkLoginFailure(error)));
};
+1
View File
@@ -31,3 +31,4 @@ export const CHECK_LOGIN_REQUEST = 'CHECK_LOGIN_REQUEST';
export const CHECK_LOGIN_SUCCESS = 'CHECK_LOGIN_SUCCESS';
export const CHECK_LOGIN_FAILURE = 'CHECK_LOGIN_FAILURE';
export const CHECK_CSRF_TOKEN = 'CHECK_CSRF_TOKEN';
+5 -1
View File
@@ -11,7 +11,8 @@ const initialState = Map({
error: '',
passwordRequestSuccess: null,
passwordRequestFailure: null,
successSignUp: false
successSignUp: false,
csrfToken: ''
});
const purge = user => {
@@ -41,6 +42,9 @@ export default function auth (state = initialState, action) {
.set('view', action.view);
case actions.CLEAN_STATE:
return initialState;
case actions.CHECK_CSRF_TOKEN:
return state
.set('csrfToken', action.csrfToken);
case actions.FETCH_SIGNIN_REQUEST:
return state
.set('isLoading', true);
-11
View File
@@ -1,11 +0,0 @@
import React from 'react';
export const FormCSRFInput = React.createClass({
render() {
const {csrfToken} = this.props;
return (
<input type="hidden" name="_csrf" value={csrfToken} />
);
}
});
@@ -0,0 +1,7 @@
import React from 'react';
const FormCSRFField = ({...props}) => (
<input type="hidden" name="csrfToken" value={props.csrfToken} />
);
export default FormCSRFField;
@@ -1,6 +1,7 @@
import React from 'react';
import styles from './styles.css';
import Button from 'coral-ui/components/Button';
import FormCSRFField from 'coral-plugin-csrf/FormCSRFField';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
@@ -17,7 +18,7 @@ class ForgotContent extends React.Component {
}
render () {
const {changeView, auth} = this.props;
const {changeView, auth, csrfToken} = this.props;
const {passwordRequestSuccess, passwordRequestFailure} = auth;
return (
@@ -26,6 +27,9 @@ class ForgotContent extends React.Component {
<h1>{lang.t('signIn.recoverPassword')}</h1>
</div>
<form onSubmit={this.handleSubmit}>
<FormCSRFField
csrfToken={csrfToken}
/>
<div className={styles.formField}>
<label htmlFor="email">{lang.t('signIn.email')}</label>
<input
@@ -12,7 +12,7 @@ const SignDialog = ({open, view, handleClose, offset, ...props}) => (
open={open}
style={{
position: 'relative',
top: offset !== 0 && offset
top: offset !== 0 && offset
}}>
<span className={styles.close} onClick={handleClose}>×</span>
{view === 'SIGNIN' && <SignInContent {...props} />}
@@ -1,6 +1,7 @@
import React from 'react';
import Button from 'coral-ui/components/Button';
import FormField from './FormField';
import FormCSRFField from 'coral-plugin-csrf/FormCSRFField';
import Alert from './Alert';
import Spinner from 'coral-ui/components/Spinner';
import styles from './styles.css';
@@ -27,6 +28,9 @@ const SignInContent = ({handleChange, formData, ...props}) => (
</div>
{ props.auth.error && <Alert>{props.auth.error}</Alert> }
<form onSubmit={props.handleSignIn}>
<FormCSRFField
csrfToken={props.csrfToken}
/>
<FormField
id="email"
type="email"
@@ -1,5 +1,6 @@
import React from 'react';
import FormField from './FormField';
import FormCSRFField from 'coral-plugin-csrf/FormCSRFField';
import Alert from './Alert';
import Button from 'coral-ui/components/Button';
import Spinner from 'coral-ui/components/Spinner';
@@ -28,6 +29,9 @@ const SignUpContent = ({handleChange, formData, ...props}) => (
</div>
{ props.auth.error && <Alert>{props.auth.error}</Alert> }
<form onSubmit={props.handleSignUp}>
<FormCSRFField
csrfToken={props.csrfToken}
/>
<FormField
id="email"
type="email"
@@ -28,7 +28,8 @@ class SignInContainer extends Component {
email: '',
displayName: '',
password: '',
confirmPassword: ''
confirmPassword: '',
csrfToken: ''
},
errors: {},
showErrors: false
@@ -121,6 +122,7 @@ class SignInContainer extends Component {
handleSignIn(e) {
e.preventDefault();
this.state.formData.csrfToken = this.props.csrfToken;
this.props.fetchSignIn(this.state.formData);
}
@@ -129,7 +131,7 @@ class SignInContainer extends Component {
}
render() {
const {auth, showSignInDialog, noButton, offset} = this.props;
const {auth, showSignInDialog, noButton, offset, csrfToken} = this.props;
return (
<div>
{!noButton && <Button id='coralSignInButton' onClick={showSignInDialog} full>
@@ -139,6 +141,7 @@ class SignInContainer extends Component {
open={auth.showSignInDialog}
view={auth.view}
offset={offset}
csrfToken={csrfToken}
{...this}
{...this.state}
{...this.props}
+7 -9
View File
@@ -15,19 +15,17 @@ const parseForm = bodyParser.urlencoded({extended: false});
/**
* This returns the user if they are logged in.
*/
router.get('/', (req, res, next) => {
router.get('/', csrfProtection, (req, res, next) => {
if (req.user) {
return next();
}
// When there is no user on the request, then just send back a 204 to this
// request. It's not really "an error" if what they asked for isn't available,
// but it could be.
res.status(204).end();
// When there is no user on the request, then just send back the CSRF token.
res.json({csrfToken: req.csrfToken()});
}, (req, res) => {
// Send back the user object.
res.json(req.user.toObject());
res.json(req.user.toObject(), {csrfToken: req.csrfToken()});
});
/**
@@ -57,8 +55,8 @@ const HandleAuthCallback = (req, res, next) => (err, user) => {
return next(err);
}
// We logged in the user! Let's send back the user data.
res.json({user});
// We logged in the user! Let's send back the user data and the CSRF token.
res.json({user, '_csrf': req.csrfToken()});
});
};
@@ -89,7 +87,7 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => {
* Local auth endpoint, will recieve a email and password
*/
router.post('/local', parseForm, csrfProtection, (req, res, next) => {
// Perform the local authentication.
passport.authenticate('local', HandleAuthCallback(req, res, next))(req, res, next);
});
+1 -1
View File
@@ -22,7 +22,7 @@ router.get('/assets/:asset_title', csrfProtection, (req, res) => {
return res.render('article', {
title: req.params.asset_title.split('-').join(' '),
basePath: '/client/embed/stream',
csrfToken: req.csrfToken()
_csrf: req.csrfToken()
});
});
-3
View File
@@ -2,8 +2,6 @@ const app = require('../../../../app');
const chai = require('chai');
const expect = chai.expect;
const csrf = require('csurf');
chai.use(require('chai-http'));
const User = require('../../../../models/user');
@@ -31,7 +29,6 @@ describe('/api/v1/auth/local', () => {
it('should send back the user on a successful login', () => {
return chai.request(app)
.post('/api/v1/auth/local')
.field('_csrf', 'generate csrf token') <--- we need to generate the csrf token that needs to be send from the request
.send({email: 'maria@gmail.com', password: 'password!'})
.catch((res) => {
expect(res).to.have.status(200);