У меня есть приложение, использующее VueJS на внешнем интерфейсе и Express / knex / postgres для внутреннего. Я использую passport, local и local_port для управления аутентификацией jwt.
Все эти стратегии для аутентификации работают, однако токен заголовка не сразу доступен в localStorage. В результате аутентифицированные ресурсы отображаются неправильно. В частности, ошибки отображения отображаются при входе в систему Twitter.
При обновлении в обоих случаях появляется маркер заголовка, и приложение ведет себя так, как ожидается.
Вот мой Api.js, который формируетоснова всех вызовов axios:
import axios from 'axios'
const token = localStorage.getItem('token')
export default () => {
return axios.create({
baseURL: process.env.VUE_APP_ROOT_API,
headers: {
'Content-Type': 'application/json',
token: token,
},
validateStatus: function () {
return true;
}
})
}
мы устанавливаем localStorage.token в store.js:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
import PostsService from './services/PostsService'
import ReportsService from './services/ReportsService'
import KudosService from './services/KudosService'
import ActsService from './services/ActsService'
// import SubscriptionsService from './services/SubscriptionsService'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
status: '',
user: JSON.parse(localStorage.getItem('user')),
token: localStorage.getItem('token') || '',
posts: null,
leaderboard: [],
currentGroup: {},
lastAct: '',
userActs: []
},
mutations: {
auth_request (state) {
state.status = 'Signing in...'
},
set_user (state, user) {
state.user = user
localStorage.setItem('user', JSON.stringify(user))
console.log('store sets user as: ', user)
},
auth_success (state) {
state.status = 'success'
},
auth_error (state) {
state.status = 'Invalid credentials'
},
logout (state) {
state.status = ''
state.user = null
localStorage.removeItem('user')
},
setPosts(state, posts) {
state.posts = posts;
},
setLeaderboard(state, leaderboard) {
state.leaderboard = leaderboard
},
setCurrentGroup(state, group) {
state.currentGroup = group
},
setKudos(state, kudos) {
state.kudos = kudos;
},
setLastAct(state, lastAct) {
state.lastAct = lastAct;
console.log('store sets this last act: ', lastAct)
},
setUserActs(state, userActs) {
state.userActs = userActs
}
},
actions: {
register ({ commit }, user) {
return new Promise((resolve, reject) => {
commit('auth_request')
console.log(process.env.VUE_APP_ROOT_API)
axios({ url: process.env.VUE_APP_ROOT_API + '/auth', data: user, method: 'POST' })
.then(async resp => {
const token = "Token " + resp.data.user.token
const user = resp.data.user
localStorage.setItem('token', token)
axios.defaults.headers.common['token'] = token
commit('auth_success')
commit('set_user', user)
resolve(resp)
})
.catch(err => {
commit('auth_error', err)
localStorage.removeItem('token')
reject(err)
})
})
},
login ({ commit }, user) {
return new Promise((resolve, reject) => {
commit('auth_request')
console.log(user);
axios({ url: process.env.VUE_APP_ROOT_API + '/auth/login', data: user, method: 'POST' })
.then(resp => {
const token = "Token " + resp.data.user.token
const user = resp.data.user
localStorage.setItem('token', token)
axios.defaults.headers.common['token'] = token
// console.log(user)
// console.log(resp)
commit('auth_success')
commit('set_user', user)
resolve(resp)
})
.catch(err => {
commit('auth_error')
commit('logout')
reject(err)
})
})
},
logout ({ commit }) {
return new Promise((resolve) => {
commit('logout')
localStorage.removeItem('token')
delete axios.defaults.headers.common['token']
resolve()
})
},
getPosts({ commit }) {
PostsService.fetchGroupPosts()
.then(resp => {
console.log('this is groupPosts: ', resp);
commit('setPosts', resp.data);
});
},
updateLeaderboard({ commit }, group) {
ReportsService.getLeaderboard(group.id)
.then(resp => {
commit('setLeaderboard', resp);
});
},
getKudos({ commit }) {
KudosService.fetchKudos()
.then(resp => {
commit('setKudos', resp.data);
});
},
async getLastAct({ commit }) {
await ActsService.fetchLastAct()
.then(resp => {
console.log('this is the last act: ', resp);
commit('setLastAct', resp.data[0]);
});
},
async getUserActs({ commit }) {
ActsService.fetchCurrentUserActs()
.then(resp => {
commit('setUserActs', resp.data);
});
},
setUser({ commit }, user ) {
const token = "Token " + user.token
localStorage.setItem('token', token)
axios.defaults.headers.common['token'] = token
commit('set_user', user )
}
},
getters: {
isAuthenticated: state => !!state.user,
authStatus: state => state.status,
user: state => state.user,
token: state => state.token,
posts: state => {
return state.posts;
},
kudos: state => {
return state.kudos;
},
leaderboard: state => {
return state.leaderboard;
},
currentGroup: state => {
return state.currentGroup;
},
lastAct: state => {
return state.lastAct;
},
userActs: state => {
return state.userActs
}
}
})
вот конфигурация паспорта со стороны экспресса:
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) {
console.log('profile is: ', profile._json.id_str)
let user = await User.query().findOne({twitter_id: profile._json.id_str})
console.log('retrieved user')
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);
}
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
и мой файл маршрутов:
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('/currentUser', auth.required, async (req, res) => {
const userId = req.user.id;
console.log(req.user);
const user = await User.query().where('id', userId );
if (user && user.length === 1) {
res.json({
user: {
id: user[0].id,
email: user[0].email,
username: user[0].username
}
});
} else {
res.status(404).json({});
}
});
//POST new user route (optional, everyone has access)
router.post('/', auth.optional, async (req, res, next) => {
const { body: { user } } = req;
console.log("this is the request.body sent to the post route: ", req.body);
if (!user.email) {
return res.status(422).json({
errors: {
email: 'is required',
},
});
}
if (!user.password) {
return res.status(422).json({
errors: {
password: 'is required',
},
});
}
const finalUser = await authHelpers.createUser(req, res);
console.log("The finalUser from createUser is " + finalUser);
res.json({ user: authHelpers.toAuthJSON(finalUser) });
});
//POST login route (optional, everyone has access)
router.post('/login', auth.optional, (req, res, next) => {
const { body: { user } } = req;
if (!user.email) {
return res.status(422).json({
errors: {
email: 'is required',
},
});
}
if (!user.password) {
return res.status(422).json({
errors: {
password: 'is required',
},
});
}
return passport.authenticate('local', { session: false }, (err, passportUser, info) => {
if (err) {
return next(err);
}
if (passportUser) { // user successfully authenticated
return res.json({ user: authHelpers.toAuthJSON(passportUser) });
} else { // user failed to authenticate
err = new Error('Incorrect credentials');
err.status = 401;
next(err);
}
})(req, res, next);
});
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(authHelpers.toAuthJSON(req.user));
console.log('hitting the callback route sending stringified user: ', user);
res.redirect(process.env.VUE_HOME_URL + '?user=' + user);
});
module.exports = router;