I have a Vue app consuming Express API via Axios, trying to access an authenticated route. Including the Auth token in Postman Request header, the route yields the correct json response. However, from the Vue front end, it returns the error 404 unauthorized, no token found.
here are the request headers:
Request URL: http://localhost:8000/api/groups
Request Method: GET
Status Code: 401 Unauthorized
Remote Address: [::1]:8000
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 2587
Content-Security-Policy: default-src 'none'
Content-Type: text/html; charset=utf-8
Date: Sat, 14 Sep 2019 21:47:42 GMT
X-Content-Type-Options: nosniff
X-Powered-By: Express
Provisional headers are shown
Accept: application/json, text/plain, */*
Origin: http://localhost:8080
Referer: http://localhost:8080/groups
Sec-Fetch-Mode: cors
token: Token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InVzZXJ0d29AZ21haWwuY29tIiwiaWQiOjIsImV4cCI6MTU3MzY4NTI0NiwiaWF0IjoxNTY4NDk3NjQ2fQ.6zDOfTQzf4KW5ry4mJFaLXnUL7wAnHP_8W0B0JEW5DA
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Here is the response:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>UnauthorizedError: No authorization token was found<br> at middleware (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express-jwt/lib/index.js:76:21)<br> at Layer.handle [as handle_request] (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/layer.js:95:5)<br> at next (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/route.js:137:13)<br> at Route.dispatch (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/route.js:112:3)<br> at Layer.handle [as handle_request] (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/layer.js:95:5)<br> at /Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:281:22<br> at Function.process_params (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:335:12)<br> at next (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:275:10)<br> at Function.handle (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:174:3)<br> at router (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:47:12)<br> at Layer.handle [as handle_request] (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/layer.js:95:5)<br> at trim_prefix (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:317:13)<br> at /Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:284:7<br> at Function.process_params (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:335:12)<br> at next (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/express/lib/router/index.js:275:10)<br> at jsonParser (/Users/dariusgoore/development/writerboard/writerboard-express-api/node_modules/body-parser/lib/types/json.js:110:7)</pre>
</body>
</html>
Here is the Base config for Axios (the console.log statement retrieves the correct result):
import axios from 'axios'
const token = localStorage.getItem('token')
console.log('this is the token from localStorage ls', token)
export default () => {
return axios.create({
baseURL: process.env.VUE_APP_ROOT_API,
headers: {
'Content-Type': 'application/json',
token: token,
},
validateStatus: function () {
return true;
}
})
}
Here is my cors config in Express server:
const cors = require('cors');
const app = express()
...
var corsOptions = {
origin: '*',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));
Here is the authentication middleware that should retrieve the token (but per the console log is getting 'undefined'):
const jwt = require('express-jwt');
const getTokenFromHeaders = (req) => {
const { headers: { authorization } } = req;
console.log('this is the authorization token from the header: ', authorization);
if(authorization && authorization.split(' ')[0] === 'Token') {
return authorization.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;
here is the express route I am trying to secure:
const express = require('express');
const auth = require('../middlewares/authenticate');
const User = require('../models/User');
const knex = User.knex();
let router = express.Router();
router.get('/', auth.required, async (req, res) => {
console.log('this is the req.user from /groups', req.user);
const userId = req.user.id
let results = await knex.raw(`SELECT users.id, users.username, groups.id, groups.name FROM users JOIN memberships ON users.id = memberships.users_id JOIN groups ON memberships.groups_id = groups.id WHERE users.id = ${userId}`);
console.log(results);
res.json(results.rows);
});
Your server has this:
const { headers: { authorization } } = req;
That appears to be looking for a header called authorization.
Your request has this:
token: Token eyJhbGciOiJIUzI1...
due to this:
headers: {
'Content-Type': 'application/json',
token: token,
},
That header is called token, not authorization.
Related
I am using VueJS and Axios to send a request like this:
axiosAPI.get('/login/flows', {params: {id: 'string'}})
.then(res => {
console.log('cookie', res.headers)
}
In return server sends me this response:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Content-Type
Cache-Control: private, no-cache, no-store, must-revalidate
Content-Length: 646
Content-Type: application/json; charset=utf-8
Date: Thu,xxxx 13:56:21 GMT
Set-Cookie: csrf_token=Lxv3zynm1Fua0hU0/W1+R2w=; Path=/.ory/kratos/public/; Domain=x.y ; Max-Age=31536000; HttpOnly; SameSite=Lax
Vary: Origin
Vary: Cookie
As you can see, server sends a csrf-token in Set-Cookies. but when I try to print out the headers I can not get and store the csrf-token. In addition, browser doesn't store the token at all in the storage section.
I need to use the csrf-token inside of this cookie but I don't know how I can do this?
Note: i don't have any access to back-end codes.
Maybe you can use the axios-cookiejar-support.
https://www.npmjs.com/package/axios-cookiejar-support
A medium article showing how to use it.
https://medium.com/#adityasrivast/handling-cookies-with-axios-872790241a9b
Sample (getting cookie from a login page):
const axios = require('axios');
const wrapper = require('axios-cookiejar-support').wrapper;
const CookieJar = require('tough-cookie').CookieJar;
const jar = new CookieJar();
const client = wrapper(axios.create({ jar }));
const url = '<your url>';
const params = new URLSearchParams();
params.append('username', '<username>');
params.append('password', '<password>');
client.post(`${url}/Login`, params, {
headers: {
'Accept': '*/*'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Using this will get you the whole string for that header:
const cookieHeaders = res.headers['Set-Cookie'];
After that, you could split the string in an array with
cookieHeaders.split('; ');
In the array, you can then get the specific one you need.
app.use(
express.text({type: 'text/xml'}),
express.json({type: 'application/json'}),
other middlewares...) ```
Post method headers:
{
connection: 'keep-alive',
'content-length': '1082',
'content-encoding': 'gzip',
'content-type': 'text/xml',
accept: '/',
'accept-encoding': 'gzip',
origin: 'chrome-extension://sxwwwwagimdiliamlcqswqsw',
'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7'
}
Also I have tried express.raw with a wildcard for the type, but the response
is always 400.
```express.raw({type:'*/*', inflate:true}), (req, res, next)=>{console.log(req.body); next() },```
After finding this nodejs tutorial I was able to fix it.
The key is not to use any express parser, and use vanilla nodejs instead.
app.use(
(req, res, next)=>{
let data = []
req.on('data', chunk => {
data.push(chunk)
})
req.on('end', () => {
req.body = data.toString()
});
next()
}
)
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
};
It's strange, but I don't see some set-cookie headers from my express server in react-native app only on android devices!
I use express on my node.js server
const express = require('express');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const bodyParser = require('body-parser');
app.use(cookieParser());
app.use(session({
name: 'sid',
secret: CONSTANTS.SESSION_SECRET,
resave: false,
saveUninitialized: true
}));
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
const csrfProtection = csrf();
app.use(csrfProtection);
app.get('/', (request, response, next) => {
response.cookie('csrftoken', request.csrfToken());
response.status(200).send(...);
});
I just set csrftoken cookie and send some response;
On client i just get mail page as first:
fetch(URL_INDEX, {
method: 'GET',
headers: {
'User-Agent': USER_AGENT,
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
}
}).then((response) => {
//check response here <--------------------------------
processCookies(response);
return response;
})
What I see in debugger in fetch answer:
In IOS
Headers
map:Object
connection:"keep-alive"
content-length:"11716"
content-type:"text/html; charset=utf-8"
date:"Fri, 08 Feb 2019 10:58:25 GMT"
etag:"W/"2dc4-Ew7HRSMcOHECQlBtXIfAuuPw/Vs""
set-cookie:"csrftoken=bzKoDm5w-CdiLpGk0NWZ1TxaACRNJoRvVeng; Path=/, sid=s%3AGLvycaobuCL7xyIiNRBCXHSZU3SmHmAJ.vv9dzPurvicFvMb%2FkeB9Lshy6ZAQ0SvsMm8cR2ij9O8; Path=/; HttpOnly"
x-powered-by:"Express"
In Android
Headers
map: Object
connection:"keep-alive"
content-length:"11716"
content-type:"text/html; charset=utf-8"
date:"Fri, 08 Feb 2019 10:52:52 GMT"
etag:"W/"2dc4-Ew7HRSMcOHECQlBtXIfAuuPw/Vs""
set-cookie:"sid=s%3AOgeCB1Xsu8yA0SKiXF5Xgdc5pE-gSjfs.cDNHgzc41X1JmnUKmY6Hn3J2%2BrjxbXpBt7%2FhXwN6HbQ; Path=/; HttpOnly"
x-powered-by:"Express"
As you can see csrftoken cookie exists in response on ios, but we don't see it on android.
Any ideas?
I don't understand why express js cannot read my header.
I use vuejs + axios for send data.
I use a module for intercept request ans response for send token.
In axios module :
axios.interceptors.request.use((req) => {
req.headers.authorization = `Bearer: ${MYTOKEN}`;
return req;
});
In my server, i use nodesJS + Express with middleware :
const router = express.Router();
router.use(function timeLog(req, res, next) {
console.log(req.headers.authorization); // undefined :(
})
So req.headers do not contains key 'authorization' and console.log(req.headers.authorization); return to me 'UNDEFINED'.
I've try to put req.header.BLABLABLA. I find it but not as key.
I really don't understand.
Exemple of return with authorization :
{ host: 'localhost:5000',
connection: 'keep-alive',
'access-control-request-method': 'POST',
origin: 'http://localhost:8080',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
'access-control-request-headers': 'authorization,content-type',
accept: '*/*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'fr-FR,fr;q=0.9,en;q=0.8,en-US;q=0.7,ru;q=0.6'
}
try this
axios.interceptors.request.use((req) => {
// req here, it's axios config, not express req'. req.headers.Authorization = Bearer: ${MYTOKEN}`;
return req;
});
Authorization => start with A Capital
You're using Axios in the wrong way.
You're trying to log headers of the Express Request, not headers of the Axios.
// server/index.js
router.use(function timeLog(req, res, next) {
console.log(req.headers.authorization); // of course this is will undefined
})
If you're doing like so, you'll get your authorization headers...
// server/index.js
import axios from 'axios'
axios.interceptors.request.use((req) => {
// `req` here, it's axios config, not express `req'.
req.headers.authorization = `Bearer: ${MYTOKEN}`;
return req;
});
router.use(function timeLog(req, res, next) {
console.log(axios.headers.authorization); // here we are
})