Skip to content

Commit d8594c5

Browse files
authored
Merge pull request #28 from rpichioli/improvements
New actions, reducers, utilitaries, routes and jwt / bcrypt implementation - In development
2 parents 7e6322f + fabbbe9 commit d8594c5

File tree

7 files changed

+85
-65
lines changed

7 files changed

+85
-65
lines changed
Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
11
import axios from 'axios';
2+
import jwtDecode from 'jwt-decode';
3+
import setAuthorizationToken from '../utils/setAuthorizationToken';
24

3-
export const LOGGED_IN = 'LOGGED_IN';
4-
export const LOGGED_OUT = 'LOGGED_OUT';
5+
export const SET_CURRENT_USER = 'SET_CURRENT_USER';
56

6-
export function loggedIn(data) {
7-
return { type: LOGGED_IN, data };
7+
export function setCurrentUser(user) {
8+
return { type: SET_CURRENT_USER, user }
89
}
910

10-
export function loggedOut(data) {
11-
return { type: LOGGED_OUT, data };
12-
}
13-
14-
export function logIn(data) {
11+
export function logout() {
1512
return dispatch => {
16-
const { identifier, password } = data;
17-
axios.post('/api/auth/login', { identifier, password })
18-
.then(response => dispatch(loggedIn(response.data)))
19-
.catch(err => console.error(err));
20-
};
21-
};
13+
// Remove token from storage
14+
localStorage.removeItem('jwtToken');
15+
// Remove token from request headers
16+
setAuthorizationToken(false);
17+
// Send empty user to reducer
18+
dispatch(setCurrentUser({}));
19+
}
20+
}
2221

23-
export function logOut(data) {
22+
export function login(data) {
2423
return dispatch => {
25-
const { identifier } = data;
26-
axios.get('/api/auth/logout', { identifier })
27-
.then(() => dispatch(loggedOut(identifier)))
28-
.catch(err => console.error(err));
29-
};
24+
return axios.post('/api/auth', data).then(res => {
25+
// Receive token
26+
const token = res.data.token;
27+
// Create token key in storage
28+
localStorage.setItem('jwtToken', token);
29+
// Add token in header requests for future actions
30+
setAuthorizationToken(token);
31+
// Promises data to reducer
32+
dispatch(setCurrentUser(jwtDecode(token)));
33+
});
34+
}
3035
}

client/src/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,39 @@ import { composeWithDevTools } from 'redux-devtools-extension';
1010
import thunk from 'redux-thunk';
1111
// Redux Provider for React
1212
import { Provider } from 'react-redux';
13+
// JSON Web Token
14+
import jwt from 'jsonwebtoken';
1315

1416
// API that helps in caching assets and other files when the user is offline or on slow network
1517
import registerServiceWorker from './registerServiceWorker';
1618
// Root reducer to use in Redux Store
1719
import rootReducer from './reducers/rootReducer';
20+
// Actions
21+
import { setCurrentUser } from './actions/authActions';
1822
// The application, high order component
1923
import App from './components/App';
2024
// Application routes as external component
2125
import Routes from './routes/Routes';
2226
// Style
2327
import './style/index.css';
2428

29+
// Browser history
2530
const history = createBrowserHistory();
2631

32+
// Redux store
2733
const store = createStore(
2834
rootReducer,
2935
composeWithDevTools(
3036
applyMiddleware(thunk)
3137
)
3238
);
3339

40+
// Verify if token exists and set it to request headers
41+
if (localStorage.jwtToken) {
42+
setAuthorizationToken(localStorage.jwtToken);
43+
store.dispatch(setCurrentUser(jwt.decode(localStorage.jwtToken)));
44+
}
45+
3446
ReactDOM.render(
3547
<Provider store={store}>
3648
<Router history={history}>
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { LOGGED_IN, LOGGED_OUT } from '../actions/authentication';
1+
import isEmpty from 'lodash/isEmpty';
2+
import { SET_CURRENT_USER } from '../actions/authentication';
23

3-
export default function authentication(state = []action = {}) {
4+
const initialState = { isAuthenticated: false, user: {} };
5+
6+
export default function authentication(state = [], action = {}) {
47
switch(action.type) {
5-
case 'LOGGED_IN':
6-
return state;
7-
case 'LOGGED_OUT':
8-
return state;
8+
case SET_CURRENT_USER:
9+
return {
10+
isAuthenticated: !isEmpty(action.user),
11+
user: action.user
12+
}
913
default: return state;
1014
}
1115
}

client/src/utils/requireAuth.js

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,18 @@
11
import React from 'react';
2-
import { connect } from 'react-redux';
32
import PropTypes from 'prop-types';
4-
import {} from 'react-router-dom';
5-
//import { addFlashMessage } from '../actions/flashMessages';
63

74
/**
8-
* Protect Client-Side Routes with Higher Order Component
5+
* Protect client-side Routes with HOC (Higher Order Component)
96
* @return {[type]} [description]
107
*/
118
export default (ComposedComponent) => {
129
class Authenticate extends React.Component {
13-
/**
14-
* Função chamada antes de iniciar o componente
15-
* Este recurso é um dos vários "life cycle hook" que o React provê para componentes
16-
*/
1710
componentWillMount() {
18-
// Se não está autenticado - Pega do state recebido do Redux mapeado para propriedade
1911
if (!this.props.isAuthenticated) {
20-
// Mensagem de acesso negado
21-
// this.props.addFlashMessage({
22-
// type: 'error',
23-
// text: 'You need to login to access this page'
24-
// });
25-
// Redireciona
2612
this.props.history.push('/login');
2713
}
2814
}
2915

30-
/**
31-
* Função chamada quando há alguma alteração nas propriedades do componente
32-
* Mais um "life cycle hook"
33-
*/
3416
componentWillUpdate(nextProps) {
3517
if (!nextProps.isAuthenticated) {
3618
this.props.history.push('/login');
@@ -45,16 +27,6 @@ export default (ComposedComponent) => {
4527
}
4628

4729
Authenticate.propTypes = {
48-
isAuthenticated: PropTypes.bool.isRequired //,
49-
//addFlashMessage: PropTypes.func.isRequired,
50-
//history: PropTypes.object.isRequired
30+
isAuthenticated: PropTypes.bool.isRequired
5131
}
52-
53-
// let mapStateToProps = (state) => {
54-
// return {
55-
// isAuthenticated: state.auth.isAuthenticated
56-
// }
57-
// }
58-
//
59-
// return connect(mapStateToProps, { addFlashMessage })(Authenticate);
6032
}

server/config/secret.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default {
2+
jwtSecret: 'node_and_more_node_please'
3+
}

server/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import bodyParser from 'body-parser';
55
import cookieParser from 'cookie-parser';
66
import passport from 'passport';
77

8+
import secret from './config/secret';
9+
import models from './models';
10+
import strategies from './config/passport.js'
811
import bands from './routes/bands';
912
import albums from './routes/albums';
10-
import models from './models';
11-
import strategies from './config/passport.js';
1213

1314
const app = express();
1415

@@ -30,13 +31,14 @@ app.use(bodyParser.urlencoded({ extended: true }));
3031
// Passport
3132
// Initialize passport, express + passport session and add them both as middleware.
3233
// We do this by adding these lines some spaces after the bodyParser import line.
33-
app.use(session({ secret: 'node_and_more_node_please!', resave: true, saveUninitialized: true })); // session secret
34+
app.use(session({ secret: secret.jwtSecret, resave: true, saveUninitialized: true })); // session secret
3435
app.use(passport.initialize());
3536
app.use(passport.session()); // persistent login sessions
3637

3738
// Routes
3839
app.use('/api/bands/', bands);
3940
app.use('/api/albums/', albums);
41+
app.use('/api/auth/', app, passport); // Authentication with Passport middleware
4042

4143
// Load passport strategies
4244
strategies(passport, models.user);

server/routes/auth.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
11
import express from 'express';
22
import models from '../models';
3+
import bcrypt from 'bcrypt';
4+
import jwt from 'jsonwebtoken';
5+
import secret from '../config/secret';
36

47
const router = express.Router();
58

6-
router.post('/login', (req, res) => {
7-
res.json({ success: true, message: 'Logged in.' });
8-
});
9+
router.post('/', (req, res) => {
10+
const {identifier, password } = req.body;
11+
12+
models.user.find({ username: identifier }).fetch().then((user) => {
13+
if (user) {
14+
if (bcrypt.compareSync(password, user.get('password_digest'))) {
15+
const token = jwt.sign({
16+
id: user.get('id'),
17+
username: user.get('username')
18+
}, secret.jwtSecret);
919

10-
router.post('/logout', (req, res) => {
11-
res.json({ success: true, message: 'Logged out.' });
20+
res.json({ token });
21+
} else {
22+
res.status(401).json({ errors: { form: 'Invalid credentials' } });
23+
}
24+
} else {
25+
res.status(401).json({ errors: { form: 'Invalid credentials' } });
26+
}
27+
});
28+
29+
router.get('/logout', (req, res) => {
30+
req.session.destroy(() => res.json({ success: true, message: "Successful logout!" }));
31+
});
1232
});
33+
34+
export default router;

0 commit comments

Comments
 (0)