I'm trying to build a RESTful api with restify.js, but I don't want to expose the api to everyone. And I'm going to use token-based authentication. The process in my mind is like this, I'm not sure whether it is reasonable.
the user send username/password to an api to acquire the token.
this token should be included in the request for the calls of every other api.
If this is reasonable, is there any node.js library I can use?
In addition, how do I protect the token? If someone intercept a http request with the token, then that person will get the api url and the token. Then he can send request as he wants. Is there a way to avoid this?
Thanks a lot!
Restify is bundled with an authorizationParser
plugin. authorizationParser
parser out the Authorization
. When the plugin is in use, it will make req.username
and req.authorization
properties available. Format of the latter is:
{
scheme: <Basic|Signature|...>,
credentials: <Undecoded value of header>,
basic: {
username: $user
password: $password
}
}
Your server will need to selectively intercept the requests that require authentication and validate user access credentials.
Here is an example server that will require authentication for all calls:
var restify = require('restify'),
server;
server = restify.createServer();
server.use(restify.authorizationParser());
server.use(function (req, res, next) {
var users;
// if (/* some condition determining whether the resource requires authentication */) {
// return next();
// }
users = {
foo: {
id: 1,
password: 'bar'
}
};
// Ensure that user is not anonymous; and
// That user exists; and
// That user password matches the record in the database.
if (req.username == 'anonymous' || !users[req.username] || req.authorization.basic.password !== users[req.username].password) {
// Respond with { code: 'NotAuthorized', message: '' }
next(new restify.NotAuthorizedError());
} else {
next();
}
next();
});
server.get('/ping', function (req, res, next) {
res.send('pong');
next();
});
server.listen(8080);
The easiest way to test is using curl:
$ curl -isu foo:bar http://127.0.0.1:8080/ping
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 6
Date: Fri, 12 Dec 2014 10:52:17 GMT
Connection: keep-alive
"pong"
$ curl -isu foo:baz http://127.0.0.1:8080/ping
HTTP/1.1 403 Forbidden
Content-Type: application/json
Content-Length: 37
Date: Fri, 12 Dec 2014 10:52:31 GMT
Connection: keep-alive
{"code":"NotAuthorized","message":""}
Restify comes with inbuilt JsonClient that supports basic authentication, e.g.
var restify = require('restify'),
client;
client = restify.createJsonClient({
url: 'http://127.0.0.1:8080'
});
client.basicAuth('foo', 'bar');
client.get('/ping', function(err, req, res, obj) {
console.log(obj);
});
If you prefer the token authentication, then you can use restify-oauth2 package that implements Client Credentials authentication flow, which is what you are after.
The documentation page describes step-by-step how to setup such authentication, including roles of each endpoint, and there is a code example in their repository.
Summary
Regardless of which method of authentication you choose, all of them require you to use HTTPS. The difference is that if username/password is compromised, user would need to change their credentials. If token is compromised, then user would need to request a new token. The latter can be done programmatically, while the former usually relies on hardcoded values.
Side note. In production, credentials must be considered "compromised" if transferred at least once over an insecure channel, e.g. compromised HTTPS, as in case of SSL bug, such as Heartbleed.