Basic HTTP authentication with Node and Express 4

2019-01-12 21:03发布

It looks like implementing basic HTTP authentication with Express v3 was trivial:

app.use(express.basicAuth('username', 'password'));

Version 4 (I'm using 4.2) removed the basicAuth middleware, though, so I'm a little stuck. I have the following code, but it doesn't cause the browser to prompt the user for credentials, which is what I'd like (and what I imagine the old method did):

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.writeHead(401, 'Access invalid for user', {'Content-Type' : 'text/plain'});
        res.end('Invalid credentials');
    } else {
        next();
    }
});

8条回答
放荡不羁爱自由
2楼-- · 2019-01-12 21:18

A lot of the middleware was pulled out of the Express core in v4, and put into separate modules. The basic auth module is here: https://github.com/expressjs/basic-auth-connect

Your example would just need to change to this:

var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));
查看更多
Melony?
3楼-- · 2019-01-12 21:20

Simple Basic Auth with vanilla JavaScript (ES6)

app.use((req, res, next) => {

  // -----------------------------------------------------------------------
  // authentication middleware

  const auth = {login: 'yourlogin', password: 'yourpassword'} // change this

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const [login, password] = new Buffer(b64auth, 'base64').toString().split(':')

  // Verify login and password are set and correct
  if (!login || !password || login !== auth.login || password !== auth.password) {
    res.set('WWW-Authenticate', 'Basic realm="401"') // change this
    res.status(401).send('Authentication required.') // custom message
    return
  }

  // -----------------------------------------------------------------------
  // Access granted...
  next()

})

Why?

  • req.headers.authorization contains the value "Basic <base64 string>", but it can also be empty and we don't want it to fail, hence the weird combo of || ''
  • Node doesn't know atob() and btoa(), hence the Buffer

ES6 -> ES5

const is just var .. sort of
(x, y) => {...} is just function(x, y) {...}
const [login, password] = ...split() is just two var assignments in one

source of inspiration (uses packages)


The above is a super simple example that was intended to be super short and quickly deployable to your playground server. But as was pointed out in the comments, passwords can also contain colon characters :. To correctly extract it from the b64auth, you can use this.

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const strauth = new Buffer(b64auth, 'base64').toString()
  const splitIndex = strauth.indexOf(':')
  const login = strauth.substring(0, splitIndex)
  const password = strauth.substring(splitIndex + 1)

  // using shorter regex by @adabru
  // const [_, login, password] = strauth.match(/(.*?):(.*)/) || []

On the other side, if you only ever use one or very few logins, this is the bare minimum you need:
(you don't need to parse the credentials)

//btoa('yourlogin:yourpassword') -> "eW91cmxvZ2luOnlvdXJwYXNzd29yZA=="

// Verify login and password is correct
if (req.headers.authorization !== 'Basic eW91cmxvZ2luOnlvdXJwYXNzd29yZA==') {
  // ...send('Authentication required.')
  return
}

BTW, do you need to have both secure and "public" paths? Consider using express.router instead.

var securedRoutes = require('express').Router()

securedRoutes.use(/* auth-middleware from above */)
securedRoutes.get('path1', /* ... */) 

app.use('/secure', securedRoutes)
app.get('public', /* ... */)

// example.com/public       // no-auth
// example.com/secure/path1 // requires auth
查看更多
祖国的老花朵
4楼-- · 2019-01-12 21:21

I changed in express 4.0 the basic authentication with http-auth, the code is:

var auth = require('http-auth');

var basic = auth.basic({
        realm: "Web."
    }, function (username, password, callback) { // Custom authentication method.
        callback(username === "userName" && password === "password");
    }
);

app.get('/the_url', auth.connect(basic), routes.theRoute);
查看更多
一纸荒年 Trace。
5楼-- · 2019-01-12 21:25

We can implement the basic authorization without needing any module

//1.
var http = require('http');

//2.
var credentials = {
    userName: "vikas kohli",
    password: "vikas123"
};
var realm = 'Basic Authentication';

//3.
function authenticationStatus(resp) {
    resp.writeHead(401, { 'WWW-Authenticate': 'Basic realm="' + realm + '"' });
    resp.end('Authorization is needed');

};

//4.
var server = http.createServer(function (request, response) {
    var authentication, loginInfo;

    //5.
    if (!request.headers.authorization) {
        authenticationStatus (response);
        return;
    }

    //6.
    authentication = request.headers.authorization.replace(/^Basic/, '');

    //7.
    authentication = (new Buffer(authentication, 'base64')).toString('utf8');

    //8.
    loginInfo = authentication.split(':');

    //9.
    if (loginInfo[0] === credentials.userName && loginInfo[1] === credentials.password) {
        response.end('Great You are Authenticated...');
         // now you call url by commenting the above line and pass the next() function
    }else{

    authenticationStatus (response);

}

});
 server.listen(5050);

Source:- http://www.dotnetcurry.com/nodejs/1231/basic-authentication-using-nodejs

查看更多
何必那么认真
6楼-- · 2019-01-12 21:26

There seems to be multiple modules to do that, some are deprecated.

This one looks active:
https://github.com/jshttp/basic-auth

Here's a use example:

// auth.js

var auth = require('basic-auth');

var admins = {
  'art@vandelay-ind.org': { password: 'pa$$w0rd!' },
};


module.exports = function(req, res, next) {

  var user = auth(req);
  if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
    res.set('WWW-Authenticate', 'Basic realm="example"');
    return res.status(401).send();
  }
  return next();
};




// app.js

var auth = require('./auth');
var express = require('express');

var app = express();

// ... some not authenticated middlewares

app.use(auth);

// ... some authenticated middlewares

Make sure you put the auth middleware in the correct place, any middleware before that will not be authenticated.

查看更多
再贱就再见
7楼-- · 2019-01-12 21:28

TL;DR:

express.basicAuth is gone
basic-auth-connect is deprecated
basic-auth doesn't have any logic
http-auth is an overkill
express-basic-auth is what you want

More info:

Since you're using Express then you can use the express-basic-auth middleware.

See the docs:

Example:

const app = require('express')();
const basicAuth = require('express-basic-auth');

app.use(basicAuth({
    users: { admin: 'supersecret123' },
    challenge: true // <--- needed to actually show the login dialog!
}));
查看更多
登录 后发表回答