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

View File

@@ -1,5 +1,8 @@
APP_PORT = APP_PORT =
DB_URI = DB_URI =
SECRET = SECRET =
GOOGLE_ENABLED =
GOOGLE_CLIENT_ID =
GOOGLE_CLIENT_SECRET =
HOST =

7
app.js
View File

@@ -74,6 +74,7 @@ app.use((error, req, res, next) => {
switch (error.name) { switch (error.name) {
case 'ValidationError': case 'ValidationError':
case 'MissingPasswordError': case 'MissingPasswordError':
case 'BadRequest':
case 'BadRequestError': case 'BadRequestError':
res.status(400); res.status(400);
res.json({ success: false, error }); res.json({ success: false, error });
@@ -91,6 +92,12 @@ app.use((error, req, res, next) => {
res.status(500); res.status(500);
res.json({ success: false, error }); res.json({ success: false, error });
} }
if (
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'test'
) {
console.error(error);
}
next(error); next(error);
}); });

View File

@@ -10,6 +10,12 @@ const production = {
process.env.MONGODB_URI || process.env.MONGODB_URI ||
'mongodb://localhost/todolist', 'mongodb://localhost/todolist',
}, },
googleOAuth: {
googleEnabled: process.env.GOOGLE_ENABLED,
googleClientId: process.env.GOOGLE_CLIENT_ID,
googleClientSecret: process.env.GOOGLE_CLIENT_SECRET,
googleCallback: `${process.env.HOST}/api/users/login/google/callback`,
},
secret: process.env.SECRET, secret: process.env.SECRET,
}; };

View File

@@ -1,8 +1,31 @@
const passport = require('passport'); const passport = require('passport');
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const {
googleClientId,
googleClientSecret,
googleCallback,
googleEnabled,
} = require('./').googleOAuth;
const User = mongoose.model('User'); const User = mongoose.model('User');
passport.use(User.createStrategy()); passport.use(User.createStrategy());
if (googleEnabled) {
passport.use(
new GoogleStrategy(
{
clientID: googleClientId,
clientSecret: googleClientSecret,
callbackURL: googleCallback,
},
(accessToken, refreshToken, profile, done) => {
User.findOrCreate({ googleId: profile.id }, (err, user) =>
done(err, user),
);
},
),
);
}
module.exports = passport; module.exports = passport;

View File

@@ -2,6 +2,8 @@ const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose'); const passportLocalMongoose = require('passport-local-mongoose');
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const uniqueValidator = require('mongoose-unique-validator'); const uniqueValidator = require('mongoose-unique-validator');
const findOrCreate = require('mongoose-findorcreate');
const { BadRequestError } = require('../errors');
const { secret } = require('../config'); const { secret } = require('../config');
@@ -10,13 +12,17 @@ const { Schema } = mongoose;
const UserSchema = Schema({ const UserSchema = Schema({
username: { username: {
type: String, type: String,
required: true,
unique: true, unique: true,
validate: /^\S*$/, validate: /^\S*$/,
minLength: 3, minLength: 3,
maxLength: 50, maxLength: 50,
trim: true, trim: true,
}, },
googleId: {
type: String,
unique: true,
sparse: true,
},
lists: [{ type: Schema.Types.ObjectId, ref: 'TodoList' }], lists: [{ type: Schema.Types.ObjectId, ref: 'TodoList' }],
todos: [{ type: Schema.Types.ObjectId, ref: 'Todo' }], todos: [{ type: Schema.Types.ObjectId, ref: 'Todo' }],
}); });
@@ -26,6 +32,13 @@ UserSchema.plugin(passportLocalMongoose, {
maxAttempts: 20, maxAttempts: 20,
}); });
UserSchema.plugin(uniqueValidator); UserSchema.plugin(uniqueValidator);
UserSchema.plugin(findOrCreate);
UserSchema.pre('validate', async function() {
if (!this.username && !this.googleId) {
throw new BadRequestError('username is required');
}
});
UserSchema.pre('remove', async function() { UserSchema.pre('remove', async function() {
await this.model('TodoList') await this.model('TodoList')

229
package-lock.json generated
View File

@@ -171,6 +171,15 @@
} }
} }
}, },
"agent-base": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
"dev": true,
"requires": {
"es6-promisify": "^5.0.0"
}
},
"ajv": { "ajv": {
"version": "5.5.2", "version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
@@ -1075,10 +1084,9 @@
} }
}, },
"bluebird": { "bluebird": {
"version": "3.5.1", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw="
"dev": true
}, },
"body-parser": { "body-parser": {
"version": "1.18.3", "version": "1.18.3",
@@ -2246,6 +2254,21 @@
"is-symbol": "^1.0.1" "is-symbol": "^1.0.1"
} }
}, },
"es6-promise": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
"integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
"dev": true
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"dev": true,
"requires": {
"es6-promise": "^4.0.3"
}
},
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -2409,9 +2432,9 @@
"dev": true "dev": true
}, },
"eslint-plugin-import": { "eslint-plugin-import": {
"version": "2.12.0", "version": "2.13.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.12.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz",
"integrity": "sha1-2tMXgSktZmSyUxf9BJ0uKy8CIF0=", "integrity": "sha512-t6hGKQDMIt9N8R7vLepsYXgDfeuhp6ZJSgtrLEDxonpSubyxUZHjhm6LsAaZX8q6GYVxkbT3kTsV9G5mBCFR6A==",
"dev": true, "dev": true,
"requires": { "requires": {
"contains-path": "^0.1.0", "contains-path": "^0.1.0",
@@ -2445,9 +2468,9 @@
"dev": true "dev": true
}, },
"eslint-plugin-prettier": { "eslint-plugin-prettier": {
"version": "2.6.1", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.2.tgz",
"integrity": "sha512-wNZ2z0oVCWnf+3BSI7roS+z4gGu2AwcPKUek+SlLZMZg+X0KbZLsB2knul7fd0K3iuIp402HIYzm4f2+OyfXxA==", "integrity": "sha512-tGek5clmW5swrAx1mdPYM8oThrBE83ePh7LeseZHBWfHVGrHPhKn7Y5zgRMbU/9D5Td9K4CEmUPjGxA7iw98Og==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-diff": "^1.1.1", "fast-diff": "^1.1.1",
@@ -3946,6 +3969,27 @@
"sshpk": "^1.7.0" "sshpk": "^1.7.0"
} }
}, },
"https-proxy-agent": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
"integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
"dev": true,
"requires": {
"agent-base": "^4.1.0",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.23", "version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
@@ -5519,26 +5563,27 @@
} }
}, },
"mongodb": { "mongodb": {
"version": "3.0.10", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.10.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.0.tgz",
"integrity": "sha512-jy9s4FgcM4rl8sHNETYHGeWcuRh9AlwQCUuMiTj041t/HD02HwyFgmm2VZdd9/mA9YNHaUJLqj0tzBx2QFivtg==", "integrity": "sha512-fSDZRq9FomRqeDSM7MpMTLa8sz+STs3nZ7Ib0+xvmaKZ6nquNDN4zGDsVhjto6UozFvHMDYJMAfJwhqUygXs9g==",
"requires": { "requires": {
"mongodb-core": "3.0.9" "mongodb-core": "3.1.0"
} }
}, },
"mongodb-core": { "mongodb-core": {
"version": "3.0.9", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.9.tgz", "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.0.tgz",
"integrity": "sha512-buOWjdLLBlEqjHDeHYSXqXx173wHMVp7bafhdHxSjxWdB9V6Ri4myTqxjYZwL/eGFZxvd8oRQSuhwuIDbaaB+g==", "integrity": "sha512-qRjG62Fu//CZhkgn0jA/k8jh5MhACIq8cOJUryH6sck87pgt+C222MSD02tsCq5zNo/B6ZFHtNodZ2qpf8E86g==",
"requires": { "requires": {
"bson": "~1.0.4", "bson": "~1.0.4",
"require_optional": "^1.0.1" "require_optional": "^1.0.1",
"saslprep": "^1.0.0"
} }
}, },
"mongodb-memory-server": { "mongodb-memory-server": {
"version": "1.8.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-1.8.0.tgz", "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-1.9.0.tgz",
"integrity": "sha512-3Pzgv7UruHu99aJ4yVPT4xmd1cNt8QBXZvYbB3Vs4IvPcA0h1wT8+I3zguwqQPIHpeqFXafVfjIiUfXiax3bjA==", "integrity": "sha512-sVAOe68NoQwgORI9YP/FkOZi7YNX5efD452FKS/WPBXlIPqt/baoHWzP+GnmHHqyMrUEdPJdTmcOZVOoVs/aGw==",
"dev": true, "dev": true,
"requires": { "requires": {
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
@@ -5547,11 +5592,10 @@
"fs-extra": "^6.0.1", "fs-extra": "^6.0.1",
"get-port": "^3.2.0", "get-port": "^3.2.0",
"getos": "^3.1.0", "getos": "^3.1.0",
"https-proxy-agent": "^2.2.1",
"lockfile": "^1.0.4", "lockfile": "^1.0.4",
"md5-file": "^4.0.0", "md5-file": "^4.0.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"request": "^2.87.0",
"request-promise": "^4.2.2",
"tmp": "^0.0.33", "tmp": "^0.0.33",
"uuid": "^3.2.1" "uuid": "^3.2.1"
}, },
@@ -5564,47 +5608,20 @@
"requires": { "requires": {
"ms": "2.0.0" "ms": "2.0.0"
} }
},
"request": {
"version": "2.87.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.6.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.5",
"extend": "~3.0.1",
"forever-agent": "~0.6.1",
"form-data": "~2.3.1",
"har-validator": "~5.0.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.17",
"oauth-sign": "~0.8.2",
"performance-now": "^2.1.0",
"qs": "~6.5.1",
"safe-buffer": "^5.1.1",
"tough-cookie": "~2.3.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.1.0"
}
} }
} }
}, },
"mongoose": { "mongoose": {
"version": "5.1.6", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.1.6.tgz", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.2.1.tgz",
"integrity": "sha512-p8p/3Z2kfXViqawN1TV+cZ8XbHz6SsllkytKTog+CDWfCNObyGraHQlUuRv/9aYPNKiZfq6WWITgLpJLZW/o/A==", "integrity": "sha512-WB5F/T2B3W7p2+uftd3WkIrNLhg8VzSbxxtFGbTIqsZ4KCOhjhYALN0ltZPLaBlIrLtEoGFKTNwyWEcOtxY+oA==",
"requires": { "requires": {
"async": "2.6.1", "async": "2.6.1",
"bson": "~1.0.5", "bson": "~1.0.5",
"kareem": "2.2.1", "kareem": "2.2.1",
"lodash.get": "4.4.2", "lodash.get": "4.4.2",
"mongodb": "3.0.10", "mongodb": "3.1.0",
"mongodb-core": "3.1.0",
"mongoose-legacy-pluralize": "1.0.2", "mongoose-legacy-pluralize": "1.0.2",
"mpath": "0.4.1", "mpath": "0.4.1",
"mquery": "3.0.0", "mquery": "3.0.0",
@@ -5623,6 +5640,11 @@
} }
} }
}, },
"mongoose-findorcreate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mongoose-findorcreate/-/mongoose-findorcreate-3.0.0.tgz",
"integrity": "sha512-kQhDe5XDj6tMv8kq1wjK+hITGIGUl60rj8oGLupF9poNsqIDkAJBXudZKcCdSyBZ7p6DLK2+0jSBthrb26tSYQ=="
},
"mongoose-legacy-pluralize": { "mongoose-legacy-pluralize": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
@@ -5665,11 +5687,6 @@
"sliced": "0.0.5" "sliced": "0.0.5"
}, },
"dependencies": { "dependencies": {
"bluebird": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
"integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw="
},
"sliced": { "sliced": {
"version": "0.0.5", "version": "0.0.5",
"resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz",
@@ -5859,6 +5876,11 @@
"integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==",
"dev": true "dev": true
}, },
"oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
},
"oauth-sign": { "oauth-sign": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
@@ -6127,6 +6149,31 @@
"pause": "0.0.1" "pause": "0.0.1"
} }
}, },
"passport-google-oauth": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-google-oauth/-/passport-google-oauth-1.0.0.tgz",
"integrity": "sha1-ZfUGMxkq0GJ6GLCJYAdxCdhOt20=",
"requires": {
"passport-google-oauth1": "1.x.x",
"passport-google-oauth20": "1.x.x"
}
},
"passport-google-oauth1": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz",
"integrity": "sha1-r3SoA99R7GRvZqRNgigr5vEI4Mw=",
"requires": {
"passport-oauth1": "1.x.x"
}
},
"passport-google-oauth20": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-1.0.0.tgz",
"integrity": "sha1-O5YOih1w0dvnlGFcgnxoxAOSpdA=",
"requires": {
"passport-oauth2": "1.x.x"
}
},
"passport-local": { "passport-local": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
@@ -6157,6 +6204,27 @@
} }
} }
}, },
"passport-oauth1": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz",
"integrity": "sha1-p96YiiEfnPRoc3cTDqdN8ycwyRg=",
"requires": {
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"utils-merge": "1.x.x"
}
},
"passport-oauth2": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz",
"integrity": "sha1-9i+BWDy+EmCb585vFguTlaJ7hq0=",
"requires": {
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x",
"utils-merge": "1.x.x"
}
},
"passport-strategy": { "passport-strategy": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
@@ -6690,18 +6758,6 @@
"uuid": "^3.1.0" "uuid": "^3.1.0"
} }
}, },
"request-promise": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
"integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
"dev": true,
"requires": {
"bluebird": "^3.5.0",
"request-promise-core": "1.1.1",
"stealthy-require": "^1.1.0",
"tough-cookie": ">=2.3.3"
}
},
"request-promise-core": { "request-promise-core": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
@@ -6768,9 +6824,9 @@
} }
}, },
"resolve": { "resolve": {
"version": "1.7.1", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
"integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
"dev": true, "dev": true,
"requires": { "requires": {
"path-parse": "^1.0.5" "path-parse": "^1.0.5"
@@ -7187,6 +7243,12 @@
} }
} }
}, },
"saslprep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.0.tgz",
"integrity": "sha512-5lvKUEQ7lAN5/vPl5d3k8FQeDbEamu9kizfATfLLWV5h6Mkh1xcieR1FSsJkcSRUk49lF2tAW8gzXWVwtwZVhw==",
"optional": true
},
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@@ -8354,6 +8416,11 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"uid2": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
"integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
},
"unbzip2-stream": { "unbzip2-stream": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz",
@@ -8418,9 +8485,9 @@
} }
}, },
"universalify": { "universalify": {
"version": "0.1.1", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true "dev": true
}, },
"unpipe": { "unpipe": {
@@ -8894,9 +8961,9 @@
} }
}, },
"yauzl": { "yauzl": {
"version": "2.9.2", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.2.tgz", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-T7G8euH8L1cDe1SvasyP4QMcW3c=", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"dev": true, "dev": true,
"requires": { "requires": {
"buffer-crc32": "~0.2.3", "buffer-crc32": "~0.2.3",

View File

@@ -28,10 +28,12 @@
"express-jwt": "^5.3.1", "express-jwt": "^5.3.1",
"hsts": "^2.1.0", "hsts": "^2.1.0",
"jsonwebtoken": "^8.3.0", "jsonwebtoken": "^8.3.0",
"mongoose": "^5.1.6", "mongoose": "^5.2.1",
"mongoose-findorcreate": "^3.0.0",
"mongoose-unique-validator": "^2.0.1", "mongoose-unique-validator": "^2.0.1",
"morgan": "^1.9.0", "morgan": "^1.9.0",
"passport": "^0.4.0", "passport": "^0.4.0",
"passport-google-oauth": "^1.0.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"passport-local-mongoose": "^5.0.1" "passport-local-mongoose": "^5.0.1"
}, },
@@ -41,16 +43,19 @@
"eslint-config-airbnb-base": "^12.1.0", "eslint-config-airbnb-base": "^12.1.0",
"eslint-config-node": "^2.0.0", "eslint-config-node": "^2.0.0",
"eslint-config-prettier": "^2.9.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-jest": "^21.17.0",
"eslint-plugin-prettier": "^2.6.1", "eslint-plugin-prettier": "^2.6.2",
"jest": "^22.4.4", "jest": "^22.4.4",
"mongodb-memory-server": "^1.8.0", "mongodb-memory-server": "^1.9.0",
"nodemon": "^1.17.5", "nodemon": "^1.17.5",
"prettier-eslint": "^8.8.2", "prettier-eslint": "^8.8.2",
"supertest": "^3.1.0" "supertest": "^3.1.0"
}, },
"jest": { "jest": {
"testEnvironment": "node" "testEnvironment": "node",
"roots": [
"<rootDir>/tests/"
]
} }
} }

157
react/package-lock.json generated
View File

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

View File

@@ -3,9 +3,9 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^1.2.3", "@material-ui/core": "^1.3.1",
"@material-ui/icons": "^1.1.0", "@material-ui/icons": "^1.1.0",
"@redux-offline/redux-offline": "^2.3.3", "@redux-offline/redux-offline": "^2.4.0",
"localforage": "^1.7.2", "localforage": "^1.7.2",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.4.1", "react": "^16.4.1",
@@ -14,7 +14,7 @@
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-router-redux": "^4.0.8", "react-router-redux": "^4.0.8",
"react-scripts": "1.1.4", "react-scripts": "1.1.4",
"react-spring": "^5.3.18", "react-spring": "^5.4.0",
"redux": "^4.0.0", "redux": "^4.0.0",
"redux-form": "^7.4.2", "redux-form": "^7.4.2",
"redux-thunk": "^2.3.0" "redux-thunk": "^2.3.0"
@@ -25,14 +25,19 @@
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"proxy": "http://localhost:4000", "proxy": {
"/api": {
"target": "http://localhost:4000",
"ws": true
}
},
"devDependencies": { "devDependencies": {
"eslint-config-airbnb": "^16.1.0", "eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.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-jest": "^21.17.0",
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-jsx-a11y": "^6.1.0",
"eslint-plugin-react": "^7.9.1", "eslint-plugin-react": "^7.10.0",
"prettier-eslint": "^8.8.2" "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) { function signupSuccess(user) {
return { type: SIGNUP_SUCCESS, user }; return { type: SIGNUP_SUCCESS, user };
} }

View File

@@ -16,8 +16,18 @@ form {
color: red; color: red;
} }
#googlebutton {
margin: auto;
margin-left: 0rem;
}
#submitbutton { #submitbutton {
margin: auto; margin: auto;
margin-top: 1rem; margin-right: 0rem;
margin-right: 0.5rem; }
#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 { export default class Todos extends React.Component {
componentDidUpdate() { componentDidUpdate() {
if (!this.props.user.user && !this.props.user.dirty) { if (!this.props.user.user && !this.props.user.dirty) {
this.props.history.push('/login'); this.props.history.replace('/login');
} }
} }
render() { render() {

View File

@@ -11,7 +11,13 @@ export default function InputField({
}) { }) {
return ( return (
<React.Fragment> <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>} {touched && error && <span className="error">{error}</span>}
</React.Fragment> </React.Fragment>
); );

View File

@@ -10,55 +10,83 @@ import UserErrors from './UserErrors';
import '../Form.css'; import '../Form.css';
import { login, reset } from '../../actions/user'; import { login, reset, loginJWT } from '../../actions/user';
function LoginForm({ handleSubmit, onLogin, user, history, resetUser }) { class LoginForm extends React.Component {
if (user.user) { componentDidMount() {
history.push('/'); 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 = { LoginForm.propTypes = {
@@ -67,6 +95,7 @@ LoginForm.propTypes = {
user: PropTypes.object.isRequired, user: PropTypes.object.isRequired,
history: PropTypes.any.isRequired, history: PropTypes.any.isRequired,
resetUser: PropTypes.func.isRequired, resetUser: PropTypes.func.isRequired,
setJWT: PropTypes.func.isRequired,
}; };
function mapStateToProps(state) { function mapStateToProps(state) {
@@ -80,6 +109,7 @@ function mapDispatchToProps(dispatch) {
resetUser: () => dispatch(reset()), resetUser: () => dispatch(reset()),
onLogin: ({ username, password }) => onLogin: ({ username, password }) =>
dispatch(login({ 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} component={InputField}
type="password" type="password"
/> />
<div id="submitbutton"> <div id="buttons">
<Button variant="raised" color="primary" type="submit"> <Button
id="submitbutton"
variant="raised"
color="primary"
type="submit"
>
Signup Signup
</Button> </Button>
</div> </div>

23
routes/google.js Normal file
View File

@@ -0,0 +1,23 @@
const express = require('express');
const passport = require('passport');
const router = express.Router();
const asyncHelper = require('../asyncHelper');
router.get(
'/google',
passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/plus.login'],
}),
);
router.get(
'/google/callback',
passport.authenticate('google', { session: false, failWithError: true }),
asyncHelper(async (req, res) => {
res.redirect(`/login?jwt=${req.user.generateJwt()}`);
}),
);
module.exports = router;

View File

@@ -8,6 +8,8 @@ const router = express.Router();
const asyncHelper = require('../asyncHelper'); const asyncHelper = require('../asyncHelper');
const auth = require('./auth'); const auth = require('./auth');
const { googleEnabled } = require('../config').googleOAuth;
const googleOAuth = require('./google');
const { NotFoundError } = require('../errors'); const { NotFoundError } = require('../errors');
@@ -69,6 +71,9 @@ router.delete(
}), }),
); );
if (googleEnabled) {
router.use('/login', googleOAuth);
}
router.post( router.post(
'/login', '/login',
passport.authenticate('local', { session: false, failWithError: true }), passport.authenticate('local', { session: false, failWithError: true }),