Node.js/Passport Twitter authorization returns 302 - node.js

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.

Related

How to use LinkedIn api with Node js

All I need is to check in the backend side if the user access token is valid and get user email by its access token. It's hard to understand how to use this npm library for these purposes, so please help me.
In the documentation I've found the API address for it, but how to fetch the with Client ID and Client Secret of my app which I created on https://www.linkedin.com/developers/apps/new..
Hopefully, my question makes sense, thanks in advance <3
var passport = require('passport');
var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;
// linkedin app settings
var LINKEDIN_CLIENT_ID = "CLIENT_ID_HERE";
var LINKEDIN_CLIENT_SECRET = "CLIENT_SECRET_HERE";
var Linkedin = require('node-linkedin')(LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET);
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
passport.use(new LinkedInStrategy({
clientID: LINKEDIN_CLIENT_ID,
clientSecret: LINKEDIN_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/linkedin/callback",
scope: ['r_emailaddress', 'r_basicprofile', 'rw_company_admin'],
passReqToCallback: true
},
function (req, accessToken, refreshToken, profile, done) {
req.session.accessToken = accessToken;
process.nextTick(function () {
return done(null, profile);
});
}));
// for auth
app.get('/auth/linkedin',
passport.authenticate('linkedin', { state: 'SOME STATE' }),
function(req, res){
// The request will be redirected to LinkedIn for authentication, so this
// function will not be called.
});
// for callback
app.get('/auth/linkedin/callback', passport.authenticate('linkedin', { failureRedirect: '/' }),
function (req, res) {
res.redirect('/');
});
This is a Code Snippet of how i have used it, i guess it will help you on fetching with CLIENT_ID and CLIENT_SECRET.
Note : npm passport and passport-linkedin-oauth2 should already be installed
const accessToken = req.params.accessToken;
const options = {
host: 'api.linkedin.com',
path: '/v2/me',
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'cache-control': 'no-cache',
'X-Restli-Protocol-Version': '2.0.0'
}
};
const profileRequest = https.request(options, function(result) {
let data = '';
result.on('data', (chunk) => {
data += chunk;
console.log(data)
});
result.on('end', () => {
const profileData = JSON.parse(data);
return res.status(200).json({
'status': true,
'message': "Success",
'result': profileData
});
});
});
profileRequest.end();
The existing NodeJS LinkedIn official API is not so straightforward and hardly maintained.
If relevant, you can use this NodeJS LinkedInAPI.
It allows you to easily login via username & password or with a Cookie and make various requests:
import { Client } from 'linkedin-private-api';
const username = process.env.USERNAME as string;
const password = process.env.PASSWORD as string;
(async () => {
// Login
const client = new Client();
await client.login.userPass({ username, password });
// search for companies
const companiesScroller = await client.search.searchCompanies({ keywords: 'Microsoft' });
const [{ company: microsoft }] = await companiesScroller.scrollNext();
// Search for profiles and send an invitation
const peopleScroller = await client.search.searchPeople({
keywords: 'Bill Gates',
filters: {
pastCompany: microsoft.companyId
}
});
const [{ profile: billGates }] = await peopleScroller.scrollNext();
await client.invitation.sendInvitation({
profileId: billGates.profileId,
trackingId: billGates.trackingId,
});
})

Nuxt auth with passportjs?

How use nuxt auth Module (front-end) with passport-local using JWT (back-end express) ?
defining jwt strategy for verify jwt token (express)
var JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'secret';
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// or you could create a new account
}
});
}));
defining local strategy for verify username nad password (express)
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
code for issuing token after verifying username and password (expresss)
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }), //need to update from nuxt auth.
function(req, res) {
res.redirect('/');
});
nuxt auth local strategy consume username and passsword returns a JWT token (nuxt)
this.$auth.loginWith('local', {
data: {
username: 'your_username',
password: 'your_password'
}
})
It can work independently how do i combine these ?
code for express
Create passport strategies
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JwtStrategy = require('passport-jwt').Strategy;
passport.use(
new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
users.findOne({ email: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { error: 'Invalid username' });
}
if (!user.checkPassword(password)) {
return done(null, false, { error: 'invalid password' });
}
const info = { scope: '*' };
done(null, user, info);
});
}
)
);
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'JWT_SECRET_OR_KEY';
passport.use(
new JwtStrategy(opts, function(payload, done) {
users.findById(payload, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
}
return done(null, false);
});
})
);
use passport strategies
const express = require('express');
const passport = require('passport');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(passport.initialize()); // Used to initialize passport
// Routes
app.post(
'/login',
passport.authenticate('local', { session: false }),
function(req, res) {
const token = jwt.sign(req.user.userId, 'JWT_SECRET_OR_KEY');
return res.json({ token });
}
);
app.get(
'/me',
passport.authenticate(['jwt', 'bearer'], { session: false }),
function(req, res, next) {
const { userId } = req.user;
users.findOne({ _id: userId }, (err, data) => {
if (err) {
res.status(500).send(err);
} else if (data) {
const userData = data;
res.status(200).send(userData);
} else {
res.status(500).send('invalid token');
}
});
}
);
configuration for nuxt
inside nuxt.config.js
auth: {
resetOnError: true,
redirect: {
login: '/login', // User will be redirected to this path if login is required.
home: '/app/dashboard', // User will be redirect to this path after login. (rewriteRedirects will rewrite this path)
logout: '/login', // User will be redirected to this path if after logout, current route is protected.
user: '/user/profile',
callback: '/callback // User will be redirect to this path by the identity provider after login. (Should match configured Allowed Callback URLs (or similar setting) in your app/client with the identity provider)
},
strategies: {
local: {
endpoints: {
login: {
url: '/login',
method: 'post',
propertyName: 'token'
},
logout: false,
user: {
url: '/me',
method: 'GET',
propertyName: false
}
},
tokenRequired: true,
tokenType: 'Bearer'
}
}
inside Login .vue
this.$auth
.loginWith('local', {
data: {
username: this.user.email,
password: this.user.password
}
})
.catch(err => {
console.error(err );
});

passport token is not being authorized

const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/User');
const key = require('./keys').secret;
const mongoose = require('mongoose');
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = key;
module.exports = (passport) => {
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload);
User.findById(jwt_payload.id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}))
}
I am using passport-jwt stratergy to validate with token but this is file is not running properly i even tried to console the payload here but this is not even logging it
passport.use(new JwtStrategy(opts, function(jwt_payload, done)
console.log(jwt_payload);
Token Stratergy
const payload = {
id: user.id,
username: user.username,
name: user.name
}
const token = jwt.sign(payload, config.secret, { expiresIn: 36000 });
return res.json({
success: true,
token: 'JWT ' + token,
user: {
id: user.id,
name: user.name,
username: user.username
}
})
And when i am using this on the protected routes it is not authorizing the protected routes
Result = Unauthorized
router.get('/profile', passport.authenticate('jwt', { session: false }), (req, res) => {
res.send("Profile");
});
The reason your authentication is not working is that you've used the legacy token prefix "JWT" and a modern JWT (fromAuthHeaderAsBearerToken) extractor as an option to your strategy.
You can do one of the steps to fix the problem:
change the token prefix to 'Bearer ' + token
change the token extractor:
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme("JWT");

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);
});

Passport.authenticate not sending a response

I'm using Passport for authentication, specifically with a JWT strategy. I'm able to create a new token when a user is created, however, when I use that token in the header of a request to a route that requires authentication, my request just hangs up. I'm using Postman to test these POST/GET requests.
Here's my initial configuration for signing up a user:
const User = require('../db/models/User');
const jwt = require('jsonwebtoken');
function userToken(user) {
return jwt.sign({
id: user.id,
}, process.env.JWT_SECRET);
}
exports.signup = function(req, res, next) {
const email = req.body.email.toLowerCase();
const password = req.body.password.toLowerCase();
User.findOne({
where: { email },
}).then(function(user) {
if (!user) {
User.create({
email,
password,
})
.then(function(user) {
return res.send({ token: userToken(user) });
});
}
if (user) {
return res.send({ message: 'That user is in use' });
}
});
};
Here's my passport configuration:
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../db/models/User');
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
secretOrKey: process.env.JWT_SECRET,
};
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
User.findOne({
where: { id: payload.id },
}, function(err, user) {
if (err) { return done(err, false); }
if (user) { return done(null, user); }
return done(null, false);
});
});
passport.use(jwtLogin);
Here's what my protected route looks like:
const passport = require('passport');
const requireAuth = passport.authenticate('jwt', { session: false });
module.exports = function router(app) {
app.get('/', requireAuth, function(req, res) {
res.send({ 'hi': 'there' });
});
};
Here's what I see in my terminal:
Executing (default): SELECT "id", "username", "email", "password", "photo", "createdAt", "updatedAt" FROM "users" AS "user" WHERE "user"."id" = 15;
So I know that it's correctly querying for a user id and searching for it, however, it just hangs up at this point, rather than serving me a response.
Not sure what the issue is, so any and all suggestions are welcomed and appreciated. Thank you!
Realized that because I am using Sequelize, it handles errors with a catch like so:
...
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
User.findOne({
where: { id: payload.id }
})
.then(user => {
if (user) {
done(null, user);
} else {
done(null, false);
}
})
.catch(err => {
if (err) { return done(err, false); }
});
});
...
This solved my issue and is returning my response.

Resources