My app is blocked using Google Strategy with PassportJs and MERN - node.js

i develop an app using MERN stack and PassportJs for authentication. I used Google strategy to allow user authentication but i'm faced issues.
Here are the errors that appear in the console:
Access to fetch at 'https://tagsite.herokuapp.com/users/google' from origin 'https://tagsite.netlify.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
GET https://tagsite.herokuapp.com/users/google net::ERR_FAILED
How can i fix it? Some help please.
I already use cors package in backend, but still have same errors.
Here is my code.
Backend
app.js
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const passport = require('passport');
const serverless = require('serverless-http');
if (process.env.NODE_END !== 'production') {
// Load environment variables from .env file for non production environment
require('dotenv').config()
}
// Connect database
require('./utils/connectdb')
// Strategies
require('./strategies/GoogleStrategy')
require('./utils/authenticate')
// Routes import
const userRoutes = require('./routes/user')
const app = express();
app.use(bodyParser.json())
app.use(cookieParser(process.env.COOKIE_SECRET))
app.use(cors())
app.use(passport.initialize())
// Use routes
app.use('/users', userRoutes)
app.get('/', (req, res) => {
res.send({ status: 'success' })
})
// Server set up
let port;
if (process.env.PORT) {
port = process.env.PORT
} else {
port = 8000
}
app.listen(port, () => {
console.log('Listen on port: ' + port)
})
module.exports.handler = serverless(app)
GoogleStrategy.js
const passport = require('passport')
const googleStrategy = require('passport-google-oauth20').Strategy
const User = require('../models/user')
const slug = require('slug')
require('dotenv').config()
// serialize & deserialize user
passport.serializeUser((user, done) => {
done(null, user.id)
// done(null, user)
})
passport.deserializeUser((id, done) => {
User.findById(id)
.then((user) => {
done(null, user)
})
// done(null, user)
})
passport.use(
new googleStrategy(
{
clientID: process.env.GOOGLE_APP_ID,
clientSecret: process.env.GOOGLE_APP_SECRET,
callbackURL: process.env.GOOGLE_APP_CALLBACK_URL
},
(accessToken, refreshToken, profile, done) => {
User.findOne({ providerId: profile.id })
.then((user) => {
if (user) {
const userData = {
username: profile.displayName,
provider: profile.provider,
providerId: profile.id,
email: profile.emails[0].value,
avatar: profile._json.picture
&& profile._json.picture,
token: accessToken,
slug: slug(profile.displayName)
}
user.refreshToken.push({ refreshToken: accessToken })
user.save()
done(null, userData)
} else {
new User({
username: profile.displayName,
provider: profile.provider,
providerId: profile.id,
email: profile.emails[0].value,
avatar: profile._json.picture
&& profile._json.picture,
$addToSet: { refreshToken: accessToken },
slug: slug(profile.displayName)
})
.save()
.then((user) => {
const userData = {
username: profile.displayName,
provider: profile.provider,
providerId: profile.id,
email: profile.emails[0].value,
avatar: profile._json.picture
&& profile._json.picture,
token: accessToken,
slug: slug(profile.displayName)
}
user.refreshToken.push({ refreshToken: accessToken })
user.save()
done(null, userData)
})
.catch((error) => {
console.log(`create new user failed: ${error}`)
})
}
})
}
)
)
user.js (controllers)
const passport = require('passport');
module.exports = {
googleLogin: [
passport.authenticate('google', { scope: ['profile', 'email'] })
],
googleLoginCallback : [
passport.authenticate('google', {
failureRedirect: process.env.AUTH_FAILURE_REDIRECT_URL,
session: false
}),
(req, res, next) => {
const token = req.user.token
// res.status(200).json({userData: req.user})
res.cookie('refreshToken', req.user.token, COOKIE_OPTIONS)
res.send({ success: true, token })
}
],
user.js (routes)
const router = require('express').Router()
// Controllers
const {
googleLogin, googleLoginCallback
} = require('../controllers/user')
router.get('/google', googleLogin)
router.get('/google/callback', googleLoginCallback)
module.exports = router
Frontend
google.js (component)
import { Button, Callout } from '#blueprintjs/core'
import React, { useContext, useState } from 'react'
import { UserContext } from '../../utils/context/UserContext'
const GoogleLogin = () => {
const [error, setError] = useState(null)
const [userContext, setUserContext] = useContext(UserContext)
const loginGoogle = () => {
setError(null)
const genericErrorMessage = 'Something error'
fetch(`${process.env.REACT_APP_API_URL}/users/google`, {
method: 'GET',
// credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
})
.then(async (res) => {
if (!res.ok) {
if (res.status === 400) {
setError('Please try again')
} else if (res.status === 401) {
setError('Unauthorized')
} else {
setError(genericErrorMessage)
}
} else {
const data = await res.json()
setUserContext((oldValues) => {
return { ...oldValues, token: data.token }
})
}
})
.catch((err) => {
setError(genericErrorMessage)
})
}
return (
<>
{error && <Callout intent="danger">{error}</Callout>}
<Button text="login with google" onClick={loginGoogle} />
</>
)
}
export default GoogleLogin

app.use(
cors({
origin: [
"https://tagsite.netlify.app"
],
credentials: true,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
})
);
Like your error is saying, CORS doesn't like your Front End Code. It doesnt know it. You have to introduce your Front End to your Backend, and tell your Backend to let certain origins access your server.
I'm not experienced enough on whether you have to include credentials or not, but it works for me!

Related

Client doesnt get cookie from my node.js api

I have made an authentication with jwt api in node.js/express and i run it on heroku.
When a user logged in, server create a cookie via cookie-parser and send it to the client.
Below is the code from server.js
const express = require('express');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const path = require('path');
const bcrypt = require('bcrypt');
const PORT = process.env.PORT || 3000;
const serveStatic = require('serve-static');
require('dotenv').config();
const mongoose = require('mongoose');
const { User, Posts } = require(path.join(__dirname, './model.js'));
const mongoString = process.env.DATABASE_URL;
const JWT_SECRET = process.env.JWT_SECRET;
const { verifyToken, checkUser } = require(path.join(__dirname, './auth.js'));
const app = express();
//Middlewares
app.use(
cors({
credentials: true,
origin: true,
})
);
app.use(express.json());
app.use(cookieParser());
//Connect to Database
mongoose.connect(mongoString);
const db = mongoose.connection;
db.on('error', (err) => {
console.log(err);
});
db.once('connected', () => {
console.log('----Database Connected----\n');
});
//functions
const maxAge = 3 * 24 * 60 * 60;
const createToken = (id) => {
return jwt.sign({ id }, JWT_SECRET, {
expiresIn: maxAge,
});
};
// AUTH ROUTES
app.get('*', checkUser);
app.get('/', checkUser, (req, res) => {
res.json({ status: 'success' });
});
app.post('/api/register', async (req, res) => {
const salt = await bcrypt.genSalt();
try {
const user = await User.create(
new User({
username: req.body.username,
email: req.body.email,
city: req.body.city,
password: await bcrypt.hash(req.body.password, salt),
})
);
const token = createToken(user._id);
res.cookie('jwt', token, {
maxAge: maxAge * 1000,
secure: true,
});
res.status(201).json(user);
console.log(user);
} catch (err) {
console.log(err);
res.json(err);
}
});
app.post('/api/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email }).lean();
if (!user) {
return res.send({
status: 'error',
error: 'Invalid email',
});
}
if (await bcrypt.compare(password, user.password)) {
const token = createToken(user._id);
res.cookie('jwt', token, { secure: true, maxAge: maxAge * 1000 });
res.status(200).send({ status: 'ok', token: token });
console.log(user._id + ' logged in successfully');
return;
}
return res.send({ status: 'error', error: 'Invalid password' });
} catch (err) {
console.log(err);
}
});
app.get('/api/home', verifyToken, (req, res) => {
res.send(res.locals.user);
});
app.get('/api/logout', (req, res) => {
try {
res.cookie('jwt', '', { maxAge: 1 });
res.status(200).send({ status: 'ok' });
} catch (err) {
res.send(err);
}
});
//POSTS ROUTES
app.post('/api/posts', verifyToken, checkUser, async (req, res) => {
try {
const post = await Posts.create(
new Posts({
postBody: req.body.postBody,
city: req.body.city,
author: res.locals.user.id,
})
);
res.status(200).json(post);
console.log('====New Post=====');
} catch (err) {
res.status(400).send({ message: err.message });
}
});
app.get('/api/posts', verifyToken, async (req, res) => {
try {
const data = await Posts.find();
res.send({ user: res.locals.user, data: data });
} catch (err) {
res.json({ message: err.message });
}
});
app.get('/api/posts/:city', verifyToken, async (req, res) => {
try {
const data = await Posts.find({ city: req.params.city });
res.json(data);
res.send(res.locals.user);
} catch (err) {
res.json({ message: err.message });
}
});
//run server
app.listen(PORT, () => {
console.log(`Server running on ${PORT}...\n`);
});
Now, for front-end i use Vue.js that its running on Firebase.
Here is the script part of Login.Vue
<script>
/* eslint-disable */
import axios from 'axios';
export default {
name: 'Login',
data() {
return {
email: '',
password: '',
error: '',
};
},
methods: {
async onSubmit() {
if (!this.email || this.password.length < 6) {
this.error = 'vale kati';
return;
}
await axios
.post(
'https://thelostpet.herokuapp.com/api/login',
{
email: this.email,
password: this.password,
},
{ withCredentials: true }
)
.then((res) => {
console.log(res.data.token);
if (res.data.status == 'error') {
this.error = res.data.error;
}
if (res.data.status == 'ok') {
this.$router.push('/home');
}
})
.catch((err) => {
console.log(err);
});
},
},
};
</script>
When I try to login from Login.vue, that its ruuning on Firebase, the browser doesn't save the cookie that it created from the api.
BUT when I make a post request on https://thelostpet.herokuapp.com/api/login from postman, the cookie is saved on postman.
Thank you!

I am getting 404 error and 204 error when consuming backend (node,vuejs)?

When signing in with postman everything works fine. But when i am doing an axios request i get 404 error and directly after 204 error. When i render my vue.js page i get "cannot get api/auth/signin. Also I get a message somewhere that says user not found.
What i have tried:
Frontend: I tried with adding headers to my axios request. I console logged the data and it seems perfectly fine.
Backend: Changed deprecated body parsers.
Frontend Code:
Auth store
import axios from "axios";
const state = {
token: "",
users: [],
};
const getters = {};
const actions = {
async signIn(_, payload) {
const response = await axios.post(
"http://localhost:3000/api/auth/signin",
{ payload },
{
headers: {
"Content-Type": "application/json",
},
}
);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
},
};
const mutations = {};
export default {
state,
getters,
actions,
mutations,
};
This is my backend:
Controller
//signin
exports.signin = (req, res) => {
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
var passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Invalid Password!",
});
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400, // 24 hours
});
var authorities = [];
for (let i = 0; i < user.roles.length; i++) {
authorities.push("ROLE_" + user.roles[i].name.toUpperCase());
}
res.status(200).send({
id: user._id,
username: user.username,
email: user.email,
roles: authorities,
accessToken: token,
});
});
};
Route
module.exports = function (app) {
app.use(function (req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.post(
"/api/auth/signup",
[
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExisted,
],
controller.signup
);
app.post("/api/auth/signin", controller.signin);
And my server
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const Quote = require("./models/Quote");
const quoteRoute = require("./routes/quoteRoute");
const quoteController = require("../Maxico/controllers/quoteController");
const config = require("./config/config");
const verifySignup = require("./middlewares/verifySignUp");
const Role = require("./models/Role");
const app = express();
//Import routes
//const authRoute = require("./routes/auth");
var corsOptions = {
origin: "http://localhost:8080/?#/",
};
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: true }));
app.use(express.json()); //
const db = require("./models/Quote");
mongoose
.connect(
"url",
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
}
)
.then(() => {
console.log("Connected to the database!");
})
.catch((err) => {
console.log("Cannot connect to the database!", err);
process.exit();
});
app.use(express.json());
app.get("/", (req, res) => {
res.send("Welcome to homepage");
});
app.use("/quote", quoteRoute);
require("./routes/authRoute")(app);
//require("./routes/userRoute")(app);
// initial roles
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
new Role({
name: "moderator",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'moderator' to roles collection");
});
new Role({
name: "admin",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'admin' to roles collection");
});
new Role({
name: "superadmin",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'superadmin' to roles collection");
});
}
});
// set port, listen for requests
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
In my network tab the request pay load got sent like this:
{payload: {username: "jon", password: "password"}}
payload: {username: "jon", password: "password"}
But my postman only accepts this:
{username: "jon", password: "password"}
So in my action i sent like this:
const actions = {
async signIn(_, payload) {
console.log(payload);
const response = await axios.post(
"http://localhost:3000/api/auth/signin",
payload,
{
headers: {
"Content-Type": "application/json",
},
}
);
console.log(payload);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
},
};

Where did I go wrong with passport-jwt setup?

I tried to add passport-jwt to my MEVN-stack application, login is successful, but when app tries to redirect to home page after login, I get 401 Unauthorized error in console. I pass token as value of Authorization header in get request on the home page, but it did not help.
This is the code of server.js (the entrypoint of server side):
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
const morgan = require('morgan');
const fs = require('fs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const passportJWT = require('passport-jwt');
const ExtractJWT = passportJWT.ExtractJwt;
const JWTStrategy = passportJWT.Strategy;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJWT.fromAuthHeaderWithScheme('jwt');
jwtOptions.secretOrKey = 'movieratingapplicationsecretkey';
const app = express();
const router = express.Router();
const User = require('./models/User');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
passport.use(new JWTStrategy(jwtOptions, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.id }, (err, user) => {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
mongoose.connect('mongodb://localhost/movie_rating_app', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('Connection is established');
})
.catch((err) => {
console.error(`App starting error: ${err.stack}`);
});
// include controllers
fs.readdirSync('controllers').forEach(file => {
if (file.substr(-3) === '.js') {
const route = require(`./controllers/${file}`)
route.controller(app)
}
})
router.get('/', (req, res) => {
res.json({ message: 'API was initialized!' });
});
const port = process.env.API_PORT || 8081;
app.use('/', router);
app.listen(port, () => {
console.log(`api running on port ${port}`);
});
This is the movies.js controllerm, which contains passport.authenticate() method in get request:
const Movie = require('../models/Movie')
const Rating = require('../models/Rating')
const passport = require('passport')
module.exports.controller = app => {
// fetch all movies
app.get(
'/movies',
passport.authenticate('jwt', { session: false }),
(req, res) => {
Movie.find({}, 'name description release_year genre', (error, movies) => {
if (error) console.error(error)
res.send(movies);
})
})
// fetch a single movie
app.get('/movies/:id', (req, res) => {
Movie.findById(req.params.id, 'name description release_year genre', (error, movie) => {
if (error) console.error(error)
res.send(movie);
})
})
}
This is the users.js controller:
const User = require('../models/User');
const passportJWT = require('passport-jwt');
const jwt = require('jsonwebtoken');
const ExtractJwt = passportJWT.ExtractJwt;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');
jwtOptions.secretOrKey = 'thisisthesecretkey';
module.exports.controller = app => {
// register a user
app.post('/users/register', (req, res) => {
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
})
User.createUser(newUser, (error, user) => {
if (error) {
res.status(422).json({
message: 'Something went wrong. Please try again after some time'
})
}
res.send({ user });
})
})
// login user
app.post('/users/login', (req, res) => {
if (req.body.email && req.body.password) {
const email = req.body.email,
password = req.body.password;
User.getUserByEmail(email, (err, user) => {
if (!user) {
res.status(404).json({ message: 'The user does not exist' })
} else {
User.comparePassword(password, user.password, (error, isMatch) => {
if (error) throw error;
if (isMatch) {
const payload = { id: user.id };
const token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ message: 'ok', token })
} else {
res.status(401).json({ message: 'The password is incorrect' })
}
})
}
})
}
})
}
This is the script part of Home.vue, when I try to receive response:
<script>
import axios from 'axios';
import MovieCard from '#/components/MovieCard.vue';
export default {
name: 'Home',
components: {
MovieCard,
},
data: () => ({
movies: [],
}),
mounted() {
this.fetchMovies();
},
methods: {
async fetchMovies() {
const token = window.localStorage.getItem('auth');
return axios({
method: 'get',
url: 'http://localhost:8081/movies',
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
})
.then((response) => {
console.log(response);
});
// return axios.get('http://localhost:8081/movies')
// .then(({ data }) => {
// this.movies = data;
// })
// .catch((error) => {
// console.error(error);
// });
},
},
};
</script>
Please don't mark my question as duplicate, because I have already tried some advices such as change ExtractJWT.fromAuthHeaderWithScheme('jwt') to ExtractJWT.fromAuthHeaderWithScheme('bearer'), but it did not help me.
How to fix 401 error?
I replaced all ExtractJwt.fromAuthHeaderWithScheme('jwt') with ExtractJwt.fromAuthHeaderAsBearerToken(), and set all jwtOptions.secretOrKey values as 'thisisthesecretkey'. 401 error does not exist now

passport-jwt in MEAN-Stack returning Internal Server Error 500

Im using passport.js JWT strategy to authenticate my MEAN-Stack app. The unsecured routes work properly but i cant get the secured routes to work. they allways return Internal Server Error 500 even though im sticking to the docs. Here is the code:
im initializing in index.js before applying routes:
server.use(passport.initialize());
My passport.js setup file:
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config/db');
module.exports = function(passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
};
My Route that doesnt work:
const express = require('express');
const router = express.Router();
const config = require('../config/db');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
require('../config/passport');
const passport = require('passport');
router.get('/profile', passport.authenticate('jwt', {session: false}), function(req,
res){
res.json(user);
});
module.exports = router;
my token is setup properly since i can decode it manually.
How im calling this route from angular (i know that i actually dont need the userId parameter for the call itself):
public getProfile(userId) : any{
let httpOptions = {
headers: new HttpHeaders({ 'Authorization': `Bearer ${this.token}` })
};
this.http.get('http://localhost:8080/api/v1/profile', httpOptions).subscribe(res => {
console.log('got the profile for user with id: ' + userId + '=> ' + res);
return res;
}, err => {
console.log(err);
});
}
Any help is appreciated. Thanks in advance for your time!
EDIT: Mongoose logs
Mongoose: users.findOne({ _id: ObjectId("5bcf1218cace7d1168a23672") }, {
projection: {} })
GET /api/v1/profile 500 4.579 ms - 5
OPTIONS /api/v1/profile 204 0.097 ms - 0
{ _id: '5bcf1218cace7d1168a23672',
email: '12#email.com',
password: '$2a$10$z8li41jQMESsmbIyQUsPfO6VkYjOyO/ybj4lW04VGUkJmlShydBN.',
name: '12',
age: 12,
gender: 'male',
description: '12',
question: '12',
__v: 0,
iat: 1540419498 }
Mongoose: users.findOne({ _id: ObjectId("5bcf1218cace7d1168a23672") }, {
projection: {} })
GET /api/v1/profile 500 3.995 ms - 5
I have added single-line comments to hit spots, verify the spots and comment accordingly to further debug. Confirm /api/v1/ is an express root route.
passport.js
const JwtStrategy = require('passport-jwt').Strategy,
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config/db');
module.exports = function(passport) {
let opts = {
secretOrKey: config.secret,
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
issuer: 'TODO', // configure issuer
audience: 'TODO' // configure audience
};
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload); // confirm _id is defined
User.findOne({ _id: jwt_payload._id }, function(err, user) {
if (err) {
return done(err, false);
}
if (!user) {
return done(null, false);
}
return done(null, user);
});
}));
};
route.js
const express = require('express');
const passport = require('passport');
const jwt = require('jsonwebtoken');
const config = require('../config/db');
const User = require('../models/user');
const router = express.Router();
require('../config/passport');
router.get('/profile', passport.authenticate('jwt', { session: false }), function(req, res) {
res.json(req.user); // use req.user not user
});
module.exports = router;
Had the same issue. Seems like the way you are returning the done(null, user) callback needs to be modified. Try this.
User.findOne({ _id: jwt_payload._id }, function(err, user) {
if (err) {
return done(err, false);
}
if (!user) {
return done(null, false);
}
// Your code should process further if you get user. Returning
// here will terminate your program early. Hence, protected
// route is not accessed.
done(null, user);
});

Node.js/Passport Twitter authorization returns 302

I'm doing a POST request to authorize Twitter via Passport Twitter strategy. The request is going on fine if I use Postman but it fails with if I use it in my app. I get a 302 in response and this in browser console
Error: Network Error
Stack trace:
createError#webpack-internal:///99:16:15
handleError#webpack-internal:///96:87:14
From what I read on the net I found that 302 would be because of some bad implementation of the a redirect call? Maybe is it that twitter does when authorizing a application and I'm not handling it correctly?
The code is as follows. Client-side using Vuejs with Axios to send a request.
Vue template:
<template>
<card>
<h4 slot="header" class="card-title">Edit linked accounts</h4>
<form>
<button type="submit" class="btn btn-info btn-fill" #click.prevent="authTwitter">
Link Facebook
</button>
<div class="clearfix"></div>
</form>
</card>
</template>
<script>
import Card from 'src/components/UIComponents/Cards/Card.vue'
import controller from '../../../../../src/controller/AuthController'
export default {
components: {
Card
},
data () {
return {
}
},
methods: {
authTwitter () {
let token = this.$session.get('jwt')
controller.http.post('/auth/twitter',{}, {
headers: {
Authorization: 'Bearer ' + token
}
})
.then(function(response) {
console.log(response)
})
.catch(function(error) {
console.log(error);
})
}
}
}
</script>
<style>
</style>
Axios settings:
const axios = require('axios');
const config = require('../../config/index.js')
let http = axios.create({
baseURL: config.url,
'Content-Type' : 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin':'*'
})
module.exports = {
http
}
And in my backend I'm doing the following:
Passport settings:
const passport = require('passport');
const StrategyTwitter = require('passport-twitter').Strategy,
LocalStrategy = require('passport-local').Strategy;
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const {
User,
UserDetails,
UserAccounts
} = require('../models');
const bcrypt = require('bcrypt-nodejs');
const jwt = require('jsonwebtoken');
const config = require('../config/config');
let session = require('express-session')
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secretKey;
module.exports = function(app) {
// Local sign-in stragetgy
passport.use('local-signin', new LocalStrategy(
function(username, password, done) {
User.findOne({
where: {
username: username
}
}).then(function(data) {
bcrypt.compare(password, data.password, function(err, response) {
if (err) {
return done(err);
}
const token = jwt.sign({
id: data.id
}, config.secretKey, {
expiresIn: 86400 // 86400 expires in 24 hours
});
return done(null, token);
});
}).catch(function(error) {
return done(error)
});
}
));
// Local sign-up strategy
passport.use('local-signup', new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, username, password, done) {
process.nextTick(function() {
User.beforeCreate(function(req) {
return encryptPass(req.password)
.then(success => {
req.password = success;
})
.catch(err => {
if (err) console.log(err);
});
});
User.create({
username: username,
password: password
}).then(function(data) {
UserDetails.create({
username: req.body.username,
name: req.body.name,
dob: req.body.dob,
phone: req.body.phone,
gender: req.body.gender,
address: req.body.address,
country: req.body.country,
}).then(function(data) {
UserAccounts.create({
username: username
}).then(function(data) {
return done(null, data);
}).catch(function(error) {
return done(error.message);
});
}).catch(function(error) {
return done(error.message);
});
}).catch(function(error) {
return done(error.message);
});
})
}
));
// Passport Jwt strategy
passport.use('jwt', new JwtStrategy(opts, function(jwt_payload, done) {
console.log('jwt', jwt_payload);
UserDetails.findOne({
where: {
id: jwt_payload.id
}
})
.then(function(user) {
return done(null, user.id);
})
.catch(function(err) {
return done(err, false);
});
}));
// Use sessions for twitterStrategy Oauth1 authorizations.
passport.serializeUser(function(user, cb) {
console.log('user', user)
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
console.log('obj', obj)
cb(null, obj);
});
// Configure the Twitter strategy for use by Passport.
//
// OAuth 1.0-based strategies require a `verify` function which receives the
// credentials (`token` and `tokenSecret`) for accessing the Twitter API on the
// user's behalf, along with the user's profile. The function must invoke `cb`
// with a user object, which will be set at `req.user` in route handlers after
// authentication.
passport.use('twitter-authz', new StrategyTwitter({
consumerKey: config.keys.twitter.consumerKey,
consumerSecret: config.keys.twitter.consumerSecret,
callbackURL: process.env.CALLBACK_URL_TWITTER || 'http://120.0.0.1:8000/#/dashboard/user'
},
function(token, tokenSecret, profile, cb) {
process.nextTick(function() {
// In this example, the user's Twitter profile is supplied as the user
// record. In a production-quality application, the Twitter profile should
// be associated with a user record in the application's database, which
// allows for account linking and authentication with other identity
// providers.
console.log(token, tokenSecret, profile);
return cb(null, profile);
})
}));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
// Session for twitterStrategy
app.use(session({
secret: config.secretKey,
resave: false,
saveUninitialized: false
}));
}
function encryptPass(pass) {
return new Promise((resolve, reject) => {
bcrypt.hash(pass, null, null, function(err, hash) {
if (err) {
return reject(err);
};
return resolve(hash);
})
})
}
And my router settings:
const AuthControllerPolicy = require('./controllers/policies/AuthControllerPolicy.js')
const AuthController = require('./controllers/AuthController.js')
const passport = require('passport');
module.exports = (app) => {
app.post('/auth/twitter',
passport.authenticate('jwt', { session: false }),
passport.authorize('twitter-authz', { session: false }),
AuthController.authTwitter)
}
Answer found here: Axios and twitter API
Looks like Twitter would not allow CORS request and it must be done only from the server-side.

Resources