django-tastypie: why are api keys useful and how to support multiple auth schemes?
Asked Answered
B

3

6

I'm designing a website in which people will sign on as users and potentially be in multiple groups, which come in a couple of different types. I wanted to both have a website that people can use directly, as well as expose an API that can be consumed by other websites.

What's the best way to implement a login system that works both for regular users of the site itself, as well as which allow API-consuming websites to seamlessly create an account on behalf of the user and allow the user to view their data both from my website and the API-consuming website?

I'm using Django 1.5, so I'm willing to customize the user model and all that. The API will be furnished using Tastypie.

EDIT: Honestly, my main problem is that I don't really understand when API keys are useful and how they coexist (if they do) with regular user logins.

Betthel answered 7/7, 2013 at 3:9 Comment(0)
W
11

Use case:

The first and use case for API keys is automation. You provide your api key (or commonly called token) to a 3rd party and voila, you can have the 3rd party do stuff for you. When you do not trust the 3rd party anymore, you can just revoke the api key or re-generate it. Api keys allow the user to initiate & authenticate the chain of actions by requesting the token via traditional authentication (e.g username/password), and then the user passes it on to the interested parties. See my little story about phone numbers at the end.

Why not use passwords?

Because you don't want to compromise your users on other websites and have them type their password there in order to use your APIs. If the 3rd party is compromised, then the user's communications or password are compromised.

Here are good resources:


Api keys with tastypie

Here is a good starting point:

from django.contrib.auth.models import User
from django.db import models
from tastypie.models import create_api_key
models.signals.post_save.connect(create_api_key, sender=User)

This should take care of creating api keys thanks to the post_save signal. The second part is to allow more than one scheme for the authentication to fit your use case, so... onto MultiAuthentication:

from django.contrib.auth.models import User
from tastypie.authentication import BasicAuthentication, ApiKeyAuthentication, MultiAuthentication
from tastypie.authorization import DjangoAuthorization
from tastypie.resources import ModelResource

class UserResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        resource_name = 'auth/user'
        excludes = ['email', 'password', 'is_superuser']

        authentication = MultiAuthentication(BasicAuthentication(), ApiKeyAuthentication())
        authorization = DjangoAuthorization()

Other considerations

These are good practice to never leak sensitive data on the network:

  • do not do any user/password authentication without ssl or tls.
  • never use http basic authentication without ssl or tls.
  • lock down resources users shouldn't access to with permissions.
  • make sure your responses have correct cache-control headers
  • always allow users to reset/regenerate/delete their tokens / api keys.
  • a user need not have only one api key, you can have several of them; one for each 3rd party.

I use gmail so here is an example from https://security.google.com/settings/security (review permissions) where I can see how google openid is used:

revoking permissions for google openid

Why should you use one api key for each 3rd party? is the same as why should you allow the user to have more than one api key and label them for a particular use?.

The answer is that if the api key is something you share just like your phone number, but you don't need to give the same phone number to all your friends (google voice FTW!).

  • case 1: one phone number.

If your friend misbehaves and gives it to a bunch of sales rep, you are going to be pretty annoyed. If your phone is the same, then whoever has it may share it with somebody else without your knowing. End results, all sales reps know your number... not so good. But you still want your mom to be able to call you, right? So you can't really change your number. Now imagine a more dangerous situation; you have a tab at the local pizzeria, and they know you by name / phone number. If someone gets your api key, they may impersonate you to order pizza and still charge you (you have a tab!).

  • case 2: multiple phone numbers:

If you have 100 numbers to you give to 100 different friends, not only can they contact you, but if a sales rep calls you on a particular number, you would know which of your friend gave it away and you can just cut that one number. Mom's now happy because she can tell you where to go for brunch. Your friend now decides to order pizza ... you can now trace it to your friend (or you should provide the pizzeria a number that none of your friend knows).

Wideangle answered 7/7, 2013 at 3:9 Comment(2)
So let me confirm. A user might start by logging onto my site by themselves, so they just have username/password for authentication at that point (over HTTPS of course). Then a user could join a 3rd-party site and the site provides them a link to my site to authorize that 3rd-party to use an API key, allowing the 3rd-party to then make requests on their behalf. Am I understanding that part right? And from this, should I infer that API keys are a many-to-many entity linking users with the 3rd-parties? For some reason, I thought it was just one-to-many (my site to the other sites).Betthel
To amend my last question, is it more secure to allow users an API key per site (each of which can be independently reset or revoked as needed)?Betthel
M
0

From a security standpoint there are two main classes of "API Keys" with different purposes for each:

  • A random API-key that acts sort of like a password and username combined: It is hardwired into client configuration files and is considered a secret. Simply by possessing this key, you have API access. Because the key represents a secret identifying the client (thus validating trust), the secret must be transmitted with every request. The connection must be trusted to ensure the secret is not intercepted, meaning all API access must require TLS/SSL. Often this approach is extended by only using the key on the first request, with the remainder of the connection performed using a rotating session key linked to the client's IP address (much like logging in on a website)
  • An asymmetric signature scheme: An asymetric cypher like DSA or RSA is used to create a secret key that is stored in the client's configuration. The secret is never shared on the network, and instead encrypts a hash of each API request. The server keeps a public key linked to each private key which decrypts each request's hash signature, and then creates its own hash of the request to validate against. This is the recommended and ideal way to perform API authentication because it does not require TLS/SSL and requires no session management policy.

I am not aware of an API authentication library for django and would recommend looking at the source code of Amazon AWS's HMAC asymmetric signature based approach for an excellent implementation.

Motivation answered 16/7, 2013 at 19:53 Comment(0)
E
0

I would design the system so that every request needs to have an API key. Ideally you put that in the Authorization header of every request. The API should be as purely RESTFul as possible, ie no session handling. I think thats very important for good API design.

Once that part is done, your users will access the site in exactly the same manor. However, they won't remember their API key, but only their credentials. So, have a login page that authenticates the user based on their credentials, then return the api key to be stored in the client's session. From then on all requests should be authorized with the API key.

Ellinger answered 17/7, 2013 at 12:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.