ryanfitz / hapi-auth-jwt

JSON Web Token (JWT) authentication plugin
Other
169 stars 49 forks source link

Dynamically resolve key based on token #9

Open dschenkelman opened 10 years ago

dschenkelman commented 10 years ago

In order to easily support multi tenant scenarios, it would be useful to have optionally have a callback function take the JWT and return the key.

To keep the API simple the idea would be to keep the current behavior if key is not a function. If key is a function then this fragment would look like this:

var token = parts[1];

var key = isFunction(settings.key) ? settings.key(token) : settings.key;

jwt.verify(token, key, function(err, decoded) {

What do you think about it?

I'm working on a PR with the updated docs, implementation and tests.

bbrown commented 10 years ago

This would be very helpful and is well supported in the node-jsonwebtoken library on which this depends.

ryanfitz commented 10 years ago

@dschenkelman @bbrown Wouldn't a better solution be to use Hapi's pack system and also the ability to register multiple variations of the same auth schema? With Hapi you can create a pack of servers, giving you the ability to have server A setup with token auth key A, and then also have server B setup with token auth key B.

dschenkelman commented 10 years ago

@ryanfitz thanks for the suggestion, but unfortunately that would not work for us :(

We have a scenario in which users can create an arbitrary number of applications, each with their own private key. Thus we have no way of telling up front the amount of servers we would need, and instead need information embedded in each JWT to figure out the key to be used.

bbrown commented 10 years ago

@ryanfitz Our situation is that we want to have each token signed with the particular user's secret. That way we can deactivate a particular user without having to change the common secret, which would kick everyone out of the application.

dschenkelman commented 9 years ago

@ryanfitz should I close this one?

martinj commented 9 years ago

Would also like to see this PR merged..

eventhough commented 9 years ago

Would it be better if the plugin had an optional setting that allowed us to pass in a function to do the key look up? So along with validateFunc() we could also pass in something like keyLookUp(decoded, callback) that takes the decoded token and a callback to return a key, otherwise just take the key directly from settings?

dschenkelman commented 9 years ago

@eventhough that's exactly what the PR does. Here's how it works: https://github.com/auth0/hapi-auth-jwt/issues/3#issuecomment-70019337

nelsonic commented 9 years ago

@dschenkelman totally understand that you want to support multi-tenant scenarios, but what is the advantage to having a dynamic key instead of including a session id (_jti_) inside the JWT which can be looked up after the JWT has been validated?

The JWT.verify function is computed on the Hapi node so is fast (no network latency). If that succeeds, we confirm the session is still valid with a database check. If you have to lookup the key for every request, it makes your app much more susceptible to DDOS because the attacker knows they can force a database flood simply by hitting a protected url.

dschenkelman commented 9 years ago

@nelsonic I see what you mean and we address that in a different way (the DDoS).

How would the session id work?

More info about what we are doing here. Not trying to promote, just provide more info without typing everything again.

nelsonic commented 9 years ago

@dschenkelman using the JWT in your example you would lookup the jti (_unique token_) after the JWT.verify returns valid.

Even with good caching (memcache/redis) you are still forcing your app to do a lookup for all invalid requests instead of letting JWT.verify do the work of verification before the lookup.

How are you mitigating against DDOS? (e.g. does your load-balancer limit requests/sec per IP?)

dschenkelman commented 9 years ago

@nelsonic there's something I don't understand, would you give the same secret to all users? How do you know which secret to use to verify?

Regarding DDoS mitigation: something like that, sorry I can't provide a lot of details.

nelsonic commented 9 years ago

The only "secret" we are giving users is the jti (inside the JWT payload) which we look up in redis after the JWT has been verified. I don't see how having a unique signing key per user increases security or improves multi-tenancy.

I hope I haven't miss-understood the JWT (draft) spec ... I'm not the expert in this, but the &yet guys might be able to provide some insight:

@evilpacket @mattlowe @diasdavid : should we be using a unique key (or key+salt) to sign each JWT? _or_ can we use the same key to sign every JWT?

// a unique key for each JWT
var jwt = require('jsonwebtoken');
db.get(uid, function(err, key) {
  var token = jwt.sign({ foo: 'bar' }, key);
  // callback(err, token);
}
// one key to sign them all
var key = fs.readFileSync('private.key');  // get private key
var token = jwt.sign({ foo: 'bar' }, key);

Unless the _dynamic key_ implementation allows _Perfect forward secrecy_ ...? (in which case I'm curious how...)

dschenkelman commented 9 years ago

Note that per user and per tenant can be different things, depending on the perspective. We have multiple tenants (one secret each) and each tenants has multiple users (same secret for all, it's the tenant's secret).

If you use the same secret for all tenants (and share it with them as we do), then anyone can create a token with a valid signature.

Thus, verifying the token is rendered useless since it gives you no information whatsoever (anyone could have generated it). You would be basically just trusting the jti (the only differential piece) which is like falling back to using opaque secrets.

ksck23 commented 9 years ago

I agree with @dschenkelman We have a similar use case trying to build a federated identity provider that issues JWTs by authenticating with multiple auth servers (ideally each one belongs to a different partner in the company).

We would like to use different secrets to sign users data belonging to different partners. Our application will be accepting JWTs from the federated identity server for all the users (union of users belonging to all the partners). While verifying we would need to dynamically detect the respective partner (exists within the token payload) and use the respective public key.

Could you please tell me if you have any plans to merge this functionality sometime in the coming weeks ?

nelsonic commented 9 years ago

@dschenkelman / @ksck23 / @bbrown / @martinj in case you still need/want the dynamic keys feature, @eventhough added it to hapi-auth-jwt2

example usage: https://github.com/dwyl/hapi-auth-jwt2/blob/d1f842ea22745f8095f6216fd508d4c165485740/test/dynamic-key-server.js