53

I'm trying to write a REST-API server with NodeJS like the one used by Joyent, and everything is ok except I can't verify a normal user's authentication. If I jump to a terminal and do curl -u username:password localhost:8000 -X GET, I can't get the values username:password on the NodeJS http server. If my NodeJS http server is something like

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, "127.0.0.1");

, shouldn't I get the values username:password somewhere in the req object that comes from the callback ? How can I get those values without having to use Connect's basic http auth ?

3
  • 1
    console.dir(req.headers) Commented May 10, 2011 at 14:53
  • console.dir(req.headers) only outputs { authorization: 'Basic am9hb2plcm9uaW1vOmJsYWJsYWJsYQ==', 'user-agent': 'curl/7.21.3 (x86_64-pc-linux-gnu) libcurl/7.21.3 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18', host: 'localhost:8000', accept: '/' } Commented May 10, 2011 at 15:15
  • 1
    for Express 4 see this answer Commented Jun 12, 2014 at 14:51

7 Answers 7

105

The username:password is contained in the Authorization header as a base64-encoded string.

Try this:

const http = require('http');
 
http.createServer(function (req, res) {
  var header = req.headers.authorization || '';       // get the auth header
  var token = header.split(/\s+/).pop() || '';        // and the encoded auth token
  var auth = Buffer.from(token, 'base64').toString(); // convert from base64
  var parts = auth.split(/:/);                        // split on colon
  var username = parts.shift();                       // username is first
  var password = parts.join(':');                     // everything else is the password
 
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('username is "' + username + '" and password is "' + password + '"');
}).listen(1337, '127.0.0.1');

From HTTP Authentication: Basic and Digest Access Authentication - Part 2 Basic Authentication Scheme (Pages 4-5)

Basic Authentication in Backus-Naur Form

basic-credentials = base64-user-pass
base64-user-pass  = <base64 [4] encoding of user-pass,
                    except not limited to 76 char/line>
user-pass   = userid ":" password
userid      = *<TEXT excluding ":">
password    = *TEXT
Sign up to request clarification or add additional context in comments.

5 Comments

Don't forget to parse out the "Basic", i.e.: 'Basic abcdef0123456789' === req.headers.authorization
The third line in the example already does this by splitting the header on whitespace to produce ["Basic","abcdef0123456789"] and popping off the last value, which will be the authorization token.
It's much better to use the express.basicAuth method instead to handle all the issues. Simpler and cleaner.
Agree, but the OP didn't wish to do so.
This may fail, if the password contains a colon.
38

If you're using express, you can use the connect plugin (included with express):

//Load express
var express = require('express');

//User validation
var auth = express.basicAuth(function(user, pass) {     
   return (user == "super" && pass == "secret");
},'Super duper secret area');

//Password protected area
app.get('/admin', auth, routes.admin);

2 Comments

I don't believe this works any longer from Express 4 onwards. See stackoverflow.com/questions/24283848/… or use something like npmjs.org/package/http-auth as an alternative
Yes http-auth should just do fine.
8

You can use node-http-digest for basic auth or everyauth, if adding authorization from external services are in you roadmap.

Comments

6

I use this code for my own starter sites with auth.

It does several things:

  • basic auth
  • return index.html for / route
  • serve content without crashing and silent handle the error
  • allow port parameter when running
  • minimal amount of logging

Before using the code, npm install express

var express = require("express");
var app = express();

//User validation
var auth = express.basicAuth(function(user, pass) {     
     return (user == "username" && pass == "password") ? true : false;
},'dev area');

/* serves main page */
app.get("/", auth, function(req, res) {
try{
    res.sendfile('index.html')
}catch(e){}
});

/* add your other paths here */

/* serves all the static files */
app.get(/^(.+)$/, auth, function(req, res){ 
try{
    console.log('static file request : ' + req.params);
    res.sendfile( __dirname + req.params[0]); 
}catch(e){}
});

var port = process.env.PORT || 8080;
app.listen(port, function() {
    console.log("Listening on " + port);
});

Comments

5

It can be implemented easily in pure node.js with no dependency, this is my version which is based on this answer for express.js but simplified so you can see the basic idea easily:

const http = require('http');

http.createServer(function (req, res) {
    const userpass = Buffer.from(
        (req.headers.authorization || '').split(' ')[1] || '',
        'base64'
    ).toString();
    if (userpass !== 'username:password') {
        res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="nope"' });
        res.end('HTTP Error 401 Unauthorized: Access is denied');
        return;
    }
    res.end('You are in! Yay!!');
}).listen(1337, '127.0.0.1');

Comments

3

The restify framework (http://mcavage.github.com/node-restify/) includes an authorization header parser for "basic" and "signature" authentication schemes.

Comments

1

You can use http-auth module

// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
    realm: "Simon Area.",
    file: __dirname + "/../data/users.htpasswd" // gevorg:gpass, Sarah:testpass ...
});

// Creating new HTTP server.
http.createServer(basic, function(req, res) {
    res.end("Welcome to private area - " + req.user + "!");
}).listen(1337);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.