summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jan.tuomi@eficode.com>2020-02-05 19:50:40 +0200
committerJan Tuomi <jan.tuomi@eficode.com>2020-02-05 19:50:40 +0200
commitf9b88d03e322734b3e81e2df6e1dd2d13c919d6b (patch)
treeed22518b1565717cfc710f00307a17898c2e3849
parent71d9490d0988c97b8b023a70e0b61c65f32ad3b9 (diff)
Implement password strength requirements
-rw-r--r--backend/package-lock.json5
-rw-r--r--backend/package.json1
-rw-r--r--backend/src/authn.js10
-rw-r--r--backend/src/index.js11
-rw-r--r--frontend/src/Login.js3
-rw-r--r--frontend/src/Register.js13
-rw-r--r--frontend/src/UserList.js43
-rw-r--r--frontend/src/axios.js1
8 files changed, 60 insertions, 27 deletions
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 6d7cd28..d86cb73 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -1733,6 +1733,11 @@
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
+ "owasp-password-strength-test": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/owasp-password-strength-test/-/owasp-password-strength-test-1.3.0.tgz",
+ "integrity": "sha1-T2KeQpA+j20nmyMNZXq2HljkSxI="
+ },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
diff --git a/backend/package.json b/backend/package.json
index e6b64b3..daba5f7 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -19,6 +19,7 @@
"js-sha512": "^0.8.0",
"knex": "^0.20.8",
"morgan": "^1.9.1",
+ "owasp-password-strength-test": "^1.3.0",
"pg": "^7.18.1",
"shortid": "^2.2.15"
},
diff --git a/backend/src/authn.js b/backend/src/authn.js
index c6c4a2e..40c7da8 100644
--- a/backend/src/authn.js
+++ b/backend/src/authn.js
@@ -2,6 +2,7 @@ const base64 = require('base-64');
const sha512 = require('js-sha512');
const shortid = require('shortid');
const db = require('./db');
+const owaspPw = require('owasp-password-strength-test');
const buildSessionCookie = ({ username, sessionToken, isLogout }) => {
if (!isLogout) {
@@ -78,6 +79,15 @@ const registerRoute = async (req, res) => {
});
}
+ const owaspPwTestResults = owaspPw.test(password);
+ if (!owaspPwTestResults.strong) {
+ res.status(400);
+ return res.json({
+ error: 'Password not strong enough',
+ errors: owaspPwTestResults.errors,
+ });
+ }
+
const salt = shortid.generate();
const pwHash = sha512(salt + password);
diff --git a/backend/src/index.js b/backend/src/index.js
index 128ed60..6b196f3 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -6,6 +6,17 @@ const routes = require('./routes');
const app = express();
+app.use((_, res, next) => {
+ try {
+ next();
+ } catch (err) {
+ console.error(err);
+ res.status(500);
+ res.json({
+ error: 'Internal server error',
+ });
+ }
+});
app.use(morgan('dev'));
app.use(bodyParser.json());
app.use(cookieParser());
diff --git a/frontend/src/Login.js b/frontend/src/Login.js
index 70fff91..a251216 100644
--- a/frontend/src/Login.js
+++ b/frontend/src/Login.js
@@ -28,7 +28,7 @@ const Login = () => {
return (
<form onSubmit={handleSubmit(onSubmit)}>
- <h1 class="title">Log in</h1>
+ <h1 className="title">Log in</h1>
<label className="label">
Username <input className="input" name="username" type="text" ref={register} required />
</label>
@@ -42,7 +42,6 @@ const Login = () => {
<div>
No account? Register <Link to="/register">here</Link>.
</div>
- {/* <div className="error">{errors}</div> */}
</form>
);
};
diff --git a/frontend/src/Register.js b/frontend/src/Register.js
index 6ba1537..6b04816 100644
--- a/frontend/src/Register.js
+++ b/frontend/src/Register.js
@@ -7,12 +7,11 @@ import userState from './UserState';
const Register = () => {
const { register, handleSubmit } = useForm();
- const [ loginError, setLoginError ] = useState(null);
+ const [ data, setData ] = useState({});
const onSubmit = async data => {
if (data.password !== data.confirm_password) {
- setLoginError('Passwords do not match.');
- return;
+ return setData({ errors: [ 'Passwords do not match.' ] });
}
try {
const resp = await axios.post('/register', data);
@@ -22,10 +21,12 @@ const Register = () => {
navigate('/');
} catch (err) {
if (err.response && err.response.data) {
- setLoginError(err.response.data.error);
+ setData({
+ errors: [ err.response.data.error, ...(err.response.data.errors || []) ],
+ });
} else {
console.error(err);
- setLoginError('Unknown error occurred. See console for details.');
+ setData({ errors: [ 'Unknown error occurred. See console for details.' ] });
}
}
};
@@ -45,7 +46,7 @@ const Register = () => {
<button className="button" type="submit">
Register
</button>
- <div className="error">{loginError}</div>
+ <ul className="error">{data.errors ? data.errors.map(e => <li>{e}</li>) : null}</ul>
{/* <div className="error">{errors}</div> */}
</form>
);
diff --git a/frontend/src/UserList.js b/frontend/src/UserList.js
index dd17ace..59c6425 100644
--- a/frontend/src/UserList.js
+++ b/frontend/src/UserList.js
@@ -1,6 +1,29 @@
import React, { useEffect, useState } from 'react';
import axios from './axios';
+const Table = ({ users }) => {
+ const userRows = !!users
+ ? users.map(user => (
+ <tr key={user.id}>
+ <td>{user.id}</td>
+ <td>{user.username}</td>
+ </tr>
+ ))
+ : null;
+
+ return (
+ <table className="table">
+ <thead>
+ <tr>
+ <th>ID</th>
+ <th>Username</th>
+ </tr>
+ </thead>
+ <tbody>{userRows}</tbody>
+ </table>
+ );
+};
+
const UserList = () => {
const [ data, setData ] = useState({});
useEffect(() => {
@@ -22,16 +45,6 @@ const UserList = () => {
})();
}, []);
- const users = data.users;
- const userRows = !!users
- ? users.map(user => (
- <tr key={user.id}>
- <td>{user.id}</td>
- <td>{user.username}</td>
- </tr>
- ))
- : null;
-
return (
<div>
<h2 className="subtitle">User list</h2>
@@ -39,15 +52,7 @@ const UserList = () => {
<div className="error" key="error">
{data.error}
</div>
- <table className="table">
- <thead>
- <tr>
- <th>ID</th>
- <th>Username</th>
- </tr>
- </thead>
- <tbody>{userRows}</tbody>
- </table>
+ {!data.error && <Table users={data.users} />}
</div>
</div>
);
diff --git a/frontend/src/axios.js b/frontend/src/axios.js
index f55b37f..2676326 100644
--- a/frontend/src/axios.js
+++ b/frontend/src/axios.js
@@ -3,6 +3,7 @@ import { navigate } from '@reach/router';
axios.defaults.baseURL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:4000';
axios.defaults.withCredentials = true;
+axios.defaults.timeout = 2000;
axios.interceptors.response.use(
response => {