Passport-twitter strategy requires refresh to provide token - express

Revising this question as I have made some progress and passport-twitter authentication is working mostly: VueJS app with Express/Knex/Postgresql backend.
The problem is that I am combining passport-twitter with my previous usage of passport-local using jwt. As a result, I have to generate a header token for the new Twitter user. Currently on logging in with Twitter, the header token is not being read. Without a valid token the app does crazy, time consuming things.
here is the header object from the initial Twitter login request, showing token as an undefined Object:
headers:
{ host: 'localhost:8000',
connection: 'keep-alive',
accept: 'application/json, text/plain, */*',
origin: 'http://localhost:8080',
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
token: '[object Object]',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
referer:
'http://localhost:8080/?user=%7B%22id%22%3A10,%22email%22%3Anull,%22token%22%3A%22eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6bnVsbCwiaWQiOjEwLCJleHAiOjE1NzYwNTU1MzYsImlhdCI6MTU3MDg2NzkzNn0.SUYL2KcnK8C9zYNLCcqGF99TjfIRxAcyg0Uc8WjQJD0%22%7D',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
'if-none-match': 'W/"79-a0G25cV4V6cfQoTIFr1X/nYU2Kg"' },
On refreshing the browser, the header token appears as expected and errors are gone.
headers:
{ host: 'localhost:8000',
connection: 'keep-alive',
accept: 'application/json, text/plain, */*',
origin: 'http://localhost:8080',
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
token:
'Token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6bnVsbCwiaWQiOjEwLCJleHAiOjE1NzYwNTU1MzYsImlhdCI6MTU3MDg2NzkzNn0.SUYL2KcnK8C9zYNLCcqGF99TjfIRxAcyg0Uc8WjQJD0',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
referer:
'http://localhost:8080/?user=%7B%22id%22%3A10,%22email%22%3Anull,%22token%22%3A%22eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6bnVsbCwiaWQiOjEwLCJleHAiOjE1NzYwNTU1MzYsImlhdCI6MTU3MDg2NzkzNn0.SUYL2KcnK8C9zYNLCcqGF99TjfIRxAcyg0Uc8WjQJD0%22%7D',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
'if-none-match': 'W/"79-a0G25cV4V6cfQoTIFr1X/nYU2Kg"' }
Here are the relevant files in Express:
authenticate.js is middleware, I am using to get the token from the header:
const jwt = require('express-jwt');
const getTokenFromHeaders = (req) => {
console.log('req from headers is: ', req)
const { headers: { token } } = req; // same as token = req.headers.token
if(token && token.split(' ')[0] === 'Token') {
return token.split(' ')[1];
}
return null;
};
const auth = {
required: jwt({
secret: 'secret',
userProperty: 'user',
getToken: getTokenFromHeaders,
}),
optional: jwt({
secret: 'secret',
userProperty: 'user',
getToken: getTokenFromHeaders,
credentialsRequired: false,
}),
};
module.exports = auth;
app.js:
const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')
const dotenv = require('dotenv')
const Knex = require('knex')
const { Model } = require('objection')
const cors = require('cors');
const pathfinderUI = require('pathfinder-ui');
const session = require('express-session');
const passport = require('passport')
dotenv.config()
const app = express()
// objection setup
const knexfile = require('../knexfile')
const knex = Knex(knexfile[process.env.NODE_ENV])
Model.knex(knex)
//Configure our app
app.use('/pathfinder', function (req, res, next) {
pathfinderUI(app)
next()
}, pathfinderUI.router);
var corsOptions = {
origin: '*',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var sess = {
secret: 'keyboard cat',
cookie: {}
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1) // trust first proxy
sess.cookie.secure = true // serve secure cookies
}
app.use(session(sess))
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport');
// api routes
// should we refactor this?
app.use("/api/auth", require("./routes/auth"))
app.use("/api/posts", require("./routes/posts"))
app.use("/api/acts", require("./routes/acts"))
app.use("/api/reports", require("./routes/reports"))
app.use("/api/groups", require("./routes/groups"))
app.use("/api/users", require("./routes/users"))
app.use("/api/memberships", require("./routes/memberships"))
app.use("/api/kudos", require("./routes/kudos"))
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, "index.html"))
})
module.exports = app
passport.js:
const passport = require('passport');
const LocalStrategy = require('passport-local');
const TwitterStrategy = require('passport-twitter').Strategy
const authHelpers = require('../auth/helpers');
var port = process.env.PORT || 8000;
const User = require('../models/User');
var trustProxy = false;
if (process.env.DYNO) {
// Apps on heroku are behind a trusted proxy
trustProxy = true;
}
passport.use(new LocalStrategy({
usernameField: 'user[email]',
passwordField: 'user[password]',
}, async (email, password, done) => {
const user = await User.query().where('email', email );
if (user && user.length === 1 && authHelpers.comparePass(password, user[0].password)) {
return done(null, user[0]);
} else {
return done(null, false, { errors: { 'email or password': 'is invalid' } });
}
}));
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_SECRET_KEY,
callbackURL: process.env.TWITTER_CALLBACK_URL, //this will need to be dealt with
proxy: trustProxy
}, async function(token, tokenSecret, profile, done) {
let user = await User.query().findOne({twitter_id: profile.id_str})
console.log('hitting the passport twitter strategy')
if (user) {
// todo: update user with twitter profile
return done(null, user);
} else {
user = await authHelpers.createTwitterUser(profile);
console.log("The User from createTwitterUser is " + user);
return done(null, user);
}
}));
auth.js:
const express = require('express');
const User = require('../models/User');
const auth = require('../middlewares/authenticate');
const passport = require('passport');
const authHelpers = require('../auth/helpers')
let router = express.Router();
...
router.get('/twitter',
passport.authenticate('twitter')
);
router.get('/twitter/callback',
passport.authenticate('twitter', { failureRedirect: process.env.VUE_LOGIN_URL}),
function(req, res) {
// Successful authentication, redirect home.
const user = JSON.stringify(req.user);
console.log('hittingthe callback routesending stringified user: ', user);
res.redirect(process.env.VUE_HOME_URL + '?user=' + user);
});
module.exports = router;
helpers.js:
const bcrypt = require('bcryptjs');
const User = require('../models/User');
const jwt = require('jsonwebtoken');
...
async function createTwitterUser(profile) {
let newUser = await User.query()
.insert({
username: profile.screen_name,
bio: profile.description,
location: profile.location,
avatar: profile.profile_image_url_https,
twitter_id: profile.id_str //change to int
})
.returning('*');
console.log("new User is: ", newUser);
return newUser;
}
...
module.exports = {
comparePass,
createUser,
toAuthJSON
};

Related

Vue Apollo: No Authorization token in websocket

I have a fastify backend that runs graphql server. Without authentication hook at the server, my all graphql request and subscription in my vue app works fine.
Adding jwt authentication only graphql query and mutation works but my realtime subsciption keeps giving me unauthorized 401 error.
{"type":"UnauthorizedError","message":"No Authorization was found in request.headers","stack":"UnauthorizedError: No Authorization was found in request.headers\n at lookupToken
Theres an answer online saying to override wsclient with this code, but it does not resolve my issue.
wsClient.connectionParams = () => {
return {
headers: {
Authorization: localStorage.getItem(AUTH_TOKEN) ? `Bearer ${localStorage.getItem(AUTH_TOKEN)}` : ''
}
}
}
My Setup
vue-apollo.js
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client';
// Name of the localStorage item
const AUTH_TOKEN = 'apollo-token';
// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:3000/graphql';
// Config
const defaultOptions = {
// You can use `https` for secure connection (recommended in production)
httpEndpoint,
// You can use `wss` for secure connection (recommended in production)
// Use `null` to disable subscriptions
wsEndpoint: process.env.VUE_APP_GRAPHQL_WS || 'ws://localhost:3000/graphql',
// LocalStorage token
tokenName: AUTH_TOKEN,
// Enable Automatic Query persisting with Apollo Engine
persisting: false,
// Use websockets for everything (no HTTP)
// You need to pass a `wsEndpoint` for this to work
websocketsOnly: false,
// Is being rendered on the server?
ssr: false,
// Override default apollo link
// note: don't override httpLink here, specify httpLink options in the
// httpLinkOptions property of defaultOptions.
// link: myLink
// Override default cache
// cache: myCache
// Override the way the Authorization header is set
// getAuth: tokenName => {
// const token = 'Bearer ' + localStorage.getItem(AUTH_TOKEN);
// return token || '';
// },
// Additional ApolloClient options
// apollo: { ... }
// Client local data (see apollo-link-state)
// clientState: { resolvers: { ... }, defaults: { ... } }
};
// Call this in the Vue app file
export function createProvider(options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
//...options,
});
wsClient.connectionParams = () => {
return {
headers: {
authorization: localStorage.getItem('apollo-token'),
},
};
};
apolloClient.wsClient = wsClient;
// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
fetchPolicy: 'cache-and-network',
},
},
errorHandler(error) {
// eslint-disable-next-line no-console
console.log(
'%cError',
'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;',
error.message
);
},
});
return apolloProvider;
}
// Manually call this when user log in
export async function onLogin(apolloClient, token) {
if (typeof localStorage !== 'undefined' && token) {
localStorage.setItem(AUTH_TOKEN, token);
}
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient);
try {
await apolloClient.resetStore();
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (login)', 'color: orange;', e.message);
}
}
// Manually call this when user log out
export async function onLogout(apolloClient) {
if (typeof localStorage !== 'undefined') {
localStorage.removeItem(AUTH_TOKEN);
}
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient);
try {
await apolloClient.resetStore();
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (logout)', 'color: orange;', e.message);
}
{
console.log('%cError on cache reset (logout)', 'color: orange;', e.message);
}
}
// Install the vue plugin
Vue.use(VueApollo);
SERVER RESPONSES
When I console log the queries or mutations
{
host: 'localhost:3000',
connection: 'keep-alive',
'content-length': '205',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"',
accept: '*/*',
'content-type': 'application/json',
authorization: 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlJiQ2RxSzIwMm1URmx5NzY4X1VMWSJ9.eyJpc3MiOiJodHRwczovL2Rldi01M3c3NXJ3Yi5hdS5hdXRoMC5jb20vIiwic3ViIjoiYXV0aDB8NjI0NjQ0YWIyM2M0NjMwMDcwNTE0ZTY0IiwiYXVkIjpbImh0dHBzOi8vYXBpLmd1YXJkZXguY28ubnovIiwiaHR0cHM6Ly9kZXYtNTN3NzVyd2IuYXUuYXV0aDAuY29tL3VzIHByb2ZpbGUgZW1haWwifQ.e8OJDXmICjo582blGuCOCKtYOsrRcQrG8qp_ABcFqg6faMkw0xJNbJ3r0vvktBSjN9Kp93cIHlEr_ljSpZyb9roJbCShE7LXageX3wgvtq_nawQLOMTn-aC6AQqELjvgIEsuPb998hXioF0bXljdLnUy3Fi71cZby2uciqJes7iU2j5K2VuqGNLytNJc_RL9Cin7Z_7EE5J4_Djql1jtfN7boCrAMxjRIhs2EgQ_Y0DSr53WkdssBDcKfK1fk_cxksRXRFxhbKfrJSAWIdrGf6_hlVL8WdOYRNSlzi79_x0wNPGA0zzuEX458x0ffqXvBgnb7DUC_AsT2028e1tY',
'sec-ch-ua-mobile': '?0',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
'sec-ch-ua-platform': '"Windows"',
origin: 'http://localhost:8080',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
referer: 'http://localhost:8080/',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9'
}
When I console log the subscription headers I get:
{
host: 'localhost:3000',
connection: 'Upgrade',
pragma: 'no-cache',
'cache-control': 'no-cache',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
upgrade: 'websocket',
origin: 'http://localhost:8080',
'sec-websocket-version': '13',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
cookie: '_legacy_auth.SECRECT.is.authenticated=true; auth.is.authenticated=true',
'sec-websocket-key': 'iFNve3jfWvxmJRCsrEH+Og==',
'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',
'sec-websocket-protocol': 'graphql-ws'
}

Protected route returning malformed request.json() to React frontend

I am trying to make a fetch request to a protected route on my Express app. The route is working fine when the request is made with Postman, I get the response sent. But when I attempt a fetch request from the frontend, I get SyntaxError: Unexpected token A in JSON at position 0.
The fetch request from the frontend
export const getProtectedMessage = async (token) => {
const URI = 'http://localhost:3001'; //TODO save root uri to environment variable
const bearer = 'Bearer ' + token;
return fetch(`${URI}/me`, {
method: 'GET',
withCredentials: true,
credentials: 'include',
headers: {
'Authorization': bearer,
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
}
})
.then(handleErrors)
.then((res) => res.json())
.catch((err) => {
console.error(err);
});
};
Entry point for express app index.js
import dotenv from 'dotenv';
import express from 'express';
import morgan from 'morgan';
import cors from 'cors';
import routes from './routes/index.js';
import db from './db/index.js';
dotenv.config();
db(process.env.DB_NAME);
const PORT = process.env.PORT || 3001;
const app = express();
app.use(express.json());
app.use(cors({ credentials: true, origin: 'http://localhost:3000' }));
app.use(morgan('dev'));
app.use(routes);
app.get('*', (req, res) => {
res.sendStatus(404);
});
app.listen(process.env.PORT, () => {
console.log(`app listening on port ${PORT}`);
});
The routes
import express from 'express';
import * as auth from '../controllers/auth.js';
import expressjwt from 'express-jwt';
import dotenv from 'dotenv';
dotenv.config();
const router = express.Router();
const jwtCheck = expressjwt({
secret: process.env.SECRET_KEY,
algorithms: ['sha1', 'RS256', 'HS256']
});
router.post('/sign-up', auth.signUp);
router.post('/sign-in', auth.signIn);
router.get('/me', jwtCheck, auth.profile);
export default router;
The controller for the protected route
export function profile(req, res) {
res.status(200).send('A secret resource - only for authenticated users');
}
In the end the error was how I was sending the response. Maybe there's a more appropriate way to do it, but changing the controller to the following worked.
export function profile(req, res) {
// res.status(200).send('A secret resource - only for authenticated users'); // WRONG
res.status(200).json('Resource only for authenticated users'); // works!
}
One more thing, it's also good practice to check the application header in the fetch request. Sometimes, you don't know what you're getting from the backend could be json, could be text.
export const getProtectedMessage = async (token) => {
const URI = 'http://localhost:3001'; //TODO save root uri to environment variable
const bearer = 'Bearer ' + token;
return fetch(`${URI}/me`, {
method: 'GET',
withCredentials: true,
credentials: 'include',
headers: {
Authorization: bearer,
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
}
})
.then(handleErrors)
.then((res) => {
const contentType = res.headers.get('Content-Type').split(';')[0];
if (contentType === 'application/json') {
return res.json();
}
console.log(contentType);
if (contentType === 'text/html') {
return res.text();
}
})
.catch((err) => {
console.error(err);
});
};

Cookie not set and sent by Express Passport

index.js
const express = require('express')
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const MongoStore = connectMongo(session);
const app = express()
const sessionStore = new MongoStore({
mongooseConnection: connection,
collection: 'sessions'
})
app.use(session({
secret: 'mysecret',
resave: false,
saveUninitialized: true,
store: sessionStore,
cookie: {
maxAge: (1000 * 60)*10
},
httpOnly: true,
secure: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
console.log(user._id: " + user._id);
done(null, user._id)
})
const GoogleStrategy = passportGoogle.OAuth2Strategy
const strategy = app => {
const strategyOptions = {
clientID: GOOGLE_ID,
clientSecret: GOOGLE_SECRET,
callbackURL: `/auth/google/callback`
}
const verifyCallback = async (accessToken, refreshToken, profile, done) => {
let [err, user] = await to(getUserByProviderId(profile.id))
if (err || user) {
return done(err, user)
}
const [createdError, createdUser] = await to(
createUser({
provider: profile.provider,
...
})
)
return done(createdError, createdUser)
}
passport.use(new GoogleStrategy(strategyOptions, verifyCallback))
app.get('/auth/google/callback',
passport.authenticate('google'),
(req, res) => {
res.cookie('???what is default key set by PassportJS???', req.user._id);
res.redirect(303, `/loggedByGoogle.html`);
}
I succesfully
- logged in with Google,
- passport.serializeUser was invoked
- profile data was saved in MongoDB,
- redirection happened to `/loggedByGoogle.html`
but no cookies were sent to client
and there is no entry in "sessions" data store after login.
Do I have to explicitly set and send cookies myself?
Only after I added res.cookie('foo', 'bar');
ahead of
res.redirect(303, /loggedByGoogle.html);
I got some cookie on client side.
I though cookies were set internally by Passport after "serializeUser"
invocation.
Is there a default cookie name (key) set by Passport?
What am I missing?

Can't set a cookie w/ Nuxt.js, Express-Session

I'm new to NUXT and SSR and I've been researching this for a few hours now and I can't seem to figure it out. I'm using JWT to authenticate users in my Nuxt app with a Bearer Token, which is working great until I hit refresh and lose my session.
Now I'm looking to persist sessions using express-session and connect-mongo. I can't get the cookie to set on the client to be included on future requests.
When a user is authenticated:
router.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.status(401).send({success: false, msg: 'Authentication failed. User not found.'});
} else {
// check if password matches
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// if user is found and password is right create a token
var token = jwt.sign(user.toJSON(), config.secret, { expiresIn: 604800 });
req.session.authUser = { 'user': 'Test User' }
return res.json({success: true, token: token, user: user});
} else {
res.status(401).send({success: false, msg: 'Authentication failed. Wrong password.'});
}
});
}
The console.log above shows the authUser in the session.
Session {
cookie:
{ path: '/',
_expires: 2018-04-03T18:13:53.209Z,
originalMaxAge: 60000,
httpOnly: true },
authUser: { user: 'Test User' } }
When I look at my chrome devtools application cookies a connect.ssid hasn't been set and when I console.log(req.session) on future requests the authUser is missing.
My server code is:
// Passport
var passport = require('passport');
var passportJWT = require("passport-jwt");
var ExtractJwt = passportJWT.ExtractJwt;
var JwtStrategy = passportJWT.Strategy;
// Config File
let config = require('./config/settings.js')
// Initialize Express
var app = express();
// CORS-ENABLE
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://127.0.0.1:1337");
res.header("Access-Control-Allow-Credentials", 'true');
next();
});
app.use(cors())
const dbPath = 'mongodb://blogUser:blogUserPassword#localhost:27017/blog'
// Express Session
app.use(session({
secret: 'super-secret-key',
resave: false,
saveUninitialized: false,
store: new MongoStore({ url: dbPath }),
cookie: { maxAge: 60000 }
}))
// File Upload
app.use(fileUpload());
// view engine setup
// app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Routes
var index = require('./routes/index');
var users = require('./routes/users');
app.use('/api', index);
app.use('/users', users);
// Passport Config
app.use(passport.initialize());
app.use(passport.session())
// mongoose
const options = {
autoIndex: true, // Don't build indexes
reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
reconnectInterval: 500, // Reconnect every 500ms
poolSize: 10, // Maintain up to 10 socket connections
// If not connected, return errors immediately rather than waiting for reconnect
bufferMaxEntries: 0
};
console.log(options);
// Localhost Connect
mongoose.connect(dbPath, options).then(
() => { console.log("connected !!!"); },
err => { console.log(err); }
);
Any and all help is appreciated.
If you want to use the server you create the problem with the session is the express router, because change res and req vars so like recommend in nuxt use this.
const express = require('express');
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
const app = express()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
You are missing this step
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
// So we can use res.status() & res.json()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
The req, res parameters need to be interchanged on the client side
Once you do router.post('/login') and logout
app.use('/api', router)
And that will work perfectly

Why is my Jwt token not being extracted?

I'm new to passport JWT and have this code sending post request from client:
export function checkLogin() {
return function( dispatch ) {
//check if user has token
let token = localStorage.getItem('token');
if (token != undefined) {
//fetch user deets
var config = {
headers: {'authorization': token, 'content-type': 'application/json'}
};
axios.post(`${API_URL}/login`, null, config)
.then(response => {
console.log('response is:', response);
})
.catch((error)=> {
console.log(error);
});
}
//user is anonymous
}
and this request is sending off fine with token in headers like so:
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
authorization:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NzIzMTg2MDE5NTd9.SsCYqK09xokzGHEVFiHtHmq5_HvtWkb8EjQJzwR937M
Connection:keep-alive
Content-Length:0
Content-Type:application/json
DNT:1
Host:localhost:3090
Origin:http://localhost:8080
Referer:http://localhost:8080/
User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36
On the server side, it is correctly routed through passport and hits this code:
// setup options for JWT strategy
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
secretOrKey: config.secret,
ignoreExpiration: true
};
//create JWT strategy
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done ) {
console.log('payload:',payload); // --> payload: { iat: 1472318601957 }
// see if the user ID in the payload exists in our database
User.findById(payload.sub, function(err, user) {
if (err) {
console.log('auth error');
return done( err, false );
}
//if it does, call 'done' function with that user
if (user) {
console.log('user authd:', user);
done( null, user);
//otherwise, call 'done' function without a user object
} else {
console.log('unauthd user'); //--> gets here only
done( null, false);
}
});
});
The problem is that the extractJwt function only returns the iat portion and not the sub portion which I need to check the db. What am I doing wrong?
OK I figured it out. I examined how my token generator function was working. I'm using mongoose and it was passing the id of the user model to the token like this:
function tokenForUser(user) {
const timestamp = new Date().getTime();
//subject and issued at time
return jwt.encode({ sub: user.id, iat: timestamp }, config.secret);
}
I looked at the actual model that was being sent into this function and it turns out that mongoose adds an id with the key _id not id. Once I changed it to this everything works!
function tokenForUser(user) {
const timestamp = new Date().getTime();
//subject and issued at time
const userid = user['_id'];
return jwt.encode({ sub: userid, iat: timestamp }, config.secret);
}