In my express app I've set static files to be served from the /public directory with this line:
app.use(express.static(__dirname + '/public'));
Now I need to add a middleware for authentication before serving the static content and if the user is not authenticated to be redirected to a route for authentication (e.g., /login).
I'm not really sure how I have to do it. Any ideas?
Since you didn't specify it, I'm going to assume that you already have some kind of authentication system.
In Express, the order of the middlewares in the code matters: if you want to have middleware 1 executed before middleware 2, you should place them accordingly in your code. Since express.static is a middleware, if you want authentication before serving your static files you can simply write your authentication middleware before the call to express.static
app.use(function (req, res, next) {
if (!userAuthenticated(req)) {
return res.redirect('/login');
}
next();
});
app.use(express.static(__dirname + '/public'));
I am assuming you have a userAuthenticated function which is for instance checking if the HTTP requests contains a valid access-token.
Read more about middlewares.
Check out Passport.
Passport has many authentication strategies.
Here's an example with basic HTTP authentication:
var express = require('express');
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var db = require('./db');
// Configure the Basic strategy for use by Passport.
//
// The Basic strategy requires a `verify` function which receives the
// credentials (`username` and `password`) contained in the request. The
// function must verify that the password is correct and then invoke `cb` with
// a user object, which will be set at `req.user` in route handlers after
// authentication.
passport.use(new BasicStrategy(
function(username, password, cb) {
db.users.findByUsername(username, function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false); }
if (user.password != password) { return cb(null, false); }
return cb(null, user);
});
}));
// Create a new Express application.
var app = express();
var authenticate = passport.authenticate('basic', {
session: false,
failureRedirect: '/login'
});
app.use(authenticate, express.static(__dirname + '/public'));
Depends on what kind of authentication you are looking for, but if you just want some login-feature, this is what you need: http://passportjs.org/
It has support for local login strategies, as well as a whole bunch of 3rd party strategies like facebook, twitter, etc.
If you need something else, simpler or self-made, just write a middleware to use before you declare the static endpoint, and call next() if everything checks out, and res.redirect if user needs to retry.
Related
Ok, so this is a common error with many causes. I am trying to modify an existing Node-Passport-Facebook module to have local images from the desktop uploaded to a users Facebook account after they log in. That is my goal. This is the code module I am extending
https://github.com/passport/express-4.x-local-example
which in turn is based on
https://github.com/jaredhanson/passport-facebook
I never get past console.log('ERROR HERE... with an error of "This authorization code has been used."
What's confusing is that the auth code returned is ALWAYS DIFFERENT! so how could it already have been used when I try and exchange it for an access token?
Can anyone offer some suggestions, and or next steps I might try? My hunch is that there is something about Passport.js that is not implemented properly.
So my question is, how would I modify the code below (based on this passport facebook example) https://github.com/passport/express-4.x-facebook-example/blob/master/server.jsto upload an image after logging in?
var express = require('express');
var passport = require('passport');
var Strategy = require('passport-facebook').Strategy;
var CLIENTSECRET ='<client secret>';
var APPID ='<app id>';
// Configure the Facebook strategy for use by Passport.
//
// OAuth 2.0-based strategies require a `verify` function which receives the
// credential (`accessToken`) for accessing the Facebook 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(new Strategy({
clientID: APPID,
clientSecret: CLIENTSECRET,
callbackURL: 'http://localhost:3000/login/facebook/return',
enableProof: true
//callbackURL: 'http://localhost:3000/login/facebook/return'
},
function(accessToken, refreshToken, profile, cb) {
// In this example, the user's Facebook profile is supplied as the user
// record. In a production-quality application, the Facebook profile should
// be associated with a user record in the application's database, which
// allows for account linking and authentication with other identity
// providers.
cb(null, profile);
}));
// Configure Passport authenticated session persistence.
//
// In order to restore authentication state across HTTP requests, Passport needs
// to serialize users into and deserialize users out of the session. In a
// production-quality application, this would typically be as simple as
// supplying the user ID when serializing, and querying the user record by ID
// from the database when deserializing. However, due to the fact that this
// example does not have a database, the complete Twitter profile is serialized
// and deserialized.
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
console.log(" ");
console.log("ASSERT passport.deserializeUser being called");
console.log(" ");
cb(null, obj);
});
// Create a new Express application.
var app = express();
// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// Use application-level middleware for common functionality, including
// logging, parsing, and session handling.
app.use(require('morgan')('combined'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
//app.use(passport.session());
// Define routes.
app.get('/',
function(req, res) {
res.render('home', { user: req.user });
});
app.get('/login',
function(req, res){
res.render('login');
});
app.get('/login/facebook',
passport.authenticate('facebook'));
app.get('/login/facebook/return',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
//my code changes start here!!
var code = req.query.code;
console.log("1 ASSERT after successful login! code="+code);
if(req.query.error) {
// user might have disallowed the app
return res.send('login-error ' + req.query.error_description);
} else if(!code) {
return res.redirect('/');
}
var options={
host:'graph.facebook.com',
path:'/oauth/access_token?client_id='+APPID+'&code='+code +'&client_secret='+CLIENTSECRET+'&redirect_uri=http://localhost:3000/login/faceboo k/return'
}
var https=require('https');
https.get(options,function(res){
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('ERROR HERE'+chunk);
});
});
console.log("2 ASSERT after successful login!")
//my code changes end here!!
});
app.get('/profile',
require('connect-ensure-login').ensureLoggedIn(),
function(req, res){
res.render('profile', { user: req.user });
});
app.listen(3000);
You don't need to make a request to /oauth/access_token at all (well you do, but passport has already handled it for you). That endpoint is for getting an access token when you don't have one, but you already have an access token here:
passport.use(new Strategy({
clientID: APPID,
clientSecret: CLIENTSECRET,
callbackURL: 'http://localhost:3000/login/facebook/return',
enableProof: true
//callbackURL: 'http://localhost:3000/login/facebook/return'
},
function(accessToken, refreshToken, profile, cb) {
// You have the access token here!
cb(null, profile);
}));
You'll need to save that accessToken some way, so that you can use it later when you make requests to the Graph API. You'll probably want to save it to the user's session, but you can also use a strategy like this: https://stackoverflow.com/a/24474900/772035
If you want the user to grant permission to publish (which you will need them to do, to be able to post to their feeds) you also need to replace every call to passport.authenticate with:
passport.authenticate('facebook', { scope: ['publish_actions'] } );
So that the posting permission is requested when the user first adds your app. Then you'll be able to use the /user/photos endpoint to upload a photo, passing the accessToken that you saved earlier in the query string.
You need to encode your query params.
var qs = {
client_id: APPID,
redirect_uri: 'http://localhost:3000/login/facebook/return',
client_secret: CLIENTSECRET,
code: code,
};
options = {
host:'graph.facebook.com',
path:'/oauth/access_token?' + require('querystring').stringify(qs),
};
I think that's your problem. The code itself looks fine apart from that. You'll want the querystring module to parse the results too by the way.
I solved this error. Follow the progress.
Facebook Log in -> Settings -> Apps -> Logged in with Facebook -> Delete Your apps.
After deleting your apps, Try to login with Facebook button.
you need to define profileFields instead of enableProof: true
passport.use(new FacebookStrategy({
clientID:keys.FacebookAppID,
clientSecret:keys.FacebookAppSecret,
callbackURL:'http://localhost:3000/auth/facebook/callback',
profileFields:['email','name','displayName','photos']
},(accessToken,refreshToken,profile,done)=>{
console.log(profile);
}));
Using passport local authentication, login works, I click getStatus button and it works, then logout works. But after logout, I click BACK in the browser, it can still display the full content of getStatus. The console log in isAuthenticated() still says "you are logged in". This is the simplified code:
var express = require('express');
var passport = require('passport');
var net = require('net');
var bodyParser = require('body-parser');
var http = require('http');
var multer = require('multer');
var cp = require('child_process');
var exec = require('child_process').exec;
var sys = require('sys');
var path = require('path');
var util = require('util');
var session = require('express-session');
var crypto = require('crypto');
var sqlite3 = require('sqlite3');
/////////////////////////////////////////////////
var LocalStrategy = require('passport-local').Strategy;
var db = new sqlite3.Database('./myPassword.db');
passport.use(new LocalStrategy(function(username, password, done)
{
console.log("step 2: Client sent you user: " + username + " password: " + password);
db.get('SELECT slat FROM users WHERE username = ?', username, function(err, row)
{
if (!row) return done(null, false);
console.log("step 4");
db.get('SELECT username, id FROM users WHERE username = ? AND password = ?',
username, password, function(err, row)
{
console.log("step 6");
if (!row) return done(null, false);
console.log("step 8");
return done(null, row);
});
});
}));
passport.serializeUser(function(user, done) {
return done(null, user.id);
});
passport.deserializeUser(function(id, done) {
db.get('SELECT id, username FROM users WHERE id = ?', id, function(err, row)
{
if (!row)
return done(null, false);
return done(null, row);
});
});
/////////////////////////////////////////////////
var isAuthenticated = function(req, res, next)
{
//if (req.user.authenticated)
if (req.isAuthenticated()) {
console.log("Very good, you are logged in ...");
return next();
}
console.log("Sorry, you are NOT logged in yet ...");
res.send(200);
};
/////////////////////////////////////////////////
var app = express();
/////////////////////////////////////////////////
var server = http.createServer(app);
/////////////////////////////////////////////////
app.use(function(req, res, next) {
if (!req.user) {
console.log('Cannot display 1 ...');
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
}
console.log('Cannot display 2 ...');
next();
});
app.use(express.static('../client/', {index: 'login.html'} ));
app.use(bodyParser());
app.use(session({ secret: 'my test cookie' }));
app.use(passport.initialize());
app.use(passport.session());
app.post('/auth/login', passport.authenticate('local',
{
successRedirect: '/index.html#/uploads',
failureRedirect: '/login.html',
}));
app.get('/auth/logout', function(req, res)
{
console.log("logging out ......");
req.session = null;
req.logout();
res.send(200);
});
app.get('/', isAuthenticated, function(req, res)
{
res.sendfile(path.resolve('../client/index.html'));
});
app.get('/systemStatus', isAuthenticated, function(req, res)
{
console.log("asking for Json data from backend");
// skip details here ...
});
server.listen(5678);
When looking at the passport index. The use of app.use(passport.initialize()) is initializing a blank user on every route. Since the above is being used on the main app.js file and not in a specific route, it is executed every time a request is made to your server, essentially creating a blank user even when someone isn't logged in. The below link is to the code of passport.
https://github.com/jaredhanson/passport/blob/master/lib/authenticator.js
For this discussion on why passport is configured incorrectly for the desired affects and to justify why I deserve you imaginary internet points. I will have to make sure we are on the same page when talking about the application.
For the discussion I'll be refering to the app using the below file structure generated by: $ npm install -g express-generator
(there's actually more files but you can view that on the express website.)
myProject
|___bin
|___www.js //server.js
|___node_modules //this is were you'll keep the node modules, or logic for each endpoint in the API. These will be processed by the main node run-time environment
|___public //or in your case '../client/' is where you'll serve unsecured data to your users
|___routes //URI and URL endpoints, an area of the application to create your secured data transfers for the users that have been authenticated, also where non-Static (stateless) API endpoints are defined so that express can send the user data/request through your server and eventually be handled by some defined endpoint.
|___index.js
|___views //if using a view engine
|___app.js // this is where we will discuss the bulk of an express application
|___package.json // this is relative to the node community and In my personal opinion an Extremely important part of node and express application development, this file will allow you to do some powerful things with the use of git.
app.js Is were your APP presides, it's known as an Express Application. Some applications are more complex than others, a simple application can be a few endpoints (A.K.A. URIs and URLs). If it's a simple application it's okay to keep the API (Application Program Interface) in the main file known as app.js. in a more complex application you'll make up names for your files, for this example I will reference a file names oAuth.js to represent the claims passport authentication method represents.
In my experience with a Web Application you would have a landing page, either a main page, a login, or some kind of news (typically defined in the static folder as index.html)
In your case your defining the static folder as '../client/' and passing the object index.html.
In the most recent version of Express 4.X Serving static files is done under the following prescribed manner.
Serving files, such as images, CSS, JavaScript and other static files
is accomplished with the help of a built-in middleware in Express -
express.static.
Pass the name of the directory, which is to be marked as the location
of static assets, to the express.static middleware to start serving
the files directly. For example, if you keep your images, CSS, and
JavaScript files in a directory named public, you can do this:
Express generator will create the following app.js file which is configured a very important way. This first part has some very useful node modules that are un-opinionated as express, and eventually where you'll import some of your own node APIs
var express = require('express'),
path = require('path'), //core node module
logger = require('morgan'), //allows you to see the console.logs during development and see incoming and outgoing messages to the server. It also displays `console.log()` defined in any node_module, express route, and express app file.
cookieParser = require('cookie-parser'), //helps when trying to handle cookies
bodyParser = require('body-parser'); //helps when parsing certain types of data
routes are like mini express applications, remember when we first discussed how some applications can become more complex than others? This is how you manage the complexity so your application can grow and flourish. Assuming you wish to add new features for your loving and wonderful users.
var route = require('.routes/index',// this is importing the the logic of the URI and URL enpoint to this file. It will be referenced as the second argument in the route and configuration references below.
oAuth = require('.routes/oauth') // this is the file where you'll place all of the passport logic you have previously wrote. Any other authenticated routes need to be defined in this file.
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade'); //alot of people are also using EJS equally if not more
Now setup the basic middlewares provided by express-generator
app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); //this style declaration ensures you hit what ever directory you want to by changing public as long as that directory exists.
route URI and URL endpoints being defined by you to -->express. It takes the form of strings and a reference to it's route at the top of this file
app.use('/', routes); //unsecured landing page
this express object usage will allow you to define APIs, authorized and unAuthorized routes. This is where you will declare your reference to the authenticated portion of the Express Application. Any portion of the application declared above app.use('/auth/',oAuth) will NOT be authenticated. Any portion declared below the /auth/ portion of your URIs and URLs. will be authenticated.
app.use('/auth/', oAuth);
some extras that the express generator will place in the app file which are extremely useful.
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
Because you are polluting your applications namespace with un-needed complexity it is inadvertently causing you undesired authentication affects. This goes deeper since it's relative to formatting and configuring your application program interface and how javascript files are executed in the Node run-time environment, as well as how the express application framework is meant to be used and configured when building complex applications that require authentication to access.
Now back to your question of why you keep getting an authenticated user although no one is logged in? It's because your use of app.use('some string or some middleware'). To fix the issue remove all of your authentication processing and move it to a route. In the above example it's referenced as oAuth.js. Define any routes that need authentication behind the passport middleware.
Now because your question is also regarding node and you mentioned in the comments that you are part of a scrum it's important to state that all of this information is contained on the express website which is where i initially linked my answer. Despite me telling you that you need a route and that passport was configured incorrectly. So any inflammatory comments "read the manual" are made because I feel that you didn't even investigate the link I sent in my initial answer, nor did you read any other part of the express frame work and their website. If you plan to understand any node_modules and complex frame works its equally important to read about them and do their tutorials, actually go through the node_modules when there is a getting started and/or they have API references. By blasting into application development without trying any portion of the framework basics will just make you spend more time coding bad breakable code. It will slow your development significantly if you aren't understanding how the node_modules function. The best way to understand their functionality is by reading them. That's all for my rant/advice for learning how to develop a web application. Ultimately you'll be able to reuse a lot of your tutorial code in a application.
I have been able to use node.js and passport.js to connect to Facebook using the GitHub project available at: https://github.com/jaredhanson/passport-facebook/tree/master/examples/login.
Here is the what the app.js code is doing:
var express = require('express')
, passport = require('passport')
, util = require('util')
, FacebookStrategy = require('passport-facebook').Strategy
, logger = require('morgan')
, session = require('express-session')
, bodyParser = require("body-parser")
, cookieParser = require("cookie-parser")
, methodOverride = require('method-override');
var FACEBOOK_APP_ID = "--insert-facebook-app-id-here--"
var FACEBOOK_APP_SECRET = "--insert-facebook-app-secret-here--";
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing. However, since this example does not
// have a database of user records, the complete Facebook profile is serialized
// and deserialized.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// Use the FacebookStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Facebook
// profile), and invoke a callback with a user object.
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Facebook profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Facebook account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
}
));
var app = express();
// configure Express
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(logger());
app.use(cookieParser());
app.use(bodyParser());
app.use(methodOverride());
app.use(session({ secret: 'keyboard cat' }));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user });
});
// GET /auth/facebook
// Use passport.authenticate() as route middleware to authenticate the
// request. The first step in Facebook authentication will involve
// redirecting the user to facebook.com. After authorization, Facebook will
// redirect the user back to this application at /auth/facebook/callback
app.get('/auth/facebook',
passport.authenticate('facebook'),
function(req, res){
// The request will be redirected to Facebook for authentication, so this
// function will not be called.
});
// GET /auth/facebook/callback
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000);
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
The code works great if I am just using the internet with no proxy server but if I am behind a corporate firewall then I get the following error:
InternalOAuthError: Failed to obtain access token
at Strategy.OAuth2Strategy._createOAuthError (C:\FacebookExample\passport-facebook\examples\login\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:348:17)
at C:\FacebookExample\passport-facebook\examples\login\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:171:43
at C:\FacebookExample\passport-facebook\examples\login\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:177:18
at ClientRequest.<anonymous> (C:\FacebookExample\passport-facebook\examples\login\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:148:5)
at emitOne (events.js:77:13)
at ClientRequest.emit (events.js:169:7)
at TLSSocket.socketErrorListener (_http_client.js:259:9)
at emitOne (events.js:77:13)
at TLSSocket.emit (events.js:169:7)
at emitErrorNT (net.js:1253:8)
Does anyone know how to setup the code above to go through a corporate proxy server for connectivity? I have tried setting the npm configuration properties of proxy, http-proxy and https-proxy but it does not appear to make a difference when I run this application. Any help you can offer would be greatly appreciated. Thank you.
Adding this code in /node_moduels/oauth/lib/oauth.js would fix the issue temporarily.
var HttpsProxyAgent = require('https-proxy-agent');
if (process.env['https_proxy']) {
httpsProxyAgent = new HttpsProxyAgent(process.env['https_proxy']);
}
Finally, set the httpsProxyAgent to the request options right before _executeRequest gets called like this:
options.agent = httpsProxyAgent
oauth2.js does not take in consideration the proxy environment variables when it performed the requests. Blocking the web app to get authenticated (receive the access token and the github user info)
So I tried with this workarround that let know to the oauth2.js that uses the proxy to communicate.
You usually will find the oauth2.js code that needs to adapt at: your_project/node_modules/oauth/lib/oauth2.js
var querystring= require('querystring'),
crypto= require('crypto'),
https= require('https'),
http= require('http'),
URL= require('url'),
OAuthUtils= require('./_utils');
// line codes to add
var HttpsProxyAgent = require('https-proxy-agent');
let httpsProxyAgent = null
if (process.env['https_proxy']) {
httpsProxyAgent = new HttpsProxyAgent("http://127.0.0.1:1087");
// fill in your proxy agent ip and port
}
....
// line codes to add
options.agent = httpsProxyAgent;
this._executeRequest( http_library, options, post_body, callback );
}
exports.OAuth2.prototype._request= function(method, url, headers, post_body, access_token, callback) {
...
Adding
options.agent = httpsProxyAgent
worked for me. But I added it in $workdir\node_modules\oauth\lib\oauth2.js as method _executeRequest exists only there.
If you have trouble finding _executeRequest I recommend do full search in node_modules\oauth as I found that method in oauth2.js
Also, don't forget that if you use third party API that you should consider them as well. I use vkAPI SDK, which is basically a wrapper over http_library with handy methods.
In that case I would suggest to wrap http_library calls and decorate them for your need.
Good way to start here Anyway to set proxy setting in passportjs?
I have a project based on express with a required authentication based on passport.
The backoffice is an angularjs app served as static files.
My authentication code is completly based on https://github.com/jaredhanson/passport-local/blob/master/examples/express3-no-connect-flash/app.js
To do not serve the angular app if you are not authenticated. I have try by adding ensureAuthenticated on the /admin route but it make the route not working (404). Once I remove ensureAuthenticated the /admin is served.
app.use(express.static(path.join(__dirname, 'public')));
app.use('/admin', ensureAuthenticated, express.static(path.join(__dirname, 'admin')));
//serve routes
app.use(app.router);
The public folder contains the login page.
How could I achieve this ?
Ran into same issue, this is what I ended up doing!
app.use doesn't let you chain middlewares in that way. The various
app.VERB functions do, but app.use doesn't. That's for one middleware
at a time.
If you split the 2 middlewares out into separate calls, you should get
the results you want:
app.use('/admin', ensureAuthenticated);
app.use('/admin', express.static(path.join(__dirname, 'admin')));
Cannot use basic authentication while serving static files using express
You can check the route using middleware and redirect them if they aren't logged in and are hitting admin pages, something like (untested):
app.use(function(req, res, next) {
if (req.user == null && req.path.indexOf('/admin') === 0)
{
res.redirect('/login');
}
next();
});
app.use('/admin', function(req,res,next){
if(req.user){
return express.static(path.join(__dirname, 'public'));
} else {
res.render(403, 'login', {message:'Please, login!'});
}
});
//serve routes
app.use(app.router);
Update for express#4.16.4+, passport-jtw#0.4.0, and passport-jwt#4.0.0
First setup a passport auth strategy. If you use a jwt, you can take a token from a query parameter, if not you can use another Extract function (or multiple using Jwt.ExtractJwt.fromExtractors())
passport.use('basic-user',
new Jwt.Strategy({
...jwtConfig.options,
jwtFromRequest: Jwt.ExtractJwt.fromUrlQueryParameter('token')
}, verifyUser)
);
Then you can use a passport authenticate function before serving static files
app.use('/files', [
passport.authenticate(['basic-user'], { session: false }),
express.static(path.join(__dirname, 'files')) //make sure you access proper directory
])
You could also chain middlewares as an array to achieve this goal:
app.use('/admin', [ensureAuthenticated, express.static(path.join(__dirname, 'admin'))]);
The accepted answer felt a bit partial (and may not work in some cases), so here's a bit more verbose and generalized answer:
// Here we'll attach the user object. The correct ordering
// of these middleware functions is important.
app.use(
'/some-restricted-static-path',
passport.authenticate("bearer", { session: false })
);
// As the user object should now be attached (if authorized), we can now
// verify it to be so.
app.use('/some-restricted-static-path', (req, res, next) => {
if (!!req.user) {
// The user exists, we can continue.
// Here you can also validate the role etc if necessary.
next();
} else {
// No user object found, terminate the pipeline with .end().
res.status(401).end();
}
});
// And finally, here's the actual handler that won't be accessed if
// something went wrong earlier.
app.use(
'/some-restricted-static-path',
express.static(
path.join(
__dirname,
"../dist/attachments"
)
)
);
Explanation: In Express, the middleware is processed one-by-one. If one of the middleware terminate the process, every middleware after it will be skipped. So, knowing this, we can first attach the user object, then validate it, and finally either grant or deny access.
I'm using Passport.js to authenticate with Google via OAuth (I'm using the passport-google-oauth strategy). It works fine, but I'm currently redirecting the user to "/", and I'd like to send them to "/" plus the current hash tag. I can send the hash value in a query string parameter, but I can't seem to set that value to the callbackURL property of the object that I'm passing to authenticate.
Can someone provide an example or explanation as to the correct way to do this? I'm not beholden to using the query string, it just seemed the most straight-forward route, but I'm open to using a session variable or something else, if that would be easier or better practice.
Thank you.
You can achieve this effect by storing the return url in the session.
// server
var app, express;
express = require('express');
app = express();
app.configure(function() {
app.use(express.cookieSession({secret: 'shh'}));
});
app.get('/auth/twitter', function(req, res, next) {
// to return to '/#/returnHash', request this url:
// http://example.com/auth/twitter?return_url=%2F%23%2FreturnHash
// on the client you can get the hash value like this:
// encodeURIComponent("/"+window.location.hash)
req.session.return_url = req.query.return_url;
next();
}, passport.authenticate('twitter'));
app.get('/auth/twitter/callback', passport.authenticate('twitter', {
failureRedirect: '/login'
}), function(req, res) {
var url = req.session.return_url;
delete req.session.return_url;
// redirects to /#/returnHash
res.redirect(url);
});