add login hook to frontend
This commit is contained in:
parent
18d351d698
commit
a9a10c1adb
8 changed files with 93 additions and 22 deletions
|
@ -1,13 +1,77 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import './Login.scss';
|
import './Login.scss';
|
||||||
|
import authServices from '../../services/authServices';
|
||||||
|
import FormError from '../FormError/FormError';
|
||||||
|
|
||||||
const Login = (props) => {
|
const Login = (props) => {
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
const minimumPasswordLength = 8;
|
||||||
|
const errorDispatchAction = {
|
||||||
|
type: 'ERR',
|
||||||
|
message: 'AUTH_ERROR'
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateLoginForm = next => {
|
||||||
|
if (password.length < minimumPasswordLength) {
|
||||||
|
return props.dispatch({
|
||||||
|
...errorDispatchAction,
|
||||||
|
body: { authError: 'This password is invalid'}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[\w\d_.-]+$/.test(username)) {
|
||||||
|
return props.dispatch({
|
||||||
|
...errorDispatchAction,
|
||||||
|
body: { authError: "This username is invalid"}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const postLoginForm = async () => {
|
||||||
|
const loginResponse = await authServices.loginService({
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
})
|
||||||
|
const parsedResponse = JSON.parse(loginResponse);
|
||||||
|
if (parsedResponse.errors) {
|
||||||
|
const authError = parsedResponse.errors
|
||||||
|
return props.dispatch({
|
||||||
|
...errorDispatchAction,
|
||||||
|
body: { authError }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.dispatch({
|
||||||
|
type: 'AUTH',
|
||||||
|
message: 'LOGIN',
|
||||||
|
body: parsedResponse
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
validateLoginForm(postLoginForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
const formError = errors => {
|
||||||
|
if(!errors) return <></>;
|
||||||
|
|
||||||
|
if (errors.auth) {
|
||||||
|
return <FormError error={errors.auth}/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Login" data-testid="Login">
|
<div className="Login" data-testid="Login">
|
||||||
<form data-testid="Login__form">
|
{formError(props.state.errors)}
|
||||||
|
<form
|
||||||
|
data-testid="Login__form"
|
||||||
|
onSubmit={e => handleSubmit(e)}
|
||||||
|
>
|
||||||
|
|
||||||
<label htmlFor="username-input">Username:</label>
|
<label htmlFor="username-input">Username:</label>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import Login from './Login';
|
import Login from './Login';
|
||||||
|
import { initState } from '../../reducers/init/stateReducer.init';
|
||||||
|
|
||||||
test('renders Login without crashing', () => {
|
test('renders Login without crashing', () => {
|
||||||
const { getByTestId } = render(<Login />);
|
const state = initState();
|
||||||
|
const { getByTestId } = render(<Login state={state}/>);
|
||||||
const LoginDiv = getByTestId('Login');
|
const LoginDiv = getByTestId('Login');
|
||||||
expect(LoginDiv).toBeInTheDocument();
|
expect(LoginDiv).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
|
@ -70,7 +70,7 @@ const Signup = (props) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = async e => {
|
const handleSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
validateSignupForm(postSignupForm);
|
validateSignupForm(postSignupForm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ export const authReducer = (state: state, action: action):state => {
|
||||||
return loginReducer(state, action);
|
return loginReducer(state, action);
|
||||||
|
|
||||||
case 'SIGNUP':
|
case 'SIGNUP':
|
||||||
return signupReducer(state, action);
|
return loginReducer(state, action);
|
||||||
|
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
return state;
|
return state;
|
||||||
|
@ -18,13 +18,6 @@ export const authReducer = (state: state, action: action):state => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loginReducer(state: state, action: action): state {
|
function loginReducer(state: state, action: action): state {
|
||||||
const userCredentials = action.body;
|
|
||||||
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
function signupReducer(state: state, action: action): state {
|
|
||||||
const newUser = action.body;
|
const newUser = action.body;
|
||||||
return {...state, user: newUser };
|
return {...state, user: newUser };
|
||||||
}
|
}
|
|
@ -9,8 +9,19 @@ headers.append('Content-Type', 'application/json');
|
||||||
headers.append('Accept', 'application/json');
|
headers.append('Accept', 'application/json');
|
||||||
headers.append('Sec-Fetch-Site', 'cross-site')
|
headers.append('Sec-Fetch-Site', 'cross-site')
|
||||||
|
|
||||||
const loginService = () => {
|
const loginService = async(formData) => {
|
||||||
|
const response = await fetch(loginEndpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify(formData),
|
||||||
|
headers: headers
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
return res.text();
|
||||||
|
}).catch(err => {
|
||||||
|
return err;
|
||||||
|
});
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const signupService = async (formData) => {
|
const signupService = async (formData) => {
|
||||||
|
@ -20,7 +31,7 @@ const signupService = async (formData) => {
|
||||||
body: JSON.stringify(formData),
|
body: JSON.stringify(formData),
|
||||||
headers: headers
|
headers: headers
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
return res.text();
|
return res.text();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -16,7 +16,8 @@ describe('signupService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loginService', () => {
|
describe('loginService', () => {
|
||||||
it('', () => {
|
it('login is successful', async () => {
|
||||||
|
// const response = await loginService(newUserFormData);
|
||||||
|
// console.log(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -45,25 +45,25 @@ const login = async (req, res, next) => {
|
||||||
const savedUser = queryResults[0] || null;
|
const savedUser = queryResults[0] || null;
|
||||||
|
|
||||||
if (!savedUser) {
|
if (!savedUser) {
|
||||||
return res.status(401).json({err: 'bad credentials'});
|
return res.status(401).send({errors: 'bad credentials'});
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedPassword = savedUser.password;
|
const hashedPassword = savedUser.password;
|
||||||
const passwordMatch = await compareHash(user.password, hashedPassword);
|
const passwordMatch = await compareHash(user.password, hashedPassword);
|
||||||
|
|
||||||
if (!passwordMatch) {
|
if (!passwordMatch) {
|
||||||
return res.status(401).json({err: 'bad credentials'});
|
return res.status(401).send({errors: 'bad credentials'});
|
||||||
}
|
}
|
||||||
|
|
||||||
const authorizedUser = {...savedUser};
|
const authorizedUser = {...savedUser};
|
||||||
delete authorizedUser.password;
|
delete authorizedUser.password;
|
||||||
|
|
||||||
signToken(res, authorizedUser);
|
signToken(res, authorizedUser);
|
||||||
res.send('ok').status(200);
|
res.send({...authorizedUser}).status(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (err) {
|
catch (err) {
|
||||||
res.status(500).json(err);
|
res.status(500).send({errors: err});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ const authSignupSpec = (chai, knex, server) => {
|
||||||
.end((err, res) => {
|
.end((err, res) => {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
res.should.status(401);
|
res.should.status(401);
|
||||||
res.body.err.should.equal('bad credentials');
|
res.body.errors.should.equal('bad credentials');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -32,7 +32,7 @@ const authSignupSpec = (chai, knex, server) => {
|
||||||
.end((err, res) => {
|
.end((err, res) => {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
res.should.status(401);
|
res.should.status(401);
|
||||||
res.body.err.should.equal('bad credentials');
|
res.body.errors.should.equal('bad credentials');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue