diff --git a/client/package.json b/client/package.json
index a7340d9..965b1d5 100644
--- a/client/package.json
+++ b/client/package.json
@@ -2,7 +2,10 @@
"name": "client",
"version": "0.1.0",
"private": true,
+ "proxy": "http://localhost:3001",
"devDependencies": {
+ "eslint-plugin-babel": "^4.1.0",
+ "eslint-plugin-react": "^6.10.0",
"react-scripts": "0.9.3"
},
"dependencies": {
diff --git a/client/src/App.js b/client/src/App.js
index d03f4a8..f4760f0 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -1,5 +1,5 @@
import React, { Component } from 'react';
-import { BrowserRouter, Route } from 'react-router-dom';
+import { BrowserRouter, Route, Switch } from 'react-router-dom';
import './App.css';
import SignUpSignIn from './SignUpSignIn';
import TopNavbar from './TopNavbar';
@@ -7,7 +7,102 @@ import Secret from './Secret';
import axios from 'axios';
class App extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ signUpSignInError: '',
+ authenicated: localStorage.getItem('token') || false
+ };
+ }
+
+ handleSignUp(credentials) {
+ const { username, password, confirmPassword } = credentials;
+
+ if (!username.trim() || !password.trim() || password.trim() !== confirmPassword.trim()) {
+ this.setState({
+ signUpSignInError: 'Must provide all fields'
+ });
+ } else {
+ axios.post('/api/signup', credentials)
+ .then(resp => {
+ const { token } = resp.data;
+ // palces token
+ localStorage.setItem('token', token);
+
+ this.setState({
+ signUpSignInError: '',
+ authenicated: token
+ });
+ });
+ }
+ }
+
+ handleSignIn() {
+ /* const { username, password } = credentials;
+
+ if (!username.trim() || !password.trim() ) {
+ this.setState({
+ signUpSignInError: 'Must provide all fields'
+ });
+ } else {
+ axios.get('/api/signin', credentials)
+ .then(resp => {
+ const { token } = resp.data;
+ // places token
+ localStorage.setItem('token', token);
+
+ this.setState({
+ signUpSignInError: '',
+ authenicated: token
+ });
+ });
+ } */
+ }
+
+ handleSignOut() {
+ // removes token
+ localStorage.removeItem('token');
+
+ this.setState({
+ authenicated: false
+ });
+ }
+
+ renderSignUpSignIn() {
+ return (
+
+ );
+ }
+
+ renderApp() {
+ return (
+
+
+ I am protected!
} />
+
+ NOT FOUND!
} />
+
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
+ {this.state.authenicated ? this.renderApp() : this.renderSignUpSignIn()}
+
+
+ );
+ }
}
export default App;
diff --git a/client/src/SignUp.js b/client/src/SignUp.js
index 7b533c2..46cbd6f 100644
--- a/client/src/SignUp.js
+++ b/client/src/SignUp.js
@@ -9,7 +9,7 @@ class SignUp extends Component {
username: '',
password: '',
confirmPassword: '',
- }
+ };
}
handleSubmit(event) {
diff --git a/client/src/SignUpSignIn.js b/client/src/SignUpSignIn.js
index 8bbfc4f..6cac960 100644
--- a/client/src/SignUpSignIn.js
+++ b/client/src/SignUpSignIn.js
@@ -19,7 +19,7 @@ class SignUpSignIn extends Component {
{this.props.error && this.renderError()}
-
+
Sign In
@@ -27,7 +27,7 @@ class SignUpSignIn extends Component {
- )
+ );
}
}
diff --git a/client/src/TopNavbar.js b/client/src/TopNavbar.js
index e5d36b9..6b5b85d 100644
--- a/client/src/TopNavbar.js
+++ b/client/src/TopNavbar.js
@@ -1,6 +1,6 @@
import React, { PropTypes } from 'react';
import { Navbar, Nav, NavItem } from 'react-bootstrap';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
const TopNavbar = (props) => {
return (
@@ -13,6 +13,7 @@ const TopNavbar = (props) => {
{
props.showNavItems ?
+ (
- : null
+ ) : null
}
);
-}
+};
TopNavbar.propTypes = {
onSignOut: PropTypes.func.isRequired,
diff --git a/client/yarn.lock b/client/yarn.lock
index 1860cc5..4d7b4ff 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -146,6 +146,13 @@ array-unique@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+array.prototype.find@^2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.3.tgz#08c3ec33e32ec4bab362a2958e686ae92f59271d"
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.7.0"
+
arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@@ -1577,6 +1584,13 @@ default-require-extensions@^1.0.0:
dependencies:
strip-bom "^2.0.0"
+define-properties@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
+ dependencies:
+ foreach "^2.0.5"
+ object-keys "^1.0.8"
+
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
@@ -1751,6 +1765,23 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
+es-abstract@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c"
+ dependencies:
+ es-to-primitive "^1.1.1"
+ function-bind "^1.1.0"
+ is-callable "^1.1.3"
+ is-regex "^1.0.3"
+
+es-to-primitive@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
+ dependencies:
+ is-callable "^1.1.1"
+ is-date-object "^1.0.1"
+ is-symbol "^1.0.1"
+
es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7:
version "0.10.12"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047"
@@ -1858,6 +1889,10 @@ eslint-module-utils@^1.0.0:
debug "2.2.0"
pkg-dir "^1.0.0"
+eslint-plugin-babel@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-4.1.1.tgz#ef285c87039b67beb3bbd227f5b0eed4fb376b87"
+
eslint-plugin-flowtype@2.21.0:
version "2.21.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.21.0.tgz#a47e85abcdd181d37a336054bd552149ae387d9c"
@@ -1894,6 +1929,16 @@ eslint-plugin-react@6.4.1:
doctrine "^1.2.2"
jsx-ast-utils "^1.3.1"
+eslint-plugin-react@^6.10.0:
+ version "6.10.3"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78"
+ dependencies:
+ array.prototype.find "^2.0.1"
+ doctrine "^1.2.2"
+ has "^1.0.1"
+ jsx-ast-utils "^1.3.4"
+ object.assign "^4.0.4"
+
eslint@3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.8.1.tgz#7d02db44cd5aaf4fa7aa489e1f083baa454342ba"
@@ -2215,6 +2260,10 @@ for-own@^0.1.4:
dependencies:
for-in "^1.0.1"
+foreach@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -2273,7 +2322,7 @@ fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10:
mkdirp ">=0.5 0"
rimraf "2"
-function-bind@^1.0.2:
+function-bind@^1.0.2, function-bind@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
@@ -2647,12 +2696,20 @@ is-builtin-module@^1.0.0:
dependencies:
builtin-modules "^1.0.0"
+is-callable@^1.1.1, is-callable@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+
is-ci@^1.0.9:
version "1.0.10"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e"
dependencies:
ci-info "^1.0.0"
+is-date-object@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+
is-dotfile@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d"
@@ -2750,6 +2807,12 @@ is-property@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+is-regex@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+ dependencies:
+ has "^1.0.1"
+
is-resolvable@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
@@ -2766,6 +2829,10 @@ is-svg@^2.0.0:
dependencies:
html-comment-regex "^1.1.0"
+is-symbol@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
+
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -3161,7 +3228,7 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.3.6"
-jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.1:
+jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.1, jsx-ast-utils@^1.3.4:
version "1.4.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.0.tgz#5afe38868f56bc8cc7aeaef0100ba8c75bd12591"
dependencies:
@@ -3625,6 +3692,18 @@ object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+object-keys@^1.0.10, object-keys@^1.0.8:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+
+object.assign@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc"
+ dependencies:
+ define-properties "^1.1.2"
+ function-bind "^1.1.0"
+ object-keys "^1.0.10"
+
object.omit@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
diff --git a/package.json b/package.json
index 7098847..4a9035f 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"babel": "^6.23.0",
"babel-cli": "^6.23.0",
"babel-core": "^6.23.1",
+ "babel-eslint": "^7.1.1",
"babel-istanbul": "^0.12.2",
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-transform-builtin-extend": "^1.1.2",
diff --git a/src/index.js b/src/index.js
index 96dee02..ceabaa7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,23 +1,34 @@
// dotenv allows us to declare environment variables in a .env file, \
// find out more here https://github.com/motdotla/dotenv
-require("dotenv").config();
-const express = require("express");
-const bodyParser = require("body-parser");
-const mongoose = require("mongoose");
-const passport = require("passport");
+require('dotenv').config();
+import express from 'express';
+import bodyParser from 'body-parser';
+import mongoose from 'mongoose';
+import passport from 'passport';
+
+
+require('./services/passport');
mongoose.Promise = global.Promise;
mongoose
- .connect("mongodb://localhost/todo-list-app")
- .then(() => console.log("[mongoose] Connected to MongoDB"))
- .catch(() => console.log("[mongoose] Error connecting to MongoDB"));
+ .connect('mongodb://localhost/todo-list-app')
+ .then(() => console.log('[mongoose] Connected to MongoDB'))
+ .catch(() => console.log('[mongoose] Error connecting to MongoDB'));
const app = express();
-const authenticationRoutes = require("./routes/AuthenticationRoutes");
+const authenticationRoutes = require('./routes/AuthenticationRoutes');
+console.log(authenticationRoutes);
app.use(bodyParser.json());
-// app.use(authenticationRoutes);
+app.use(authenticationRoutes);
+
+const authStrategy = passport.authenticate('authStrategy', { session: false });
+
+app.get('/api/secert', authStrategy, (request, response) => {
+ response.send(`The current user is ${request.user.username}`);
+});
+
const port = process.env.PORT || 3001;
app.listen(port, () => {
console.log(`Listening on port:${port}`);
diff --git a/src/models/UserModel.js b/src/models/UserModel.js
index 47c5e39..f0818a7 100644
--- a/src/models/UserModel.js
+++ b/src/models/UserModel.js
@@ -1,6 +1,5 @@
-const mongoose = require('mongoose');
+import mongoose from 'mongoose';
const Schema = mongoose.Schema;
-const bcrypt = require('bcrypt');
const userSchema = new Schema({
username: {
diff --git a/src/routes/AuthenticationRoutes.js b/src/routes/AuthenticationRoutes.js
index 611413a..c3aecc8 100644
--- a/src/routes/AuthenticationRoutes.js
+++ b/src/routes/AuthenticationRoutes.js
@@ -1,5 +1,48 @@
-const express = require('express');
+import express from 'express';
const router = express.Router();
-const jwt = require('jwt-simple');
-const User = require('../models/UserModel');
-const bcrypt = require('bcrypt');
+import jwt from 'jwt-simple';
+import User from '../models/UserModel';
+import bcrypt from 'bcrypt';
+import passport from 'passport';
+import '../services/passport';
+
+const signinStrategy = passport.authenticate('signinStrategy', { session: false });
+
+function tokenForUser(user) {
+ const timestamp = new Date().getTime();
+ return jwt.encode({ userId: user.id, iat: timestamp }, process.env.SECRET);
+}
+
+router.post('/api/signin', (request, response) => {
+ response.json({ message: 'You\'ve been authenticated!'});
+});
+
+router.post('/api/signup', signinStrategy, (request, response, next) => {
+ const {username, password} = request.body;
+
+ if (!username || !password) {
+ return response.status(422)
+ .json({error: 'You must enter a username and/or password'});
+ }
+
+ User.findOne({ username }).exec()
+ .then(existingUser => {
+ if (existingUser) {
+ return response.status(422).json({ error: 'Username is in use' });
+ }
+
+ bcrypt.hash(password, 10, (err, hashedPassword) => {
+ if (err) {
+ return next(err);
+ }
+
+ const newUser = new User({ username, password: hashedPassword });
+
+ newUser.save()
+ .then(user => response.json({token: tokenForUser(user)}));
+ });
+ })
+ .catch(err => next(err));
+});
+
+export default router;
diff --git a/src/services/passport.js b/src/services/passport.js
index 5680076..8b38ba2 100644
--- a/src/services/passport.js
+++ b/src/services/passport.js
@@ -1,5 +1,52 @@
-const bcrypt = require('bcrypt');
-const passport = require('passport');
-const User = require('../models/UserModel');
-const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
-const LocalStrategy = require('passport-local');
+import bcrypt from 'bcrypt';
+import passport from 'passport';
+import User from '../models/UserModel';
+import {
+ Strategy as JwtStrategy,
+ ExtractJwt
+} from 'passport-jwt';
+import LocalStrategy from 'passport-local';
+require('dotenv').config();
+
+const signinStrategy = new LocalStrategy((username, password, done) => {
+ User.findOne({ username }).exec()
+ .then(user => {
+ if (!user) {
+ return done(null, false);
+ }
+
+ bcrypt.compare(password, user.password, (err, isMatch) => {
+
+ if (err) {
+ return done(err, false);
+ }
+
+ if (!isMatch) {
+ return done(null, false);
+ }
+
+ return done(null, user);
+ });
+ })
+ .catch(err => done(err, false));
+});
+
+const jwtOptions = {
+ secretOrKey: process.env.SECRET,
+ jwtFromRequest: ExtractJwt.fromHeader('authorization')
+};
+
+const authStrategy = new JwtStrategy(jwtOptions, (payload, done) => {
+ User.findById(payload.userId, (err, user) => {
+ if (err) { return done(err, false); }
+
+ if (user) {
+ done(null, user);
+ } else {
+ done(null, false);
+ }
+ });
+});
+
+passport.use('authStrategy', authStrategy);
+passport.use('signinStrategy', signinStrategy);
diff --git a/yarn.lock b/yarn.lock
index bf94ef4..aa617de 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -232,6 +232,16 @@ babel-core@^6.23.0, babel-core@^6.23.1:
slash "^1.0.0"
source-map "^0.5.0"
+babel-eslint@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.1.tgz#8a6a884f085aa7060af69cfc77341c2f99370fb2"
+ dependencies:
+ babel-code-frame "^6.16.0"
+ babel-traverse "^6.15.0"
+ babel-types "^6.15.0"
+ babylon "^6.13.0"
+ lodash.pickby "^4.6.0"
+
babel-generator@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5"
@@ -822,7 +832,7 @@ babel-template@^6.22.0, babel-template@^6.23.0, babel-template@^6.3.0:
babylon "^6.11.0"
lodash "^4.2.0"
-babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
+babel-traverse@^6.15.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
version "6.23.1"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48"
dependencies:
@@ -836,7 +846,7 @@ babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1:
invariant "^2.2.0"
lodash "^4.2.0"
-babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0:
+babel-types@^6.15.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf"
dependencies:
@@ -849,7 +859,7 @@ babel@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel/-/babel-6.23.0.tgz#d0d1e7d803e974765beea3232d4e153c0efb90f4"
-babylon@^6.11.0, babylon@^6.15.0:
+babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0:
version "6.16.1"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3"
@@ -2303,6 +2313,10 @@ lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+lodash.pickby@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"
+
lodash.restparam@^3.0.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"