Using Everyauth/Passport.js to authenticate with Twitter whilst asking for username/email/password - express

I want to create an authentication system whereby the user can "sign up with Twitter", but all this effectively does is authenticate their Twitter account and prefills a registration form with their Twitter username. The user will then be asked to enter an email and password (or an alternative username).
Thus, upon registration, the user has authenticated access to their Twitter account, and the access token can be stored in a database. Later down the line I will use this to access the Twitter API.
Node modules such as everyauth and passport do a lot of the heavy lifting with OAuth, but they only appear to provide a findOrCreateUser method, which doesn't offer a lot of breathing space to do something like what I need to do – that is, redirect to a registration form before registering the user, or if a user is found, just logging them in as per usual.

Here's a quick sketch of a possible approach for this:
Note that Passport does not provide a findOrCreateUser method. All database management and record creation is defined by your application (as it should be), Passport simply provides facilities for authentication.
The key to this approach is to simply create an "incomplete" user record in your database, from the profile data given by twitter. Then, in your application's routes, you can check if the conditions you need are met. If not, redirect the user to a form where they are prompted to fill out missing details.
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/twitter/callback"
},
function(token, tokenSecret, profile, done) {
// Create a user object in your database, using the profile data given by
// Twitter. It may not yet be a "complete" profile, but that will be handled
// later.
return done(null, user);
}
));
app.get('/auth/twitter',
passport.authenticate('twitter'));
app.get('/auth/twitter/callback',
passport.authenticate('twitter', { failureRedirect: '/login' }),
function(req, res) {
// The user has authenticated with Twitter. Now check to see if the profile
// is "complete". If not, send them down a flow to fill out more details.
if (req.user.isCompleteProfile()) {
res.redirect('/home');
} else {
res.redirect('/complete-profile');
}
});
app.get('/complete-profile', function(req, res) {
res.render('profile-form', { user: req.user });
});
app.post('/update-profile', function(req, res) {
// Grab the missing information from the form and update the profile.
res.redirect('/home');
});

Slight clarification. The test "if (req.user.isCompleteProfile())" could be:
if (req.user.isCompleteProfile)
ie, you create a field 'isCompleteProfile' when you are making the user record in the twitter step, and mark it true or false, depending on what you know of the user
or: it is a call to a function, thus
if (isCompleteProfile(req))
in this case, you have a separate function which tests the state of the user you have just created/modified, thus:
function isCompleteProfile(req) {
if (typeof req.user.local.email === "undefined") return false;
else return true;
}
And, I'd echo the praise for Jared and this marvellous tutorial on authentication in passportjs.

Related

How to protect routes from guests and with roles if using JWT?

On a server side I have 2 middlewares - protect (is logged in?) and restrictTo (checks user's role). These middlewares stop users or guests from performing certain actions if they are not allowed to
exports.protect = catchAsync(async (req, res, next) => {
let token;
if (
req.headers.authorization && req.headers.authorization.startsWith("Bearer")
) {
token = req.headers.authorization.split(" ")[1];
}
if (!token) {
return next(new AppError("You are not signed in!", 401));
}
const decodedToken = await promisify(jwt.verify)(
token,
process.env.JWT_SECRET
);
const currentUser = await User.findById(decodedToken.id);
if (!currentUser) {
return next(new AppError("User with such token no longer exists"));
}
req.user = currentUser;
next();
});
exports.restrictTo = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return next(new AppError("No access", 403));
}
next();
};
};
But how do I protect routes on a client side? If I'm not allowed to post a new note then I should be stopped from going to a /newnote page so I can't see and fill the form.
JWT token is stored in cookies with httpOnly flag. So I can't access the token from a Vue router. Store a user's role in Vuex? Then how do I synchronize the token state in cookies and in Vuex? If my token has been destroyed on a server side I still can have it in Vuex until I send a request to a protected endpoint.
Should I request a special auth endpoint for protected routes to check my current role using beforeEach?
Basically, you should add two things:
store the current authenticated user. By default, authUser is null. When someone logs in, authUser is an object with the user’s data. you can store this in Vuex, localStorage, etc.
create an interceptor/middleware in whatever library you are using for your api requests. If at some point you get a 401/403, it means that the current user’s session expired, or he is trying to access a protected area he shouldnt be looking at. Either way, reset the local authUser to null and redirect to login.
In Spa/mobile you dont have to worry too much about this as long as your backend is properly secured. If your authUser logic is correct, then only users with bad intentions will try to reach protected areas, whereas normal users will play by the rules and never hit a page they arent supposed to with their current privileges (assuming the UI is wired up correctly…).

How do you solve Auth0's URL Mismatch error

I'm trying to implement Auth0 with lock (version 10.11.0) inside my Angular2 app. Auth0 works if I list every possible route in Auth0's client's Allowed Callback URLs setting. It seems Auth0 picks up the callback URL from whatever URL the user happens to be on when they decide to login. This is not a scalable approach. How do you solve this?
I've tried entering a redirectUrl value in auth options. This gets me a little further in that the app does redirect to the URL supplied, however, Auth0 lock's authenticated callback never fires so I cannot retrieve authenticated user's profile.
So, I'm stuck. It seems my only course of action is to list every conceivable route in Auth0's client's Allowed Callback URLs setting and pray the guys from marketing do not come up with more routes.
Here's my code:
let options =
{
auth:
{
//redirectUrl: 'http://localhost:4200',
//redirect: true,
responseType: 'token',
params:
{
scope: 'openid user_id name nickname email picture'
}
}
};
this.lock = new Auth0Lock('xxx', 'yyy', options);
this.lock.on("authenticated", (authResult) =>
{
console.log('#### AUTH RESULTS:', authResult);
localStorage.setItem('id_token', authResult.idToken);
this.lock.getProfile(authResult.idToken, (error, profile) =>
{
if (error)
{
return;
}
console.log('#### AUTHENTICATED USER PROFILE:', profile);
});
}
Any ideas on how to make Auth0 work so you do not have to list every possible route a user can be on before deciding to authenticate?

Same flow for Login and Signup: Firebase Auth

I am using account kit and need to store the number#app.com on firebase. So I tried to do with their email and password flow.
But the problem I found their is that they have two different flows: one to create user, which is initiated at the time of register- createUserWithEmailAndPassword and other in the login flow, which need to be initiated at the time of login when user is already created -signInUserWithEmailAndPassword.
Is there something like a same create and sign in call, which can figure it out whether it is first time user, so create it or an existing user.
I can have a server check on my end but it brings in delay and multiple call to different system.
You could do the following:
firebase.auth().fetchProvidersForEmail(email)
.then(function(providers) {
if (providers.length > 0 ) {
// The user has already been registered. Careful though: the
// following assumes that your users only sign up with email
// and password. If they can sign up with other providers
// (Google, Facebook...), you may not be able to do the next
// call.
return firebase.auth().signInWithEmailAndPassword(
email, password);
} else {
// No account exists with this email address.
return firebase.auth().createUserWithEmailAndPassword(
email, password);
}).then(function(user) {
// User is signed in!
});
This will first check for the providers linked to that email address. If the list is not empty, and your users can only sign up with email and password (so no Google sign-in, Facebook login, etc.) then it is guaranteed that the user has already signed up with email and password. In this case, you can sign in the user. If the list is empty, no user exists, and you can create a new user.
Does that answer your question?
You can create a new account and verify if email is already used, and login it instead.
const auth = firebase.auth();
auth.createUserWithEmailAndPassword(email, password).then(cred => {
}).catch(err => {
if(err.code === 'auth/email-already-in-use') {
auth.signInWithEmailAndPassword(email, password).then(cred => {
console.log(cred);
}).catch(err => {
console.log(err.message);
})
} else {
console.log(err.message);
};
});

Why is my implementation of SSO using Ember-Simple-Auth with Auth0 getting stuck in a redirect loop?

I have an ember application that uses the Auth0 Ember Simple Auth addon to use the Ember-Simple-Auth functionality with Auth0's Lock.js. Recently I have been trying to implement single-sign-onfunctionality, such that if a user logs into a login portal application, their session will be preserved for other applications on the same domain, and they will not need to log in repeatedly. However my implementation of SSO is resulting in an infinite redirect loop between my login logic and Auth0's endpoint.
I have enabled SSO in the Auth0 application settings. My login is implemented in a few blocks.
My route.js contains a beforeModel() method which contains:
if (!get(session, 'isAuthenticated')){
// Forward to the login action
}
My login action:
login() {
var session = get(this, 'session');
session.authenticate('authenticator:myauthenticator', { authParams: { scope: 'openid' } });
}
This grabs the session object, and calls my custom authenticator. So far, this is basically just ember-simple-auth boilerplate, and complies with the examples supplied in the Auth0 Ember-Simple-Auth documentation.
Where I run into trouble is my custom authenticator. The base authenticator is here. You can see that it handles basic login logic easily, including showing the Auth0 lock when a user isn't authenticated. However it has no logic for handling the kind of SSO-session checking that I want to implement. So I implemented a custom authenticator as below, using examples provided by Auth0 for (basically) this exact scenario (you can see their examples [here], I'm using a slightly altered version)3:
authenticate(options) {
return new Ember.RSVP.Promise((res) => {
// the callback that will be executed upon authentication
var authCb = (err, profile, jwt, accessToken, state, refreshToken) => {
if (err) {
this.onAuthError(err);
} else {
var sessionData = { profile, jwt, accessToken, refreshToken };
this.afterAuth(sessionData).then(response => res(this._setupFutureEvents(response)));
}
};
var lock = this.get('lock');
// see if there's a SSO session available
lock.$auth0.getSSOData(function(err, data) {
if (!err && data.sso) {
// there is! redirect to Auth0 for SSO
options.authParams.callbackOnLocationHash = true;
lock.$auth0.signin(options.authParams, authCb);
} else {
// regular login
lock.show(options, authCb);
}
});
});
}
This behaves mostly as I would expect it to. When I log in with an existing session from another SSO-enabled app on the same domain, if (!err && data.sso) resolves to true, and lock.$auth0.signin(options.authParams, authCb) is called. However, this signin logic is not working as intended. Auth0.signin calls the Auth0.authorize method, which generates a target URL that looks something like:
https://mydomain.auth0.com/authorize?scope=openid&response_type=token&callbackOnLocationHash=true&sso=true&client_id=(MyClientIdHash)&redirect_uri=localhost%23access_token%3(MyAccessToken)%26id_token%3(MyIdToken1).(MyIdToken2).(MyIdToken3)token_type%3DBearer&auth0Client=(MyAuth0Client)
My application is then redirected to this URL for authorization. I get a 302 and am redirected back to the callback URL (my root page). Because there is a new page transition, if (!get(session, 'isAuthenticated')) is hit again. It returns false, and so the same logic repeats itself, looping indefinitely.
Does anyone have any insight on what I might be doing incorrectly here? The authorize endpoint seems to behave as if I were being authenticated, but then the authentication is never actually triggered. I've debugged through this code fairly extensively but seen no obvious red flags, and I've followed provided examples closely enough that I'm not sure what I would change. I'm not entirely sure where the failure to authenticate is happening such that get(session, 'isAuthenticated') is false.

user authentication libraries for node.js?

Are there any existing user authentication libraries for node.js? In particular I'm looking for something that can do password authentication for a user (using a custom backend auth DB), and associate that user with a session.
Before I wrote an auth library, I figured I would see if folks knew of existing libraries. Couldn't find anything obvious via a google search.
-Shreyas
If you are looking for an authentication framework for Connect or Express, Passport is worth investigating: https://github.com/jaredhanson/passport
(Disclosure: I'm the developer of Passport)
I developed Passport after investigating both connect-auth and everyauth. While they are both great modules, they didn't suit my needs. I wanted something that was more light-weight and unobtrusive.
Passport is broken down into separate modules, so you can choose to use only what you need (OAuth, only if necessary). Passport also does not mount any routes in your application, giving you the flexibility to decide when and where you want authentication, and hooks to control what happens when authentication succeeds or fails.
For example, here is the two-step process to setup form-based (username and password) authentication:
passport.use(new LocalStrategy(
function(username, password, done) {
// Find the user from your DB (MongoDB, CouchDB, other...)
User.findOne({ username: username, password: password }, function (err, user) {
done(err, user);
});
}
));
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) {
// Authentication successful. Redirect home.
res.redirect('/');
});
Additional strategies are available for authentication via Facebook, Twitter, etc. Custom strategies can be plugged-in, if necessary.
Session + If
I guess the reason that you haven't found many good libraries is that using a library for authentication is mostly over engineered.
What you are looking for is just a session-binder :) A session with:
if login and user == xxx and pwd == xxx
then store an authenticated=true into the session
if logout destroy session
thats it.
I disagree with your conclusion that the connect-auth plugin is the way to go.
I'm using also connect but I do not use connect-auth for two reasons:
IMHO breaks connect-auth the very powerful and easy to read onion-ring architecture of connect. A no-go - my opinion :).
You can find a very good and short article about how connect works and the onion ring idea here.
If you - as written - just want to use a basic or http login with database or file. Connect-auth is way too big. It's more for stuff like OAuth 1.0, OAuth 2.0 & Co
A very simple authentication with connect
(It's complete. Just execute it for testing but if you want to use it in production, make sure to use https)
(And to be REST-Principle-Compliant you should use a POST-Request instead of a GET-Request b/c you change a state :)
var connect = require('connect');
var urlparser = require('url');
var authCheck = function (req, res, next) {
url = req.urlp = urlparser.parse(req.url, true);
// ####
// Logout
if ( url.pathname == "/logout" ) {
req.session.destroy();
}
// ####
// Is User already validated?
if (req.session && req.session.auth == true) {
next(); // stop here and pass to the next onion ring of connect
return;
}
// ########
// Auth - Replace this example with your Database, Auth-File or other things
// If Database, you need a Async callback...
if ( url.pathname == "/login" &&
url.query.name == "max" &&
url.query.pwd == "herewego" ) {
req.session.auth = true;
next();
return;
}
// ####
// This user is not authorized. Stop talking to him.
res.writeHead(403);
res.end('Sorry you are not authorized.\n\nFor a login use: /login?name=max&pwd=herewego');
return;
}
var helloWorldContent = function (req, res, next) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('authorized. Walk around :) or use /logout to leave\n\nYou are currently at '+req.urlp.pathname);
}
var server = connect.createServer(
connect.logger({ format: ':method :url' }),
connect.cookieParser(),
connect.session({ secret: 'foobar' }),
connect.bodyParser(),
authCheck,
helloWorldContent
);
server.listen(3000);
NOTE
I wrote this statement over a year ago and have currently no active node projects. So there are may be API-Changes in Express. Please add a comment if I should change anything.
Looks like the connect-auth plugin to the connect middleware is exactly what I need
I'm using express [ http://expressjs.com ] so the connect plugin fits in very nicely since express is subclassed (ok - prototyped) from connect
I was basically looking for the same thing. Specifically, I wanted the following:
To use express.js, which wraps Connect's middleware capability
"Form based" authentication
Granular control over which routes are authenticated
A database back-end for users/passwords
Use sessions
What I ended up doing was creating my own middleware function check_auth that I pass as an argument to each route I want authenticated. check_auth merely checks the session and if the user is not logged in, then redirects them to the login page, like so:
function check_auth(req, res, next) {
// if the user isn't logged in, redirect them to a login page
if(!req.session.login) {
res.redirect("/login");
return; // the buck stops here... we do not call next(), because
// we don't want to proceed; instead we want to show a login page
}
// the user is logged in, so call next()
next();
}
Then for each route, I ensure this function is passed as middleware. For example:
app.get('/tasks', check_auth, function(req, res) {
// snip
});
Finally, we need to actually handle the login process. This is straightforward:
app.get('/login', function(req, res) {
res.render("login", {layout:false});
});
app.post('/login', function(req, res) {
// here, I'm using mongoose.js to search for the user in mongodb
var user_query = UserModel.findOne({email:req.body.email}, function(err, user){
if(err) {
res.render("login", {layout:false, locals:{ error:err } });
return;
}
if(!user || user.password != req.body.password) {
res.render("login",
{layout:false,
locals:{ error:"Invalid login!", email:req.body.email }
}
);
} else {
// successful login; store the session info
req.session.login = req.body.email;
res.redirect("/");
}
});
});
At any rate, this approach was mostly designed to be flexible and simple. I'm sure there are numerous ways to improve it. If you have any, I'd very much like your feedback.
EDIT: This is a simplified example. In a production system, you'd never want to store & compare passwords in plain text. As a commenter points out, there are libs that can help manage password security.
Also have a look at everyauth if you want third party/social network login integration.
Here is some code for basic authentication from one of my projects. I use it against CouchDB with and additional auth data cache, but I stripped that code.
Wrap an authentication method around you request handling, and provide a second callback for unsuccessfull authentication. The success callback will get the username as an additional parameter. Don't forget to correctly handle requests with wrong or missing credentials in the failure callback:
/**
* Authenticate a request against this authentication instance.
*
* #param request
* #param failureCallback
* #param successCallback
* #return
*/
Auth.prototype.authenticate = function(request, failureCallback, successCallback)
{
var requestUsername = "";
var requestPassword = "";
if (!request.headers['authorization'])
{
failureCallback();
}
else
{
var auth = this._decodeBase64(request.headers['authorization']);
if (auth)
{
requestUsername = auth.username;
requestPassword = auth.password;
}
else
{
failureCallback();
}
}
//TODO: Query your database (don't forget to do so async)
db.query( function(result)
{
if (result.username == requestUsername && result.password == requestPassword)
{
successCallback(requestUsername);
}
else
{
failureCallback();
}
});
};
/**
* Internal method for extracting username and password out of a Basic
* Authentication header field.
*
* #param headerValue
* #return
*/
Auth.prototype._decodeBase64 = function(headerValue)
{
var value;
if (value = headerValue.match("^Basic\\s([A-Za-z0-9+/=]+)$"))
{
var auth = (new Buffer(value[1] || "", "base64")).toString("ascii");
return {
username : auth.slice(0, auth.indexOf(':')),
password : auth.slice(auth.indexOf(':') + 1, auth.length)
};
}
else
{
return null;
}
};
A few years have passed and I'd like to introduce my authentication solution for Express. It's called Lockit. You can find the project on GitHub and a short intro at my blog.
So what are the differences to the existing solutions?
easy to use: set up your DB, npm install, require('lockit'), lockit(app), done
routes already built-in (/signup, /login, /forgot-password, etc.)
views already built-in (based on Bootstrap but you can easily use your own views)
it supports JSON communication for your AngularJS / Ember.js single page apps
it does NOT support OAuth and OpenID. Only username and password.
it works with several databases (CouchDB, MongoDB, SQL) out of the box
it has tests (I couldn't find any tests for Drywall)
it is actively maintained (compared to everyauth)
email verification and forgot password process (send email with token, not supported by Passport)
modularity: use only what you need
flexibility: customize all the things
Take a look at the examples.
A different take on authentication is Passwordless, a token-based authentication module for express that circumvents the inherent problem of passwords [1]. It's fast to implement, doesn't require too many forms, and offers better security for the average user (full disclosure: I'm the author).
[1]: Passwords are Obsolete
A word of caution regarding handrolled approaches:
I'm disappointed to see that some of the suggested code examples in this post do not protect against such fundamental authentication vulnerabilities such as session fixation or timing attacks.
Contrary to several suggestions here, authentication is not simple and handrolling a solution is not always trivial. I would recommend passportjs and bcrypt.
If you do decide to handroll a solution however, have a look at the express js provided example for inspiration.
Good luck.
There is a project called Drywall that implements a user login system with Passport and also has a user management admin panel. If you're looking for a fully-featured user authentication and management system similar to something like what Django has but for Node.js, this is it. I found it to be a really good starting point for building a node app that required a user authentication and management system. See Jared Hanson's answer for information on how Passport works.
Here are two popular Github libraries for node js authentication:
https://github.com/jaredhanson/passport ( suggestible )
https://nodejsmodules.org/pkg/everyauth
Quick simple example using mongo, for an API that provides user auth for ie Angular client
in app.js
var express = require('express');
var MongoStore = require('connect-mongo')(express);
// ...
app.use(express.cookieParser());
// obviously change db settings to suit
app.use(express.session({
secret: 'blah1234',
store: new MongoStore({
db: 'dbname',
host: 'localhost',
port: 27017
})
}));
app.use(app.router);
for your route something like this:
// (mongo connection stuff)
exports.login = function(req, res) {
var email = req.body.email;
// use bcrypt in production for password hashing
var password = req.body.password;
db.collection('users', function(err, collection) {
collection.findOne({'email': email, 'password': password}, function(err, user) {
if (err) {
res.send(500);
} else {
if(user !== null) {
req.session.user = user;
res.send(200);
} else {
res.send(401);
}
}
});
});
};
Then in your routes that require auth you can just check for the user session:
if (!req.session.user) {
res.send(403);
}
Here is a new authentication library that uses timestamped tokens. The tokens can be emailed or texted to users without the need to store them in a database. It can be used for passwordless authentication or for two-factor authentication.
https://github.com/vote539/easy-no-password
Disclosure: I am the developer of this library.
If you need authentication with SSO (Single Sign On) with Microsoft Windows user account. You may give a try to https://github.com/jlguenego/node-expose-sspi.
It will give you a req.sso object which contains all client user information (login, display name, sid, groups).
const express = require("express");
const { sso, sspi } = require("node-expose-sspi");
sso.config.debug = false;
const app = express();
app.use(sso.auth());
app.use((req, res, next) => {
res.json({
sso: req.sso
});
});
app.listen(3000, () => console.log("Server started on port 3000"));
Disclaimer: I am the author of node-expose-sspi.
slim-auth
A lightweight, zero-configuration user authentication module. It doesn't need a sperate database.
https://www.npmjs.com/package/slimauth
It's simple as:
app.get('/private-page', (req, res) => {
if (req.user.isAuthorized) {
// user is logged in! send the requested page
// you can access req.user.email
}
else {
// user not logged in. redirect to login page
}
})