auth using google

This commit is contained in:
2018-07-07 18:26:59 +03:00
parent aa6b0fb7af
commit bd957c89ef
17 changed files with 468 additions and 216 deletions

157
react/package-lock.json generated
View File

@@ -5,25 +5,30 @@
"requires": true,
"dependencies": {
"@babel/runtime": {
"version": "7.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.51.tgz",
"integrity": "sha1-SLjtGDBwNMZiD2Q1FGUMoszAFlo=",
"version": "7.0.0-beta.52",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.52.tgz",
"integrity": "sha1-PztCuCuStOGig/x43xuy/Uuo0Mc=",
"requires": {
"core-js": "^2.5.7",
"regenerator-runtime": "^0.11.1"
"regenerator-runtime": "^0.12.0"
},
"dependencies": {
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
},
"regenerator-runtime": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.0.tgz",
"integrity": "sha512-SpV2LhF5Dm9UYMEprB3WwsBnWwqTrmjrm2UZb42cl2G02WVGgx7Mg8aa9pdLEKp6hZ+/abcMc2NxKA8f02EG2w=="
}
}
},
"@material-ui/core": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-1.2.3.tgz",
"integrity": "sha512-5Z4LhIrFJcvp1a7E8C3DPxL4W0RkjxWO9OwqOlRsr8YCF2sJgqCMDWn8DMW9eg1VD50JnZQ8bmx1esE0GBo71Q==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-1.3.1.tgz",
"integrity": "sha512-h5pVkHgYrKExTdll4Y2Kmvkd5Hr4MxqEQLhRxzGTaXJ8RjOuRd+plfRk5r5ZauAdrIkKEsNcEt75VlEFX9aSGw==",
"requires": {
"@babel/runtime": "^7.0.0-beta.42",
"@types/jss": "^9.5.3",
@@ -49,7 +54,7 @@
"react-jss": "^8.1.0",
"react-popper": "^0.10.0",
"react-transition-group": "^2.2.1",
"recompose": "^0.26.0 || ^0.27.0",
"recompose": "^0.27.0",
"scroll": "^2.0.3",
"warning": "^4.0.1"
}
@@ -63,9 +68,9 @@
}
},
"@redux-offline/redux-offline": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@redux-offline/redux-offline/-/redux-offline-2.3.3.tgz",
"integrity": "sha512-uv0DW9ZAFzL+lc7WzjQoDoWydReJiZe+Rpz6suGCS5ux+ZJWkr3jpRzeWlTW149uo+OrEk1BchNAE7FCTakXjQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@redux-offline/redux-offline/-/redux-offline-2.4.0.tgz",
"integrity": "sha512-idNVqcRax5bxHMu6nF6Lrxe6UfDU+Ha4TdJEJgysq0A1vvLbh6HFrRU02T+wBKA6j/L+kOZwzlDmnjYuyI1a6w==",
"requires": {
"babel-runtime": "^6.26.0",
"redux-persist": "^4.5.0"
@@ -81,9 +86,9 @@
}
},
"@types/react": {
"version": "16.4.1",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.1.tgz",
"integrity": "sha512-uZP8Fd4f7rwHKztnOhFJYEJsKXO7opmcyKk5P9vRC8UJAx3AiWaGFiLxDqPJqzO3n3IhF/v6rdscxadarEXnag==",
"version": "16.4.6",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.6.tgz",
"integrity": "sha512-9LDZdhsuKSc+DjY65SjBkA958oBWcTWSVWAd2cD9XqKBjhGw1KzAkRhWRw2eIsXvaIE/TOTjjKMFVC+JA1iU4g==",
"requires": {
"csstype": "^2.2.0"
}
@@ -3364,9 +3369,9 @@
}
},
"eslint-plugin-import": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.12.0.tgz",
"integrity": "sha1-2tMXgSktZmSyUxf9BJ0uKy8CIF0=",
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz",
"integrity": "sha512-t6hGKQDMIt9N8R7vLepsYXgDfeuhp6ZJSgtrLEDxonpSubyxUZHjhm6LsAaZX8q6GYVxkbT3kTsV9G5mBCFR6A==",
"dev": true,
"requires": {
"contains-path": "^0.1.0",
@@ -3448,30 +3453,61 @@
"dev": true
},
"eslint-plugin-jsx-a11y": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.3.tgz",
"integrity": "sha1-VFg9GuRCSDFi4EDhPMMYZUZRAOU=",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.1.0.tgz",
"integrity": "sha512-hnhf28u7Z9zlh7Y56tETrwnPeBvXgcqlP7ntHvZsWQs/n/p/vPnfNMNFWTqJAFcbd8PrDEifX1NRGHsjnUmqMw==",
"dev": true,
"requires": {
"aria-query": "^0.7.0",
"aria-query": "^3.0.0",
"array-includes": "^3.0.3",
"ast-types-flow": "0.0.7",
"axobject-query": "^0.1.0",
"damerau-levenshtein": "^1.0.0",
"emoji-regex": "^6.1.0",
"jsx-ast-utils": "^2.0.0"
"ast-types-flow": "^0.0.7",
"axobject-query": "^2.0.1",
"damerau-levenshtein": "^1.0.4",
"emoji-regex": "^6.5.1",
"has": "^1.0.3",
"jsx-ast-utils": "^2.0.1"
},
"dependencies": {
"aria-query": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
"integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
"dev": true,
"requires": {
"ast-types-flow": "0.0.7",
"commander": "^2.11.0"
}
},
"axobject-query": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.1.tgz",
"integrity": "sha1-Bd+nBa2orZ25k/polvItOVsLCgc=",
"dev": true,
"requires": {
"ast-types-flow": "0.0.7"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
}
}
},
"eslint-plugin-react": {
"version": "7.9.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.9.1.tgz",
"integrity": "sha512-uvq+2ZkiqzjwF+pMZ8xqIC3pChV4KviPvvPIyQOvKWnjtvyW3iGfHIRqVumw05L3itby0QGmA4VdBA9m1OdMmg==",
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.10.0.tgz",
"integrity": "sha512-18rzWn4AtbSUxFKKM7aCVcj5LXOhOKdwBino3KKWy4psxfPW0YtIbE8WNRDUdyHFL50BeLb6qFd4vpvNYyp7hw==",
"dev": true,
"requires": {
"doctrine": "^2.1.0",
"has": "^1.0.2",
"has": "^1.0.3",
"jsx-ast-utils": "^2.0.1",
"prop-types": "^15.6.1"
"prop-types": "^15.6.2"
},
"dependencies": {
"has": {
@@ -4146,13 +4182,11 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"optional": true
"bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -4165,18 +4199,15 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@@ -4279,8 +4310,7 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@@ -4290,7 +4320,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -4303,20 +4332,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -4333,7 +4359,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -4406,8 +4431,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@@ -4417,7 +4441,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -4523,7 +4546,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -9424,9 +9446,9 @@
}
},
"react-jss": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/react-jss/-/react-jss-8.5.1.tgz",
"integrity": "sha512-5R3qCdGkE+K0+B4tuRyx8idLV7q2pT1QbGomGqberCQ/xLKEQbDukH7ER2QLkpIYqtRkeciG9S03uDJwC1o2gw==",
"version": "8.6.1",
"resolved": "https://registry.npmjs.org/react-jss/-/react-jss-8.6.1.tgz",
"integrity": "sha512-SH6XrJDJkAphp602J14JTy3puB2Zxz1FkM3bKVE8wON+va99jnUTKWnzGECb3NfIn9JPR5vHykge7K3/A747xQ==",
"requires": {
"hoist-non-react-statics": "^2.5.0",
"jss": "^9.7.0",
@@ -9673,19 +9695,19 @@
}
},
"react-spring": {
"version": "5.3.18",
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-5.3.18.tgz",
"integrity": "sha512-gPLxo0wk1OYK1b3ZL9emWIAoWuQvTjuLDT8+yKXG+PTyEOYHvhaqEWShykL4bN6AUjPJyY4+7CLioHxJeAefZA==",
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/react-spring/-/react-spring-5.4.0.tgz",
"integrity": "sha512-Ws5+L4x/M9C1yVR6BNzf96er8yO/7euGS1Uv11fMoVDdS6/SgX0JeNszJo7o6maG+v7VxoIbvhyG3wVW8wQZFQ==",
"requires": {
"@babel/runtime": "7.0.0-beta.49"
"@babel/runtime": "7.0.0-beta.51"
},
"dependencies": {
"@babel/runtime": {
"version": "7.0.0-beta.49",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.49.tgz",
"integrity": "sha1-A7O/B+uYIHLI6FHdLd1RECguYb8=",
"version": "7.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.51.tgz",
"integrity": "sha1-SLjtGDBwNMZiD2Q1FGUMoszAFlo=",
"requires": {
"core-js": "^2.5.6",
"core-js": "^2.5.7",
"regenerator-runtime": "^0.11.1"
}
},
@@ -9697,13 +9719,14 @@
}
},
"react-transition-group": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.3.1.tgz",
"integrity": "sha512-hu4/LAOFSKjWt1+1hgnOv3ldxmt6lvZGTWz4KUkFrqzXrNDIVSu6txIcPszw7PNduR8en9YTN55JLRyd/L1ZiQ==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz",
"integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==",
"requires": {
"dom-helpers": "^3.3.1",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.1"
"prop-types": "^15.6.2",
"react-lifecycles-compat": "^3.0.4"
}
},
"read-pkg": {

View File

@@ -3,9 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^1.2.3",
"@material-ui/core": "^1.3.1",
"@material-ui/icons": "^1.1.0",
"@redux-offline/redux-offline": "^2.3.3",
"@redux-offline/redux-offline": "^2.4.0",
"localforage": "^1.7.2",
"prop-types": "^15.6.2",
"react": "^16.4.1",
@@ -14,7 +14,7 @@
"react-router-dom": "^4.3.1",
"react-router-redux": "^4.0.8",
"react-scripts": "1.1.4",
"react-spring": "^5.3.18",
"react-spring": "^5.4.0",
"redux": "^4.0.0",
"redux-form": "^7.4.2",
"redux-thunk": "^2.3.0"
@@ -25,14 +25,19 @@
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:4000",
"proxy": {
"/api": {
"target": "http://localhost:4000",
"ws": true
}
},
"devDependencies": {
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jest": "^21.17.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.9.1",
"eslint-plugin-jsx-a11y": "^6.1.0",
"eslint-plugin-react": "^7.10.0",
"prettier-eslint": "^8.8.2"
}
}

View File

@@ -71,6 +71,27 @@ export function login(user) {
};
}
export function loginJWT(jwt) {
return async dispatch => {
dispatch(startLogin());
const response = await fetch(`${API_ROOT}/users/user`, {
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${jwt}`,
},
method: 'GET',
});
const json = await response.json();
if (json.success) {
setToken(jwt);
dispatch(loginSuccess(json.data));
dispatch(fetchLists());
} else {
dispatch(loginFail(json.error));
}
};
}
function signupSuccess(user) {
return { type: SIGNUP_SUCCESS, user };
}

View File

@@ -16,8 +16,18 @@ form {
color: red;
}
#googlebutton {
margin: auto;
margin-left: 0rem;
}
#submitbutton {
margin: auto;
margin-top: 1rem;
margin-right: 0.5rem;
margin-right: 0rem;
}
#buttons {
margin-top: 1rem;
display: flex;
justify-content: space-around;
}

View File

@@ -8,7 +8,7 @@ import Header from './Header';
export default class Todos extends React.Component {
componentDidUpdate() {
if (!this.props.user.user && !this.props.user.dirty) {
this.props.history.push('/login');
this.props.history.replace('/login');
}
}
render() {

View File

@@ -11,7 +11,13 @@ export default function InputField({
}) {
return (
<React.Fragment>
<TextField label={label} required={required} {...input} type={type} />
<TextField
label={label}
required={required}
{...input}
type={type}
style={{ marginBottom: '1rem' }}
/>
{touched && error && <span className="error">{error}</span>}
</React.Fragment>
);

View File

@@ -10,55 +10,83 @@ import UserErrors from './UserErrors';
import '../Form.css';
import { login, reset } from '../../actions/user';
import { login, reset, loginJWT } from '../../actions/user';
function LoginForm({ handleSubmit, onLogin, user, history, resetUser }) {
if (user.user) {
history.push('/');
class LoginForm extends React.Component {
componentDidMount() {
const params = new URLSearchParams(new URL(window.location).search);
if (params.has('jwt')) {
const jwt = params.get('jwt');
this.props.setJWT(jwt);
}
}
componentDidUpdate() {
if (this.props.user.user) {
this.props.history.push('/');
}
}
render() {
return (
<React.Fragment>
<div id="user-header">
<ButtonBase
style={{
marginRight: '1rem',
padding: '0 0.5rem',
borderRadius: '7px',
}}
onClick={() => {
this.props.resetUser();
this.props.history.push('/signup');
}}
>
signup
</ButtonBase>
</div>
<div id="form">
<form onSubmit={this.props.handleSubmit(this.props.onLogin)}>
<UserErrors user={this.props.user} />
<Field
label="username"
name="username"
required
component={InputField}
type="text"
/>
<Field
label="password"
name="password"
required
component={InputField}
type="password"
/>
<div id="buttons">
<Button
id="googlebutton"
variant="raised"
onClick={() => {
window.location = '/api/users/login/google/';
}}
>
Google
</Button>
<Button
id="submitbutton"
variant="raised"
color="primary"
type="submit"
>
Login
</Button>
</div>
</form>
</div>
</React.Fragment>
);
}
return (
<React.Fragment>
<div id="user-header">
<ButtonBase
style={{
marginRight: '1rem',
padding: '0 0.5rem',
borderRadius: '7px',
}}
onClick={() => {
resetUser();
history.push('/signup');
}}
>
signup
</ButtonBase>
</div>
<div id="form">
<form onSubmit={handleSubmit(onLogin)}>
<UserErrors user={user} />
<Field
label="username"
name="username"
required
component={InputField}
type="text"
/>
<Field
label="password"
name="password"
required
component={InputField}
type="password"
/>
<div id="submitbutton">
<Button variant="raised" color="primary" type="submit">
Login
</Button>
</div>
</form>
</div>
</React.Fragment>
);
}
LoginForm.propTypes = {
@@ -67,6 +95,7 @@ LoginForm.propTypes = {
user: PropTypes.object.isRequired,
history: PropTypes.any.isRequired,
resetUser: PropTypes.func.isRequired,
setJWT: PropTypes.func.isRequired,
};
function mapStateToProps(state) {
@@ -80,6 +109,7 @@ function mapDispatchToProps(dispatch) {
resetUser: () => dispatch(reset()),
onLogin: ({ username, password }) =>
dispatch(login({ username, password })),
setJWT: jwt => dispatch(loginJWT(jwt)),
};
}

View File

@@ -65,8 +65,13 @@ function SignupForm({ handleSubmit, onSignup, user, history, resetUser }) {
component={InputField}
type="password"
/>
<div id="submitbutton">
<Button variant="raised" color="primary" type="submit">
<div id="buttons">
<Button
id="submitbutton"
variant="raised"
color="primary"
type="submit"
>
Signup
</Button>
</div>