Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamically resolve key based on token #9

Open
dschenkelman opened this issue Oct 7, 2014 · 16 comments · May be fixed by #21
Open

Dynamically resolve key based on token #9

dschenkelman opened this issue Oct 7, 2014 · 16 comments · May be fixed by #21

Comments

@dschenkelman
Copy link
Contributor

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
Copy link

bbrown commented Oct 14, 2014

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

@ryanfitz
Copy link
Owner

@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
Copy link
Contributor Author

@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
Copy link

bbrown commented Oct 15, 2014

@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
Copy link
Contributor Author

@ryanfitz should I close this one?

@martinj
Copy link

martinj commented Dec 9, 2014

Would also like to see this PR merged..

@eventhough
Copy link

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
Copy link
Contributor Author

@eventhough that's exactly what the PR does. Here's how it works: auth0#3 (comment)

@nelsonic
Copy link

@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
Copy link
Contributor Author

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

How would the session id work?

  • We need to verify the validity of the claims inside the token and the only way to do it is to validate the token.
  • In our multi tenant system multiple secrets can be used to generate the JWT (one per tenant).
  • Each tenant can create their own JWTs, we can't share a secret among them, so the only way to know to that the claims inside a JWT are valid is to look up their secret in a DB (of course these are cached and there's usually no DB hit).

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

@nelsonic
Copy link

@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
Copy link
Contributor Author

@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
Copy link

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
Copy link
Contributor Author

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
Copy link

ksck23 commented Mar 31, 2015

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
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants