Skip to content

Instantly share code, notes, and snippets.

@luishrd
Last active July 18, 2019 02:43
Show Gist options
  • Save luishrd/afecd77686297a3125b68b96ef6371a6 to your computer and use it in GitHub Desktop.
Save luishrd/afecd77686297a3125b68b96ef6371a6 to your computer and use it in GitHub Desktop.
CS10 - JWT Auth
// ./client/src/App.js
import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';
import logo from './logo.svg';
import './App.css';
import Signin from './auth/Signin';
import Users from './users/Users';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
<div>
{localStorage.getItem('jwt') && (
<button onClick={this.signout}>Signout</button>
)}
</div>
</header>
<Route path="/signin" component={Signin} />
<Route path="/users" component={Users} />
</div>
);
}
signout = () => {
if (localStorage.getItem('jwt')) {
localStorage.removeItem('jwt');
this.props.history.push('/signin');
}
};
}
export default withRouter(App);
// ./client/package.json
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
// ./client/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import App from './App';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
registerServiceWorker();
// ./server/package.json
{
"name": "jwts",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "nodemon"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^2.0.1",
"cors": "^2.8.4",
"express": "^4.16.3",
"jsonwebtoken": "^8.3.0",
"mongoose": "^5.1.5"
},
"devDependencies": {
"nodemon": "^1.17.5"
}
}
// ./server/server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const User = require('./users/UserModel.js');
const server = express();
const secret = "toss me, but don't tell the elf!";
const corsOptions = {
origin: 'http://localhost:3000', // allow only the React application to connect
credentials: true, // sets the Access-Control-Allow-Credentials CORS header
};
server.use(express.json());
server.use(cors(corsOptions));
server.post('/api/register', (req, res) => {
User.create(req.body)
.then(user => {
const token = generateToken(user);
res.status(201).json({ username: user.username, token });
})
.catch(err => res.status(500).json(err));
});
server.post('/api/login', (req, res) => {
const { username, password } = req.body;
User.findOne({ username })
.then(user => {
if (user) {
user
.validatePassword(password)
.then(passwordsMatch => {
if (passwordsMatch) {
// generate token
const token = generateToken(user);
// send token to the client
res.status(200).json({ message: `welcome ${username}!`, token });
} else {
res.status(401).send('invalid credentials');
}
})
.catch(err => {
res.send('error comparing passwords');
});
} else {
res.status(401).send('invalid credentials');
}
})
.catch(err => {
res.send(err);
});
});
function generateToken(user) {
const options = {
expiresIn: '1h',
};
const payload = { name: user.username };
// sign the token
return jwt.sign(payload, secret, options);
}
function restricted(req, res, next) {
const token = req.headers.authorization;
if (token) {
jwt.verify(token, secret, (err, decodedToken) => {
// req.jwtPayload.decodedToken = decodedToken;
if (err) {
return res
.status(401)
.json({ message: 'you shall not pass! not decoded' });
}
next();
});
} else {
res.status(401).json({ message: 'you shall not pass! no token' });
}
}
server.get('/api/users', restricted, (req, res) => {
User.find({})
.select('username')
.then(users => {
res.status(200).json(users);
})
.catch(err => {
return res.status(500).json(err);
});
});
const port = process.env.PORT || 5000;
mongoose
.connect('mongodb://localhost/cs10jwt')
.then(() => {
console.log('\n=== Connected to MongoDB ===');
server.listen(port, (req, res) => {
console.log(`\n=== API up on port ${port} ===\n`);
});
})
.catch(err =>
console.log('\n=== Error connecting to MongoDb, is it running? ===\n', err)
);
// ./client/src/auth/signin.js
import React from 'react';
import axios from 'axios';
class Signin extends React.Component {
state = {
username: 'sam',
password: 'mellon',
};
render() {
return (
<form onSubmit={this.submitHandler}>
<div>
<label>Username</label>
<input
value={this.state.username}
onChange={this.inputChangeHandler}
name="username"
type="text"
/>
</div>
<div>
<label>Password</label>
<input
value={this.state.password}
onChange={this.inputChangeHandler}
name="password"
type="password"
/>
</div>
<div>
<button type="submit">Signin</button>
</div>
</form>
);
}
submitHandler = event => {
event.preventDefault();
axios
.post('http://localhost:5000/api/login', this.state)
.then(response => {
localStorage.setItem('jwt', response.data.token);
console.log('signing props', this.props);
this.props.history.push('/users');
})
.catch(err => console.log('bad panda!'));
};
inputChangeHandler = event => {
const { name, value } = event.target;
this.setState({ [name]: value });
};
}
export default Signin;
// ./server/users/UserModel.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
lowercase: true,
},
password: {
type: String,
required: true,
// minlength: 4, // make this at least 12 in production
maxlength: 25,
validate: checkPasswordLength,
msg: 'password is too weak',
},
race: {
type: String,
required: true,
validate: {
validator: /(hobbit|human|elf)/i,
msg: 'invalid race',
},
},
});
function checkPasswordLength(password) {
return password.length > 12;
}
userSchema.pre('save', function(next) {
return bcrypt
.hash(this.password, 10) // this time we'll use promises instead of a callback
.then(hash => {
this.password = hash;
return next();
})
.catch(err => {
return next(err);
});
});
userSchema.methods.validatePassword = function(passwordGuess) {
return bcrypt.compare(passwordGuess, this.password);
};
module.exports = mongoose.model('User', userSchema, 'users');
// ./client/users/Users.js
import React from 'react';
import axios from 'axios';
class Users extends React.Component {
state = {
users: [],
};
render() {
return (
<ul>
{this.state.users.map(user => <li key={user._id}>{user.username}</li>)}
</ul>
);
}
componentDidMount() {
// get the token from somewhere
const token = localStorage.getItem('jwt');
// attach the token as the Authorization header
const requestOptions = {
headers: {
Authorization: token,
},
};
axios
.get('http://localhost:5000/api/users', requestOptions)
.then(response => {
this.setState({ users: response.data });
console.log(response.data);
})
.catch(err => {
console.error(err);
});
}
}
export default Users;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment