├── .gitignore
├── README.md
├── client
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── actions
│ │ ├── index.js
│ │ └── types.js
│ ├── components
│ │ ├── auth
│ │ │ ├── form_helpers.js
│ │ │ ├── require_auth.js
│ │ │ ├── signin.js
│ │ │ ├── signin_form.js
│ │ │ ├── signout.js
│ │ │ ├── signup.js
│ │ │ └── signup_form.js
│ │ ├── feature.js
│ │ ├── header.js
│ │ └── welcome.js
│ ├── index.js
│ ├── reducers
│ │ ├── auth_reducer.js
│ │ └── index.js
│ └── style
│ │ └── style.css
└── yarn.lock
└── server
├── controllers
└── authentication.js
├── index.js
├── models
└── user.js
├── package.json
├── router.js
└── services
└── passport.js
/.gitignore:
--------------------------------------------------------------------------------
1 | server/node_modules
2 | server/config.js
3 | client/node_modules
4 | client/bundle.js
5 | client/npm-debug.log
6 | client/yarn-debug.log
7 | client/.DS_Store
8 | client/build
9 | client/coverage
10 |
11 | # IntelliJ
12 | *.iml
13 | /.idea
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-router-v4-redux-auth
2 |
3 | This is an application that models the implementation of JWT with the following:
4 |
5 | frontend:
6 | * react-router v4
7 | * react
8 | * redux
9 | * redux-form v6.7
10 | * material-ui
11 |
12 | backend:
13 | * express.js
14 | * MongoDB
15 | * passport.js
16 |
17 |
18 |
19 | ## Usage:
20 |
21 | 1. Start a MongoDB server running on port `27017`
22 |
23 | 1. In `/server`, create a new file called `config.js` to hold your JWT `secretOrKey`. Here is an example:
24 |
25 | ```javascript
26 | /*
27 | /server/config.js
28 | */
29 |
30 | module.exports = {
31 | secret: 'sdajkljdsalkj8932904ujaskfs'
32 | }
33 | ```
34 |
35 | 1. Navigate to `/server` in your terminal
36 | 1. run `npm install`.
37 | 1. run `npm run dev` to start the server
38 |
39 | 1. Navigate to `/client` in a new terminal window
40 | 1. run `yarn install`
41 | 1. run `yarn start` to start the webpack dev server
42 |
43 | 1. Navigate to `localhost:3000` in your browser
44 |
45 |
46 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "axios": "^0.16.1",
7 | "lodash": "^4.17.4",
8 | "material-ui": "^0.18.0",
9 | "react": "^15.5.4",
10 | "react-dom": "^15.5.4",
11 | "react-redux": "^5.0.4",
12 | "react-router-dom": "^4.1.1",
13 | "react-tap-event-plugin": "^2.0.1",
14 | "redux": "^3.6.0",
15 | "redux-form": "^6.7.0",
16 | "redux-thunk": "^2.2.0"
17 | },
18 | "devDependencies": {
19 | "react-scripts": "0.9.5"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test --env=jsdom",
25 | "eject": "react-scripts eject"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gillisd/react-router-v4-redux-auth/39bb35e50eb1af131711858f8012459191e297fe/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 | React App
18 |
19 |
20 |
21 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/client/src/actions/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { UNAUTH_USER, AUTH_USER, AUTH_ERROR, FETCH_MESSAGE } from './types'
3 | const ROOT_URL = 'http://localhost:3090'
4 |
5 | export function signinUser({email, password}) {
6 |
7 | return function (dispatch) {
8 |
9 | // submit email and password to server
10 | const request = axios.post(`${ROOT_URL}/signin`, {email, password})
11 | request
12 | .then(response => {
13 | // -Save the JWT token
14 | localStorage.setItem('token', response.data.token)
15 |
16 | // -if request is good, we need to update state to indicate user is authenticated
17 | dispatch({type: AUTH_USER})
18 | })
19 |
20 | // If request is bad...
21 | // -Show an error to the user
22 | .catch(() => {
23 | dispatch(authError('bad login info'))
24 | })
25 |
26 | }
27 | }
28 |
29 | export function signoutUser() {
30 | localStorage.removeItem('token')
31 | return {
32 | type: UNAUTH_USER
33 | }
34 | }
35 |
36 | export function signupUser({email, password, passwordConfirmation}) {
37 | return function (dispatch) {
38 | axios.post(`${ROOT_URL}/signup`, {email, password, passwordConfirmation})
39 | .then(response => {
40 | dispatch({type: AUTH_USER})
41 | localStorage.setItem('token', response.data.token)
42 | })
43 | .catch(({response}) => {
44 | dispatch(authError(response.data.error))
45 | })
46 | }
47 | }
48 |
49 | export function authError(error) {
50 | return {
51 | type: AUTH_ERROR,
52 | payload: error
53 | }
54 | }
55 |
56 | export function fetchMessage() {
57 | return function (dispatch) {
58 | axios.get(ROOT_URL, {
59 | headers: {authorization: localStorage.getItem('token')}
60 | })
61 | .then(response => {
62 | dispatch({
63 | type: FETCH_MESSAGE,
64 | payload: response.data.message
65 | })
66 | })
67 | }
68 | }
--------------------------------------------------------------------------------
/client/src/actions/types.js:
--------------------------------------------------------------------------------
1 | export const AUTH_USER = 'auth_user'
2 | export const UNAUTH_USER = 'unauth_user'
3 | export const AUTH_ERROR = 'auth_error'
4 | export const FETCH_MESSAGE = 'fetch_message'
--------------------------------------------------------------------------------
/client/src/components/auth/form_helpers.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import TextField from 'material-ui/TextField'
3 |
4 | export const renderTextField = ({input, type, label, meta: {touched, error}, ...custom}) => ( // Define stateless component to render input and errors
5 |
6 |
12 | {touched && error && {error}}
13 |
14 | )
15 |
--------------------------------------------------------------------------------
/client/src/components/auth/require_auth.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Route, Redirect } from 'react-router-dom'
4 |
5 | export const PrivateRoute = ({component: ComposedComponent, ...rest}) => {
6 |
7 | class Authentication extends Component {
8 |
9 | // redirect if not authenticated; otherwise, return the component imputted into
10 | handleRender(props) {
11 | if (!this.props.authenticated) {
12 | return
19 | } else {
20 | return
21 | }
22 | }
23 |
24 | render() {
25 | return (
26 |
27 | )
28 | }
29 | }
30 |
31 | function mapStateToProps(state) {
32 | return {authenticated: state.auth.authenticated};
33 | }
34 |
35 | const AuthenticationContainer = connect(mapStateToProps)(Authentication)
36 | return
37 | }
--------------------------------------------------------------------------------
/client/src/components/auth/signin.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import SigninForm from './signin_form'
3 | import * as actions from '../../actions'
4 | import { connect } from 'react-redux'
5 | import { Redirect } from 'react-router-dom'
6 |
7 | class Signin extends Component {
8 |
9 | componentWillUnmount() {
10 | if (this.props.errorMessage) {
11 | this.props.authError(null)
12 | }
13 | }
14 |
15 | displayRedirectMessages() {
16 | const location = this.props.location
17 | return location.state && {location.state.message}
18 | }
19 |
20 | handleSubmit({email, password}) {
21 | this.props.signinUser({email, password})
22 | }
23 |
24 | getRedirectPath() {
25 | const locationState = this.props.location.state
26 | if (locationState && locationState.from.pathname) {
27 | return locationState.from.pathname // redirects to referring url
28 | } else {
29 | return '/'
30 | }
31 | }
32 |
33 | render() {
34 | return (this.props.authenticated) ?
35 |
40 | :
41 |
42 | {this.displayRedirectMessages()}
43 |
44 |
45 | }
46 | }
47 |
48 | function mapStateToProps(state) {
49 | return {
50 | authenticated: state.auth.authenticated,
51 | errorMessage: state.auth.error
52 | }
53 | }
54 |
55 | export default connect(mapStateToProps, actions)(Signin)
--------------------------------------------------------------------------------
/client/src/components/auth/signin_form.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { reduxForm, Field } from 'redux-form'
3 | import { renderTextField } from './form_helpers'
4 | import RaisedButton from 'material-ui/RaisedButton'
5 |
6 |
7 | class SigninForm extends Component {
8 |
9 | renderAlert() {
10 | if (this.props.errorMessage) {
11 | return
12 | Oops: {this.props.errorMessage}
13 |
14 | }
15 | }
16 |
17 | render() {
18 | const {handleSubmit} = this.props
19 |
20 | return (
21 |
22 | {this.renderAlert()}
23 |
39 |
40 | )
41 | }
42 | }
43 |
44 | export default reduxForm({
45 | form: 'signin'
46 | })(SigninForm)
47 |
--------------------------------------------------------------------------------
/client/src/components/auth/signout.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import * as actions from '../../actions'
4 | class Signout extends Component {
5 |
6 | componentWillMount() {
7 | this.props.signoutUser()
8 | }
9 |
10 | render() {
11 | return Bye Bye
12 | }
13 | }
14 |
15 | export default connect(null, actions)(Signout)
--------------------------------------------------------------------------------
/client/src/components/auth/signup.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import SignupForm from './signup_form'
4 | import * as actions from '../../actions'
5 | import { Redirect } from 'react-router-dom'
6 |
7 | class Signup extends Component {
8 |
9 | componentWillUnmount() {
10 | if (this.props.errorMessage) {
11 | this.props.authError(null)
12 | }
13 | }
14 |
15 | handleSubmit({email, password, passwordConfirmation}) {
16 | this.props.signupUser({email, password, passwordConfirmation})
17 | }
18 |
19 | getRedirectPath() {
20 | const locationState = this.props.location.state
21 | if (locationState && locationState.from.pathname) {
22 | return locationState.from.pathname
23 | } else {
24 | return '/'
25 | }
26 | }
27 |
28 | render() {
29 | return (this.props.authenticated) ?
30 |
35 | :
36 |
37 |
38 |
39 | }
40 | }
41 |
42 | function mapStateToProps(state) {
43 | return {
44 | authenticated: state.auth.authenticated,
45 | errorMessage: state.auth.error
46 | }
47 | }
48 |
49 | export default connect(mapStateToProps, actions)(Signup)
--------------------------------------------------------------------------------
/client/src/components/auth/signup_form.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { reduxForm, Field } from 'redux-form'
3 | import RaisedButton from 'material-ui/RaisedButton'
4 | import { renderTextField } from './form_helpers'
5 |
6 | class SignupForm extends Component {
7 |
8 | renderAlert() {
9 | if (this.props.errorMessage) {
10 | return
11 | Oops: {this.props.errorMessage}
12 |
13 | }
14 | }
15 |
16 | render() {
17 | const {handleSubmit} = this.props
18 |
19 | return (
20 |
21 | {this.renderAlert()}
22 |
44 |
45 | )
46 | }
47 | }
48 |
49 | const validate = values => {
50 | const errors = {}
51 |
52 | if (values.password !== values.passwordConfirmation) {
53 | errors.password = 'Passwords must match'
54 | }
55 |
56 | if (!values.email) {
57 | errors.email = 'Please enter an email'
58 | }
59 |
60 | if (!values.password) {
61 | errors.password = 'Please enter a password'
62 | }
63 |
64 | if (!values.passwordConfirmation) {
65 | errors.passwordConfirmation = 'Please confirm your password'
66 | }
67 |
68 | return errors
69 | }
70 |
71 |
72 | export default reduxForm({
73 | form: 'signup',
74 | validate
75 | })(SignupForm)
--------------------------------------------------------------------------------
/client/src/components/feature.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import * as actions from '../actions'
4 | import { Link } from 'react-router-dom'
5 |
6 | class Feature extends Component {
7 |
8 | componentWillMount() {
9 | this.props.fetchMessage()
10 | }
11 |
12 | render() {
13 | return (
14 |
15 |
Welcome to the secure page!
16 |
17 |
Here's a secret response from the server that your token returned:
18 | ____________________________________________________________
19 |
{this.props.message}
20 | ____________________________________________________________
21 |
22 |
Notice that clicking these links redirect to the homepage, as you are already signed in:
23 |
24 | /signin | /signup
25 |
26 |
27 |
28 | )
29 | }
30 | }
31 |
32 | function mapStateToProps(state) {
33 | return {
34 | message: state.auth.message
35 | }
36 | }
37 |
38 | export default connect(mapStateToProps, actions)(Feature)
--------------------------------------------------------------------------------
/client/src/components/header.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import { Link } from 'react-router-dom'
4 |
5 | class Header extends Component {
6 |
7 | renderLinks() {
8 | if (this.props.authenticated) {
9 | return [
10 |
11 | Sign Out
12 | ,
13 |
14 | Protected Site
15 |
16 | ]
17 | } else {
18 | return [
19 |
20 | Sign In
21 | ,
22 |
23 | Sign Up
24 | ,
25 |
26 | Protected Site
27 |
28 | ]
29 | }
30 | }
31 |
32 | render() {
33 | return (
34 |
40 | )
41 | }
42 | }
43 |
44 | function mapStateToProps(state) {
45 | return {
46 | authenticated: state.auth.authenticated
47 | }
48 | }
49 |
50 | export default connect(mapStateToProps,)(Header)
--------------------------------------------------------------------------------
/client/src/components/welcome.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default () => {
4 | return (
5 |
6 | This is the landing page...
7 | sign in to access the secure page
8 |
9 | )
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import { Provider } from 'react-redux'
4 | import { createStore, applyMiddleware } from 'redux'
5 | import reduxThunk from 'redux-thunk'
6 | import { BrowserRouter as Router, Route } from 'react-router-dom'
7 | import { AUTH_USER } from './actions/types'
8 | import Header from './components/header'
9 | import Welcome from './components/welcome'
10 | import injectTapEventPlugin from 'react-tap-event-plugin'
11 | import Signin from './components/auth/signin'
12 | import Signout from './components/auth/signout'
13 | import Signup from './components/auth/signup'
14 | import { PrivateRoute } from './components/auth/require_auth'
15 | import Feature from './components/feature'
16 | import reducers from './reducers'
17 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
18 |
19 |
20 | // Needed for onTouchTap with material-ui
21 | // http://stackoverflow.com/a/34015469/988941
22 |
23 | injectTapEventPlugin()
24 |
25 | const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore)
26 | const store = createStoreWithMiddleware(reducers)
27 | const token = localStorage.getItem('token')
28 |
29 | if (token) {
30 | store.dispatch({type: AUTH_USER})
31 | }
32 |
33 |
34 | ReactDOM.render(
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | , document.getElementById('root'))
50 |
--------------------------------------------------------------------------------
/client/src/reducers/auth_reducer.js:
--------------------------------------------------------------------------------
1 | import { AUTH_USER, UNAUTH_USER, AUTH_ERROR, FETCH_MESSAGE } from '../actions/types'
2 |
3 | export default function authReducer(state = {}, action) {
4 | switch (action.type) {
5 | case AUTH_USER:
6 | return {...state, error: '', authenticated: true}
7 | case UNAUTH_USER:
8 | return {...state, authenticated: false}
9 | case AUTH_ERROR:
10 | return {...state, error: action.payload}
11 | case FETCH_MESSAGE:
12 | return {...state, message: action.payload}
13 | default:
14 | return state
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/client/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { reducer as form } from 'redux-form'
3 | import authReducer from './auth_reducer'
4 |
5 | const rootReducer = combineReducers({
6 | form,
7 | auth: authReducer
8 | })
9 |
10 | export default rootReducer
11 |
--------------------------------------------------------------------------------
/client/src/style/style.css:
--------------------------------------------------------------------------------
1 | .error {
2 | color: red;
3 | }
--------------------------------------------------------------------------------
/server/controllers/authentication.js:
--------------------------------------------------------------------------------
1 | const jwt = require('jwt-simple');
2 | const User = require('../models/user');
3 | const config = require('../config');
4 |
5 | function tokenForUser(user) {
6 | const timestamp = new Date().getTime();
7 | return jwt.encode({sub: user.id, iat: timestamp}, config.secret)
8 | }
9 |
10 | exports.signin = function (req, res, next) {
11 | // User has already had their email and password auth'd
12 | // We just need to give them a token
13 | res.send({token: tokenForUser(req.user)})
14 | }
15 |
16 | exports.signup = function (req, res, next) {
17 | const email = req.body.email;
18 | const password = req.body.password;
19 | // See if a user with the given email exists
20 |
21 |
22 | if (!email || !password) {
23 | return res.status(422).send({error: 'You must provide email and password'})
24 | }
25 |
26 | User.findOne({email: email}, function (err, existingUser) {
27 | if (err) {
28 | return next(err);
29 | }
30 |
31 | // If a user with a given email does exist, return an error
32 |
33 | if (existingUser) {
34 | return res.status(422).send({error: 'Email is in use'});
35 | }
36 |
37 | const user = new User({
38 | email: email,
39 | password: password
40 | });
41 |
42 | user.save(function (err) {
43 | if (err) {
44 | return next(err);
45 | }
46 |
47 | res.json({token: tokenForUser(user)});
48 | });
49 |
50 | });
51 |
52 |
53 | // If a user with email does not exist, create and save a user record
54 |
55 | // Respond to request indicating that the user was created
56 | }
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const http = require('http');
3 | const bodyparser = require('body-parser');
4 | const morgan = require('morgan');
5 | const app = express();
6 | const router = require('./router')
7 | const mongoose = require('mongoose')
8 | const cors = require('cors')
9 |
10 | mongoose.connect('mongodb://localhost:auth/auth')
11 |
12 | app.use(morgan('combined'));
13 | app.use(cors());
14 | app.use(bodyparser.json({type: '*/*'}))
15 |
16 | router(app);
17 | const port = process.env.PORT || 3090;
18 | const server = http.createServer(app);
19 | server.listen(port);
20 | console.log('Server listening on:', port);
21 |
22 |
--------------------------------------------------------------------------------
/server/models/user.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 | const bcrypt = require('bcrypt-nodejs')
4 |
5 |
6 | const userSchema = new Schema({
7 | email: {type: String, unique: true, lowercase: true},
8 | password: String
9 | });
10 |
11 | // On Save Hook, encrypt password
12 |
13 | userSchema.pre('save', function (next) {
14 | const user = this;
15 | bcrypt.genSalt(10, function (err, salt) {
16 | if (err) {
17 | return next(err);
18 | }
19 |
20 | bcrypt.hash(user.password, salt, null, function (err, hash) {
21 | if (err) {
22 | return next(err);
23 | }
24 |
25 | user.password = hash;
26 | next();
27 | });
28 |
29 | });
30 | });
31 |
32 | userSchema.methods.comparePassword = function (candidatePassword, callback) {
33 | bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
34 | if (err) {
35 | return callback(err);
36 | }
37 | callback(null, isMatch);
38 | });
39 | }
40 |
41 | const ModelClass = mongoose.model('user', userSchema);
42 |
43 | module.exports = ModelClass;
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "nodemon index.js"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "bcrypt-nodejs": "0.0.3",
14 | "body-parser": "^1.17.1",
15 | "cors": "^2.8.3",
16 | "express": "^4.15.2",
17 | "jwt-simple": "^0.5.1",
18 | "mongoose": "^4.9.8",
19 | "morgan": "^1.8.1",
20 | "nodemon": "^1.11.0",
21 | "passport": "^0.3.2",
22 | "passport-jwt": "^2.2.1",
23 | "passport-local": "^1.0.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/server/router.js:
--------------------------------------------------------------------------------
1 | const Authentication = require('./controllers/authentication');
2 | const passportService = require('./services/passport');
3 | const passport = require('passport');
4 |
5 | const requireAuth = passport.authenticate('jwt', {session: false});
6 | const requireSignIn = passport.authenticate('local', {session: false});
7 |
8 | module.exports = function (app) {
9 | app.get('/', requireAuth, function (req, res) {
10 | res.send({message: 'S3CR3T M3SS4G3'});
11 | });
12 | app.post('/signup', Authentication.signup);
13 | app.post('/signin', requireSignIn, Authentication.signin);
14 | }
--------------------------------------------------------------------------------
/server/services/passport.js:
--------------------------------------------------------------------------------
1 | const passport = require('passport');
2 | const User = require('../models/user');
3 | const config = require('../config');
4 | const JwtStrategy = require('passport-jwt').Strategy;
5 | const ExtractJwt = require('passport-jwt').ExtractJwt;
6 | const LocalStrategy = require('passport-local');
7 |
8 |
9 | // Create local strategy
10 | const localOptions = {usernameField: 'email'};
11 | const localLogin = new LocalStrategy(localOptions, function (email, password, done) {
12 | // Verify this email and password, call done with the user
13 | // if it is correct email and password
14 | // otherwise call done with false
15 | User.findOne({email: email}, function (err, user) {
16 | if (err) {
17 | return done(err)
18 | }
19 | if (!user) {
20 | return done(null, false);
21 | }
22 | user.comparePassword(password, function (err, isMatch) {
23 | if (err) {
24 | return done(err);
25 | }
26 | if (!isMatch) {
27 | return done(null, false);
28 | }
29 | return done(null, user);
30 | })
31 | // compare passwords - is `password` equal to user.password?
32 | })
33 |
34 | });
35 |
36 |
37 | // Setup options for JWT strategy
38 |
39 | const jwtOptions = {
40 | jwtFromRequest: ExtractJwt.fromHeader('authorization'),
41 | secretOrKey: config.secret
42 | };
43 |
44 | // Create jwt strategy
45 |
46 | const jwtLogin = new JwtStrategy(jwtOptions, function (payload, done) {
47 | // See if the user ID in the payload exists in our database
48 | // If it does, call 'done' with that other
49 | // otherwise, call done without a user object
50 | User.findById(payload.sub, function (err, user) {
51 | if (err) {
52 | return done(err, false);
53 | }
54 |
55 | if (user) {
56 | done(null, user);
57 | } else {
58 | done(null, false);
59 | }
60 | });
61 | });
62 |
63 |
64 | // Tell passport to use this strategy
65 | passport.use(jwtLogin);
66 | passport.use(localLogin);
--------------------------------------------------------------------------------