8

I need to generate a secure token for access to an api. User will auth and on successful auth I will need to generate a token.

Important: I do have a requirement that I need to be able to revoke a users access to the api at any time.

Option 1:

I can generate a random string and use that as the token. Store the token and the user in the db. When the user passes the token I check the DB for the token, if it exists go to go...

This gives me the ability to revoke access by removing a users token. If they tried to log back in and the token was gone they won't have access. I can also expire tokens from the DB based on time.

I am using nodejs and have seen this:

Secure random token in Node.js

require('crypto').randomBytes(48, function(ex, buf) {
  var token = buf.toString('hex');
});

Is that really secure in that someone could not guess a token that I have generated?

Option 2:

Use something like jwt. On auth generate a jwt with the users id. When the user passes that jwt on a request I make sure jwt is valid and if so grab user id and good to go. This seems a lot more secure as jwt prevents tampering.

Problem is revoking access to the api. I could store all jwts (until they expire) in the db, on request validated jwt and make sure its in my db. If I want to revoke I can just remove it from the db. Con here is that I now have the overhead of validating the jwt and looking it up to ensure its in my db.

Is there a good way to revoke access using jwt?

Community
  • 1
  • 1
lostintranslation
  • 23,756
  • 50
  • 159
  • 262
  • 1
    Storing jwt's and checking their status in a db negates the main plus point of these tokens - that once authenticated you can pass the tokens around and no further lookups are required. You could possibly set a short expiry time but that could annoy users who should be left logged in. JWT's probably aren't what you're looking for. – NickJHoran Aug 22 '15 at 20:51
  • https://auth0.com/docs/refresh-token – NickJHoran Aug 22 '15 at 20:55
  • @NickJHoran thanks! You are correct about the main plus point, which is why that solution seemed bad. However everywhere I look it seems people are asking how to 'revoke' jwts. Happens if you need to do it for security reasons or if a user changes there password. I saw you link to refresh token. Is that a possible answer? – lostintranslation Aug 22 '15 at 21:19
  • It does really depend on your design requirements but refresh tokens could potentially work for you. – NickJHoran Aug 22 '15 at 21:22

1 Answers1

3

This question will probably invoke a lot of opinion in the answers, so ultimately its up to you and your security requirements.

One solution I like is generating random characters in the same way you've suggested, and include with it something the user has (such as an email address or an ID). Then also include a timestamp to know when it was generated. Take those things and concat them together into a single string (perhaps using some character to split the parts up). So you'd end up with something like:

<random-string-of-characters>|<user-email-address>|<timestamp>

Now encrypt that string, perhaps using something like bcrypt, and that blob ends up being what you would call the "token". The internal users of this don't have to understand it, they just need to store it and later access it to send it to your security layer to validate. You validate it by unencrypting it and verifying the parts.

This solution gives you complete control on how you grant the user access. You can continue to store the generated characters in the database and revoke it at any time, or look at the timestamp to see if they are "timed out".

I suppose there are a lot of solutions to this, so this is just one :)

dylants
  • 22,316
  • 3
  • 26
  • 22
  • Yeah I tried to avoid opinion answers when writing this, was hard to do. Thanks for the answer. Bcyrpt seems like it would give a little added security but at the expense of compute time. Guess I just don't know if using bcyrpt is overkill for a short lived token. – lostintranslation Aug 22 '15 at 21:23
  • Yeah, it depends on how secure you'd like this to be :) You could even just generate a [UUID](https://www.npmjs.com/package/uuid) and use that as the token if you were less security concerned. Lots of choices out there! – dylants Aug 22 '15 at 21:27
  • Isn't bcrypt a one-way algorithm? You can only compare the given hash with the parts that make it up and run it through bcrypt and it'd tell if it matches or not? like so `bcrypt.compare(`${uuid}|${email}|${createdAt}`, hash, function(err, result) {...}` which means you'd have to store the uuid along with the creation date in order to be able to tell the token later on. So if a malicious attacker gained access to the db he can recreate the token which renders the hashing useless! – Harry Adel Nov 11 '21 at 20:37
  • I don't think you'd have to store the uuid & creation date for the token usage (Although you'll probably still store them). Couldn't you just store the token and link it to an account in the db? – Tembero Dec 14 '21 at 20:48