Grundlegende HTTP-Authentifizierung mit Node und Express 4

107

Die Implementierung der grundlegenden HTTP-Authentifizierung mit Express v3 war anscheinend trivial:

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

Version 4 (ich verwende 4.2) hat die basicAuthMiddleware jedoch entfernt, sodass ich ein wenig festgefahren bin. Ich habe den folgenden Code, aber er veranlasst den Browser nicht, den Benutzer zur Eingabe von Anmeldeinformationen aufzufordern. Dies ist das, was ich möchte (und was ich mir bei der alten Methode vorstelle):

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();
    }
});
Dov
quelle
2
Schamloser Stecker: Ich habe ein ziemlich beliebtes Modul, das dies einfach macht und die meisten Standardfunktionen bietet, die Sie benötigen würden: Express-Basic-Auth
LionC
Ich habe kürzlich das Paket von @LionC gegabelt, weil ich es für ein Unternehmensprojekt in kürzester Zeit anpassen musste (um kontextsensitive Autorisierer zu aktivieren): npmjs.com/package/spresso-authy
castarco

Antworten:

108

Einfache Basisauthentifizierung mit Vanille-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] = Buffer.from(b64auth, 'base64').toString().split(':')

  // Verify login and password are set and correct
  if (login && password && login === auth.login && password === auth.password) {
    // Access granted...
    return next()
  }

  // Access denied...
  res.set('WWW-Authenticate', 'Basic realm="401"') // change this
  res.status(401).send('Authentication required.') // custom message

  // -----------------------------------------------------------------------

})

Hinweis: Diese "Middleware" kann in jedem Handler verwendet werden. Entfernen Sie einfach next()die Logik und kehren Sie sie um. Siehe das folgende Beispiel mit 1 Anweisung oder den Bearbeitungsverlauf dieser Antwort.

Warum?

  • req.headers.authorizationenthält den Wert " Basic <base64 string>", kann aber auch leer sein und wir möchten nicht, dass er fehlschlägt, daher die seltsame Kombination von|| ''
  • Knoten weiß es nicht atob()und btoa()daher derBuffer

ES6 -> ES5

const ist nur var .. Art
(x, y) => {...}ist nur function(x, y) {...}
const [login, password] = ...split()ist nur zwei varAufgaben in einer

Inspirationsquelle (verwendet Pakete)


Das obige ist super einfach sollte sehr kurz sein und schnell auf Ihrem Spielplatzserver bereitgestellt werden können. Wie in den Kommentaren erwähnt, können Passwörter auch Doppelpunktzeichen enthalten :. Um es korrekt aus dem b64auth zu extrahieren , können Sie dies verwenden.

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const strauth = Buffer.from(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(/(.*?):(.*)/) || []

Grundlegende Authentifizierung in einer Anweisung

... auf der anderen Seite, wenn Sie immer nur eine oder sehr wenige Anmeldungen verwenden, ist dies das absolute Minimum, das Sie benötigen: (Sie müssen die Anmeldeinformationen überhaupt nicht analysieren)

function (req, res) {
//btoa('yourlogin:yourpassword') -> "eW91cmxvZ2luOnlvdXJwYXNzd29yZA=="
//btoa('otherlogin:otherpassword') -> "b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk"

  // Verify credentials
  if (  req.headers.authorization !== 'Basic eW91cmxvZ2luOnlvdXJwYXNzd29yZA=='
     && req.headers.authorization !== 'Basic b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk')        
    return res.status(401).send('Authentication required.') // Access denied.   

  // Access granted...
  res.send('hello world')
  // or call next() if you use it as middleware (as snippet #1)
}

PS: Müssen Sie sowohl "sichere" als auch "öffentliche" Pfade haben? Verwenden Sie express.routerstattdessen.

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
Qwerty
quelle
2
Das Beste von allem ... :)
Anupam Basak
2
Nicht verwenden, .split(':')da Kennwörter mit mindestens einem Doppelpunkt erstickt werden. Solche Passwörter sind gemäß RFC 2617 gültig .
Distortum
1
Sie können RegExp auch const [_, login, password] = strauth.match(/(.*?):(.*)/) || []für den Doppelpunkt verwenden.
Adabru
3
Wenn !==Sie Kennwörter vergleichen, sind Sie anfällig für Timing-Angriffe. en.wikipedia.org/wiki/Timing_attack Stellen Sie sicher, dass Sie einen konstanten Zeitstringvergleich verwenden.
Hraban
1
Verwendung Buffer.from() // for stringsoder Buffer.alloc() // for numberswie Buffer()aus Sicherheitsgründen veraltet.
Mr. Alien
71

TL; DR:

express.basicAuthist weg
basic-auth-connectist veraltet
basic-authhat keine Logik
http-authist ein Overkill
express-basic-authist was du willst

Mehr Info:

Da Sie Express verwenden, können Sie die express-basic-authMiddleware verwenden.

Siehe die Dokumente:

Beispiel:

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!
}));
rsp
quelle
17
Ich habe eine Weile challenge: true
gebraucht
1
@VitaliiZurian Guter Punkt - ich habe es der Antwort hinzugefügt. Vielen Dank für den Hinweis.
rsp
4
@rsp Wissen Sie, wie Sie dies nur auf bestimmte Routen anwenden können?
Jorge L Hernandez
Wenn Sie keine weiteren Abhängigkeiten hinzufügen möchten, ist es sehr einfach, die grundlegende Authentifizierung von Hand in eine Zeile zu schreiben ...
Qwerty
Wie würde die Client-URL aussehen?
GGEv
57

Ein Großteil der Middleware wurde in Version 4 aus dem Express-Kern gezogen und in separate Module integriert. Das grundlegende Authentifizierungsmodul finden Sie hier: https://github.com/expressjs/basic-auth-connect

Ihr Beispiel müsste nur Folgendes ändern:

var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));
Brian Prodoehl
quelle
19
Dieses Modul behauptet, veraltet zu sein (obwohl die vorgeschlagene Alternative unbefriedigend erscheint)
Arnout Engelen
3
^^ absolut unbefriedigend wie in dicht undokumentiert. Kein Beispiel für die Verwendung als Middleware, für die es wahrscheinlich gut ist, aber der Aufruf ist nicht verfügbar. Das Beispiel, das sie geben, ist allgemein und nicht für Verwendungsinformationen geeignet.
Wylie Kulik
Ja, dieser ist veraltet, und während der empfohlene nur
wenige
1
Ich habe beschrieben, wie man die basic-authBibliothek in dieser Antwort verwendet
Loourr
Wie existiert ein ganzes Modul, das darauf basiert , das Passwort im Code in Klartext zu setzen ? Zumindest die Verschleierung durch Vergleich in base64 scheint geringfügig besser zu sein.
user1944491
33

Ich habe den Code für das Original verwendet basicAuth, um die Antwort zu finden:

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

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.statusCode = 401;
        res.setHeader('WWW-Authenticate', 'Basic realm="MyRealmName"');
        res.end('Unauthorized');
    } else {
        next();
    }
});
Dov
quelle
10
Dieses Modul gilt als veraltet. Verwenden Sie stattdessen jshttp / basic-auth (dieselbe API, sodass die Antwort weiterhin gilt)
Michael
32

Ich habe in Express 4.0 die Basisauthentifizierung mit http-auth geändert , der Code lautet:

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);
WarsClon
quelle
1
Dies ist buchstäblich Plug and Play. Hervorragend.
Sidonaldson
20

Dafür scheint es mehrere Module zu geben, einige sind veraltet.

Dieser sieht aktiv aus:
https://github.com/jshttp/basic-auth

Hier ist ein Anwendungsbeispiel:

// auth.js

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

var admins = {
  '[email protected]': { 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

Stellen Sie sicher, dass Sie die authMiddleware an der richtigen Stelle platzieren. Alle zuvor vorhandenen Middleware werden nicht authentifiziert.

Michael
quelle
Ich bin eigentlich für 'basic-auth-connect', der Name ist schlecht, aber in Bezug auf die Funktionalität ist er besser als 'basic-auth'. Alles, was letzteres tut, ist der Analyseberechtigungsheader. Sie müssen implementdas Protokoll noch selbst
erstellen
Perfekt! Danke dafür. Das hat funktioniert und alles gut erklärt.
Tania Rascia
Ich habe es versucht, aber es fordert mich immer wieder auf, mich über eine Endlosschleife anzumelden.
JDOG
6

Wir können die Grundberechtigung implementieren, ohne ein Modul zu benötigen

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

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

VIKAS KOHLI
quelle
1

Express hat diese Funktionalität entfernt und empfiehlt nun die Verwendung der Basic-Auth- Bibliothek.

Hier ist ein Beispiel für die Verwendung:

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

// Create server
var server = http.createServer(function (req, res) {
  var credentials = auth(req)

  if (!credentials || credentials.name !== 'aladdin' || credentials.pass !== 'opensesame') {
    res.statusCode = 401
    res.setHeader('WWW-Authenticate', 'Basic realm="example"')
    res.end('Access denied')
  } else {
    res.end('Access granted')
  }
})

// Listen
server.listen(3000)

Um eine Anfrage an diese Route zu senden, müssen Sie einen Autorisierungsheader einfügen, der für die Basisauthentifizierung formatiert ist.

Wenn Sie zuerst eine Curl-Anfrage senden, müssen Sie die Base64- Codierung von name:passoder in diesem Fall aladdin:opensesamegleich verwendenYWxhZGRpbjpvcGVuc2VzYW1l

Ihre Curl-Anfrage sieht dann so aus:

 curl -H "Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l" http://localhost:3000/
Loourr
quelle
0
function auth (req, res, next) {
  console.log(req.headers);
  var authHeader = req.headers.authorization;
  if (!authHeader) {
      var err = new Error('You are not authenticated!');
      res.setHeader('WWW-Authenticate', 'Basic');
      err.status = 401;
      next(err);
      return;
  }
  var auth = new Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
  var user = auth[0];
  var pass = auth[1];
  if (user == 'admin' && pass == 'password') {
      next(); // authorized
  } else {
      var err = new Error('You are not authenticated!');
      res.setHeader('WWW-Authenticate', 'Basic');      
      err.status = 401;
      next(err);
  }
}
app.use(auth);
Subair Adams Ohikere
quelle
Hoffe, es wird das Problem lösen, aber bitte fügen Sie eine Erklärung Ihres Codes hinzu, damit der Benutzer ein perfektes Verständnis dafür erhält, was er wirklich will.
Jaimil Patel