0

I am creating a public API that will require API key authentication. When a user wants to register a new API key, they send a request to one of our endpoints with a list of permissions they want. We generate a unique key (uuid4) and send it back to them encrypted by TLS.

I am planning to hash the key that was created and store it as the primary key of a database. On each user request we can rehash the key they send in the headers and index the database to check if it is indeed one of the keys we've created. We'll also have their list of permissions stored in a column which we can use to authorize them for certain routes.

Question: My concern is how we're going to associate an API key to a user. Say a user wants to see a list of API keys they've generated. Is it now impossible for us to produce this list? Also say that a user loses their API key. How would we identify which API key has been lost, so we can at least delete that record in the database? I'm wondering how this stuff is usually handled.

  • This is more a database design question than a security one... – ThoriumBR Jul 17 at 17:31
0

If you hash the keys, it would not be possible to show the keys to the user ever again. Hashing can not be reversed. You can only check if a given key is linked to the user.

What is oftenly done for APIs is to create a key consisting of a public and private/secret part. The public part can then be stored in your DB, whilst the secret part follows your current flow of storing/hashing. You can then list the public keys for the user (which he can then delete if lost).

Upon authentication the user would need to provide both parts which you can check in your DB. Another option would be to request a temporary token with the public/private keypair, this token can then be used for further API calls. Note that the token should have a limited lifetime and of course must be unique among all users.

| improve this answer | |
0

Divide and conquer.

The problem in your approach is, that you want to use one key for two different things: for authentication and for authorization.

  • If a key is used for authentication, it should be kept secret. If lost, it cannot be restored and a new one should be issued.
  • If a key is used for authorization, it should not be secret. It should be open, so that both sides, user and you, can freely refer it and discuss at any time. For instance, if user wants to complain that some key does not have particular permission, where as you believe it does. Or user says that the key has expired, where as you believe it is not. How can you discuss such questions at all, if you cannot even refer the key? Referring the key would mean that it can be leaked (you or some employee of your customer support can occasionally or intentionally disclose it).

As we see, these requirements conflict to each other. If you use a single key for both, it will be impossible to satisfy all these requirements.

I'd suggest you to separate authentication and authorization from each other. Divide and conquer.

  • For authentication use user name and normal password.
    1. Use proper password hashing (stretching, derivation - whatever you name it) like Argon2, bcrypt, scrypt.
    2. If user has forgotten the password, it is easy to reset it.
    3. You can change authentication scheme at any time without changing keys used for permissions. E.g. you can use basic authentication, later on you can switch to OAuth. And all permissions keys will remain valid, they will not need any changes.
  • Use the key that you are described in your question for authorization (for permissions) only. Thus:
    1. The key does not need to be secret. You can keep it in the database as plain text.
    2. You don't need any hashing and rehashing. This will make implementation easier, simpler, will have less bugs, will require less support. Thus it will reduce your costs.
    3. In the table with keys you can have a column with referent to user. At any time you will know, what key belongs to what user. Also you can have store there any related data: validity date, the number of API calls (in case you charge users per number of API calls), etc. In particular, you will be able to inform the user that some key is going to be invalid soon (because of validity date or because of number of used API calls).
    4. Each time you check the key you will check also the user. If you see that the key does not belong to the user, you will reject the request. Thus, stealing keys from users will make no sense. Thus, users will not have to put any efforts into keeping these keys secret.
    5. It will be easy to refer the keys whenever you need, e.g. if case of troubleshooting you (your service desk) can ask user to send the problematic key per Email or per some messenger. And it will be safe, convenient and easy, because the key is not secret.
    6. For support desk and for developers it will be easy to analyze any issues, because nobody will need to care about secrecy of the key.
    7. Saving the key in the log for each request will be safe, it will not cause any security issues.
    8. Is user has lost the key, you can retrieve this key, or even all the keys of this user, from your database and send them to the user.
| improve this answer | |
0

The other answers are very helpful and are probably the best solutions for scalability and larger companies. I neglected to mention some extra information about our situation (we are a small company and we already have some users on our main site).

Looks like I've overlooked the fact that I can simply associate an API key hash with a user by adding a column for an API key hash to our already-existing users table. I wanted to make sure that users only had to pass their API key in each request to secured endpoints. I realized that this is still possible because each API key is unique, so we can do the hash and search only the column of API key hashes to find the correct user; no need for usernames.

These users have a username/password to our main site. While we won't be able to show them their API key again, we will be able to let them delete their key and generate a new one.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.