Skip to content
This repository has been archived by the owner on Feb 24, 2021. It is now read-only.

Refining JWT use: removing non-expiry vulnerability, enabling sessionless authentication, size considerations #52

Closed
heysailor opened this issue Apr 12, 2018 · 4 comments · Fixed by #55
Projects

Comments

@heysailor
Copy link
Contributor

heysailor commented Apr 12, 2018

Thanks for a great looking project. Reading your docs, it looks like you're oriented towards establishing a session with an API using a cookie, whether standalone or integrated, after obtaining a JWT via ooth to show authentication status.

However:

  • I think there is a security issue with the use of non expiring JWTs
  • ...which if fixed will allow use of ooth for sessionless authentication.
  • In addition, the use of the JWT tokens to transfer user data makes them less than ideal for sessionless authentication.

Non expiring JWT vulnerability

Looking at the ooth module, a non-expiring JWT is issued in exchange for a valid login. This means you can never log out a user - if they have the JWT, they can always pass it to the API for use, and it will be valid. Unless the API tracks a JWT blacklist, it will be unable to block malicious users. Tracking a blacklist means database calls, and the point of JWTs is to prove authentication without database access.

ooth/src/index.js:319:

  getToken(user) {
    return sign({ user }, this.sharedSecret);
  }

I think it would be preferable to use short lived JWTs. This ensures that creating a session with the API can only be done for a limited amount of time. The session cookie can be as long as the API wishes.

The JWT expiry can be set as an exp field as observed by Passport.

  getToken(user) {
    return sign({ 
       user,
       exp: ...some time in the future
     }, this.sharedSecret);
  }

Enabling sessionless JWT authentication with refresh tokens

There's a significant use case of authenticating every API request with a JWT, instead of a cookie based session. This is the typical authentication method used in mobile apps, eg React Native. For this use case, if JWTs are to have an expiry, a method must be provided to obtain new JWTs without logging in every time.

A refresh token is typically used: https://www.jaygould.co.uk/dev/2017/07/18/jwt-token-refresh-redux-react-native.html

In short, using the refresh token causes a database check to ensure:

  • the refresh token has not been revoked
  • the user can still be authenticated/has the same authorisation

The UI experience for the user is the same, but with more security:

  • the JWT token is used as before, enabling authenticated access without database calls, but expires regularly
  • the refresh token is used now and then to obtain a new JWT, without requiring the user to log in repeatedly. Its use triggers an authentication check to the database.

It seems this would be handy for many and will increase security.

JWT size/single concern contents

A disadvantage to using the ooth JWT token for authenticating all requests is that the ooth use does not quite adhere to the JWT spec....they are mean to be compact.

Ooth places the user profile uniqueFields information into the JWT. Okayish if the JWT is just used once to send to an API to establish a cookie-based session. But sending a large JWT token for every request becomes a problem when the user profile is large - eg Facebook data.

For instance, when logging in with the local strategy, the JWT token provided on login contains:

{
    user: {
         _id: <user id>,
        local: {
            username: undefined,
            email: <user email>,
           verified: undefined
        }
    }
}

The JWT is being used to transfer user data, rather than the single concern of authentication status. This mix of concerns is reflected in the response body, which has both a user field, containing the same information as encoded in the token field.

The minimal, single-concern JWT token implementation would be to only place the user _id (and perhaps as a future feature authorisation data such as roles or similar) into the JWT. The fact of having a valid JWT with that user id means that authentication has occurred. If the API needs more user data, it can always access the database.

This is how Meteor approaches tokens - a resumeToken isn't even a JWT; it's simply a random string which optionally expires, see line 589. Holding the resumeToken signifies only that the user was authenticated; the token itself contains no data.

Minimal JWT:

{
    _id: <user id>,
    exp: <UNIX time in future>,
    iat: <UNIX time of issue>
}

Naturally, it's still very handy/essential for each strategy to administer profile fields. They can still be provided in the user field of the response body, or accessed by an API from the database.

Happy to potter along with a PR(s) to address these issues, but would be great to get thoughts before starting.

@aaronmgdr
Copy link
Contributor

I believe this is what the standalone option does

@heysailor
Copy link
Contributor Author

heysailor commented Apr 12, 2018 via email

@heysailor heysailor changed the title Refresh tokens Expiring JWTs & Refresh tokens Apr 13, 2018
@heysailor heysailor changed the title Expiring JWTs & Refresh tokens Refining JWT use: Expiring JWTs & Refresh tokens, size considerations Apr 13, 2018
@heysailor heysailor changed the title Refining JWT use: Expiring JWTs & Refresh tokens, size considerations Refining JWT use: removing non-expiry vulnerability, enabling sessionless authentication, size considerations Apr 13, 2018
@nickredmark
Copy link
Owner

Hi @heysailor thank you for your extensive ticket. Sorry I didn't get back to you as of yet, was completely off the grid for 2 weeks.

@nickredmark nickredmark reopened this Apr 23, 2018
@nickredmark nickredmark added this to Backlog in Ooth Jun 1, 2018
@nickredmark nickredmark moved this from Backlog to Prioritized in Ooth Jun 1, 2018
@nickredmark nickredmark moved this from Prioritized to Doing in Ooth Jun 1, 2018
@nickredmark
Copy link
Owner

Thank you for your help, this is now released.

@nickredmark nickredmark moved this from Doing to Done June 2018 in Ooth Jun 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
Ooth
Done June 2018
3 participants