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

How to chain multiple jwt auth strategies #195

Closed
Jiropole opened this issue Sep 9, 2016 · 11 comments
Closed

How to chain multiple jwt auth strategies #195

Jiropole opened this issue Sep 9, 2016 · 11 comments

Comments

@Jiropole
Copy link

Jiropole commented Sep 9, 2016

This isn't an issue per se, but I'm struggling like mad to find a solution. I have two types of clients that each have their own signing key. I need to try two jwt strategies in a row; if either of them match, the client should be authenticated.

However, this is not how Hapi's auth system works. If the first strategy fails to decode the token, it stops the chain and the second strategy is never called. I looked at #120 and #130 as they seemed similar, but I wasn't able to find a way to use verifyFunc to achieve the desired behavior.

Basically, I need the first strategy to act like "try" and the second act like "required" but I can't figure out how to achieve this. Any ideas would be most appreciated! Sorry if this isn't exactly the right forum for the question, but seems someone must have encountered this use case before...

@nelsonic
Copy link
Member

nelsonic commented Sep 9, 2016

@Jiropole good question are you able to share some code of what you have tried? (thanks!)

@Jiropole
Copy link
Author

Jiropole commented Sep 9, 2016

Before I start dumping typical code, just a bit more context. I think my confusion stems from not understanding how to use the key function. I believe I have a low-tech version of what Stongo was doing in #120. But I'm not clear how to get the original token passed to my keyfunc, since until I can run jwt.verify() with all (both) my keys, decoding will fail.

At a higher level, this is to secure an API server that serves multiple Auth0 clients, each one sending tokens signed with a different secret. So, as to what I've tried:

  • jwt strategy chaining (obviously fails as the second strategy is never called after the first is marked invalid)
  • verifyFunc - got stuck here right away because it's too late to find the right key
  • keyFunc - feels like the only place for the solution, but again too late because it's already attempted decoding.

So, this is the tree I'm trying to bark up, something like this:

    server.auth.strategy('pws-token', 'jwt', {
        key: function (decoded, callback) {
            if (decoded.constructor === String) {
                // it's a token string; attempt to verify with all keys so as to return proper key
                // async.series(JWT.verify blah blah)...
            }
        },
        verifyOptions: {
            algorithms: ['HS256'],
<snip>

@Jiropole
Copy link
Author

Jiropole commented Sep 9, 2016

Ah, I see the token is available immediately in verifyFunc in request.auth.token. So is the correct approach for my use case to use the payload to determine the proper key, then use jwt.verify() on the original token to ensure the token is properly signed?

@Jiropole
Copy link
Author

Jiropole commented Sep 9, 2016

Ok! I think I get it now (better slow than never). If you have a moment, could you verify this is a secure approach to variable-key JWT validation?

    server.auth.strategy('token', 'jwt', 'required', {
        verifyFunc: function (decoded, request, callback) {
            if (decoded.aud == pwsAudience) {
                const verifyOptions = {
                    algorithms: ['HS256'],
                    audience: pwsAudience
                };

                JWT.verify(request.auth.token, pwsKey, verifyOptions, (err, decoded) => {
                    if (err || !decoded) {
                        return callback(err, false);
                    }
                    return callback(null, true, decoded);
                });
            } else {
                const verifyOptions = {
                    algorithms: ['HS256'],
                    audience: standardAudience
                };

                JWT.verify(request.auth.token, standardKey, verifyOptions, (err, decoded) => {
                    if (err || !decoded) {
                        return callback(err, false);
                    }
                    return callback(null, true, decoded);
                });
            }
        }
    });

@Jiropole
Copy link
Author

Jiropole commented Sep 9, 2016

Sorry, this version's easier on the eyes:

    server.auth.strategy('token', 'jwt', 'required', {
        verifyFunc: function (decoded, request, callback) {
            const key = keysByAudience[decoded.aud];
            if (!key) {
                return callback(null, false);
            }

            JWT.verify(request.auth.token, key, { algorithms: ['HS256'] }, (err, decoded) => {
                if (err || !decoded) {
                    return callback(err, false);
                }
                return callback(null, true, decoded);
            });
        }
    });

@nelsonic
Copy link
Member

nelsonic commented Sep 9, 2016

@Jiropole yeah, that sounds like a good approach. Provided the client is only passing one token at any given time. 👍

@Jiropole
Copy link
Author

Jiropole commented Sep 9, 2016

Thanks very much @nelsonic; sorry for the noise. Hopefully this can help someone else.

@Jiropole Jiropole closed this as completed Sep 9, 2016
@nelsonic
Copy link
Member

nelsonic commented Sep 9, 2016

@Jiropole not at all! you're very welcome. glad we can help! ⭐

@jschr
Copy link

jschr commented Sep 9, 2016

@Jiropole Started looking for similar solution today so you've already helped at least one person. Thanks 👍

@bujardeari
Copy link

@Jiropole How does your route look ? I cant get this working

@Jiropole
Copy link
Author

@bujardeari My routes mainly rely on the required parameter in my previous code snippet, so nothing specific concerning authentication. Just your basic method path handler stuff.

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

No branches or pull requests

4 participants