Skip to content

Commit

Permalink
Added security, protected routes | react
Browse files Browse the repository at this point in the history
  • Loading branch information
bharvi-bissa committed Feb 17, 2020
1 parent f3da89f commit fcc65d2
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 4,032 deletions.
436 changes: 0 additions & 436 deletions logs/app.log

This file was deleted.

3,278 changes: 0 additions & 3,278 deletions logs/archived/app.2019-10-04.0.log

This file was deleted.

259 changes: 0 additions & 259 deletions logs/archived/app.2019-10-05.0.log

This file was deleted.

1 change: 1 addition & 0 deletions project-manager-react-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"dependencies": {
"axios": "^0.19.0",
"classnames": "^2.2.6",
"jwt-decode": "^2.2.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-redux": "^7.1.3",
Expand Down
89 changes: 63 additions & 26 deletions project-manager-react-client/src/App.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
import React, { Component } from "react";
import jwt_decode from "jwt-decode";
import Header from "./components/Layout/Header";
import Dashboard from "./components/Dashboard";
import { BrowserRouter, Route } from "react-router-dom";
import { Switch, BrowserRouter, Route } from "react-router-dom";

import AddProject from "./components/Project/AddProject";
import UpdateProject from "./components/Project/UpdateProject";
import AddProjectTask from "./components/ProjectBoard/ProjectTasks/AddProjectTask";
import UpdateProjectTask from "./components/ProjectBoard/ProjectTasks/UpdateProjectTask";
import ProjectBoard from "./components/ProjectBoard/ProjectBoard";
import Register from "./components/Auth/Register";
import Login from "./components/Auth/Login";

import { setCurrentUser, logoutUser } from "./actions/authActions";
import { Provider } from "react-redux";

import store from "./Store";
import { registerUser } from "./actions/authActions";
import Register from "./components/Auth/Register";
import Login from "./components/Auth/Login";

import PrivateRoute from "./components/Commons/PrivateRoute";
import SetAuthToken from "./Utils/SetAuthToken";

// Check for token
if (localStorage.accessToken) {
// Set auth token header auth
SetAuthToken(localStorage.accessToken);
// Decode token and get user info and exp
const decoded = jwt_decode(localStorage.accessToken);
// Set user and isAuthenticated
store.dispatch(setCurrentUser(decoded));

// Check for expired token
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Redirect to login
window.location.href = "/auth/login";
}
}

class App extends Component {
render() {
Expand All @@ -21,28 +46,40 @@ class App extends Component {
<BrowserRouter>
{" "}
<Header />
<Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/addProject" component={AddProject} />
<Route
exact
path="/addProjectTask/:projectIdentifier"
component={AddProjectTask}
/>
<Route
exact
path="/updateProjectTask/:projectIdentifier/:taskSequence"
component={UpdateProjectTask}
/>
<Route
exact
path="/projectBoard/:projectIdentifier"
component={ProjectBoard}
/>
<Route
exact
path="/updateProject/:projectIdentifier"
component={UpdateProject}
/>
<Switch>
<PrivateRoute exact path="/dashboard" component={Dashboard} />
</Switch>
<Switch>
<PrivateRoute exact path="/addProject" component={AddProject} />
</Switch>
<Switch>
<PrivateRoute
exact
path="/addProjectTask/:projectIdentifier"
component={AddProjectTask}
/>
</Switch>
<Switch>
<PrivateRoute
exact
path="/updateProjectTask/:projectIdentifier/:taskSequence"
component={UpdateProjectTask}
/>
</Switch>
<Switch>
<PrivateRoute
exact
path="/projectBoard/:projectIdentifier"
component={ProjectBoard}
/>
</Switch>
<Switch>
<PrivateRoute
exact
path="/updateProject/:projectIdentifier"
component={UpdateProject}
/>
</Switch>
<Route exact path="/auth/register" component={Register} />
<Route exact path="/auth/login" component={Login} />
</BrowserRouter>
Expand Down
8 changes: 8 additions & 0 deletions project-manager-react-client/src/Utils/SetAuthToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import axios from "axios";

const setAuthToken = token => {
if (token) axios.defaults.headers.common["Authorization"] = "Bearer " + token;
else delete axios.defaults.headers.common["Authorization"];
};

export default setAuthToken;
7 changes: 7 additions & 0 deletions project-manager-react-client/src/Validation/is-empty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const isEmpty = value =>
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length === 0) ||
(typeof value === "string" && value.trim().length === 0);

export default isEmpty;
35 changes: 33 additions & 2 deletions project-manager-react-client/src/actions/authActions.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import axios from "axios";
import { REGISTER_USER, GET_ERRORS } from "./types";
import { REGISTER_USER, GET_ERRORS, SET_CURRENT_USER } from "./types";
import SetAuthToken from "../Utils/SetAuthToken";
import jwt_decode from "jwt-decode";

export const registerUser = (user, history) => async dispatch => {
try {
const res = await axios.post("/api/auth/signup", user);
history.push("/dashboard");

dispatch({
type: REGISTER_USER,
payload: res.data
Expand All @@ -21,10 +23,39 @@ export const loginUser = (user, history) => async dispatch => {
try {
const res = await axios.post("/api/auth/signin", user);
console.log(res.data);
// get token
const { accessToken } = res.data;
//set token to LocalStorage
localStorage.setItem("accessToken", accessToken);
//set Authorization token in axios request
SetAuthToken(accessToken);
//decode jwt access token
const decoded = jwt_decode(accessToken);
console.log(decoded);
// set current user (logged in user)
dispatch(setCurrentUser(decoded));
history.push("/dashboard");
} catch (error) {
dispatch({
type: GET_ERRORS,
payload: error.response.data
});
}
};

// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};

export const logoutUser = () => dispatch => {
// Remove token from localStorage
localStorage.removeItem("accessToken");
// Remove auth header for future requests
SetAuthToken(false);
// Set current user to {} which will set isAuthenticated to false
dispatch(setCurrentUser({}));
};
1 change: 1 addition & 0 deletions project-manager-react-client/src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export const GET_PROJECT_TASK = "GET_PROJECT_TASK";
//Types for auth actions
export const REGISTER_USER = "REGISTER_USER";
export const LOGIN_USER = "LOGIN_USER";
export const SET_CURRENT_USER = "SET_CURRENT_USER";
9 changes: 9 additions & 0 deletions project-manager-react-client/src/components/Auth/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,22 @@ class Register extends Component {

render() {
const { errors } = this.state;

return (
<div>
<div className="project">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h2 className=" text-center">Login</h2>
{errors.message ? (
<div className="alert alert-danger">
<div>{errors.message ? errors.message : ""}</div>
</div>
) : (
""
)}

<hr />
<form onSubmit={this.onSubmit}>
<div className="form-group">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
<Route
{...rest}
render={props =>
auth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect to="auth/login" />
)
}
/>
);

PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
auth: state.auth
});

export default connect(mapStateToProps)(PrivateRoute);
67 changes: 41 additions & 26 deletions project-manager-react-client/src/components/Layout/Header.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { logoutUser } from "../../actions/authActions";
import { Link } from "react-router-dom";

class Header extends Component {
onLogoutClick = e => {
e.preventDefault();
this.props.logoutUser();
};
render() {
const { isAuthenticated, user } = this.props.auth;
const guestLinks = (
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<Link className="nav-link " to="/auth/register">
Sign Up
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/auth/login">
Login
</Link>
</li>
</ul>
);
const authLinks = (
<div>
<Link
className="navbar-brand"
to="/logout"
onClick={this.onLogoutClick}
>
Logout
</Link>
</div>
);
return (
<nav className="navbar navbar-expand-sm mb-4">
<div className="container">
<a className="navbar-brand" href="Dashboard.html">
<Link className="navbar-brand" to="/dashboard">
Personal Project Management Tool
</a>
</Link>
{isAuthenticated ? authLinks : guestLinks}
<button
className="navbar-toggler"
type="button"
Expand All @@ -16,33 +50,14 @@ class Header extends Component {
>
<span className="navbar-toggler-icon" />
</button>

<div className="collapse navbar-collapse" id="mobile-nav">
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<a className="nav-link" href="/dashboard">
Dashboard
</a>
</li>
</ul>

<ul className="navbar-nav ml-auto">
<li className="nav-item">
<a className="nav-link " href="register.html">
Sign Up
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="login.html">
Login
</a>
</li>
</ul>
</div>
</div>
</nav>
);
}
}

export default Header;
const mapStateToProps = state => ({
auth: state.auth
});

export default connect(mapStateToProps, { logoutUser })(Header);
21 changes: 21 additions & 0 deletions project-manager-react-client/src/reducers/authReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import isEmpty from "../Validation/is-empty";

import { SET_CURRENT_USER } from "../actions/types";

const initialState = {
isAuthenticated: false,
user: {}
};

export default function(state = initialState, action) {
switch (action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
default:
return state;
}
}
4 changes: 3 additions & 1 deletion project-manager-react-client/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { combineReducers } from "redux";
import errorReducer from "./errorReducer";
import projectReducer from "./projectReducer";
import backlogReducer from "./backlogReducer";
import authReducer from "./authReducer";

export default combineReducers({
errors: errorReducer,
project: projectReducer,
backlog: backlogReducer
backlog: backlogReducer,
auth: authReducer
});
5 changes: 3 additions & 2 deletions src/main/java/com/projectmanagerapp/request/LoginForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import javax.validation.constraints.Size;

public class LoginForm {
@NotBlank

@NotBlank(message="username is required")
@Size(min=3, max = 60)
private String username;

@NotBlank
@NotBlank(message="password is required")
@Size(min = 6, max = 40)
private String password;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ protected void configure(HttpSecurity http) throws Exception {
authorizeRequests()
.antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**").permitAll()
.antMatchers(HttpMethod.OPTIONS,"/api/**").permitAll()
.antMatchers("/api/**").permitAll()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
Expand Down
Loading

0 comments on commit fcc65d2

Please sign in to comment.