Authentication
Ninja provides several different classes for authentication management for your API endpoints. In this lesson, you’ll use Django Auth and a key-based mechanism for authenticating your endpoints.
To learn more about the concepts covered in this lesson, check out:
00:00 In the previous lesson, I showed you a few ways to organize larger APIs. In this lesson, I’ll demonstrate two of the many ways Ninja does authentication.
00:11 Ninja has a pluggable interface for authentication and comes with several different mechanisms, including Django’s auth method, using API keys in a variety of ways, HTTP Basic Auth, and HTTP Bearer.
00:27 You can also use multiple methods at a time, with the first successful method, allowing the user into the endpoint. You can specify the use of authentication method globally, in a router, or down at the view level.
00:40 This allows you to control parts of your API while leaving other parts open.
00:47
Django uses a token to help prevent Cross Site Request Forgery attacks. This is included as the csrf-tag
when you write a form. You can ask Ninja to require this as well, by setting csrf
to True
in the NinjaAPI
object instructor.
01:06 Let’s go secure Castle Black.
01:11
I’m going to take advantage of something I showed you in the previous lesson. I’m going to use a second NinjaAPI
object. I’m doing this because I want to turn the CSRF protection on, but I don’t want to break any of our public APIs previously written.
01:26
Let me just pop over to the urls
file.
01:33
Because the object is declared elsewhere, I’m importing it here, then adding it as its own path. The stuff written before will continue to be public under "/api"
and the new stuff will be under "/secure"
. All right, let’s go back to the view.
01:55
This is inside of /nwatch/api
. The first example will use Django’s authentication method. To do that, I need to get the django_auth
function from Ninja’s security
module.
02:08
As I mentioned before, here is my second Ninja object. It has csrf
turned on, and I’ve provided a specific version tag as well. As I spoke about in the previous lesson, I’m skipping the router step here and using the NinjaAPI
object directly, not the API one in urls
, but the secure one that I’ve just created above. Like a router, I access the method on this API object. In this case, it’s the .get()
method, and I’m setting its auth
parameter to the django_auth
function, indicating that I want Django authentication required for this endpoint. The rest of what you see here is what you’d expect from a view.
02:58
Hitting the URL gets me access denied. It is possible to authenticate this with curl
, but it’s painful. You’ve got to hit the login page, get the right cookies, send them all back.
03:08 Instead, let’s go log into the admin and use the Ninja docs interface.
03:16 As I haven’t set any other kind of authentication up, I’m going to log into the admin with an admin ID that I created behind the scenes.
03:27
Now that I authenticated, I’m going to change the URL directly to the newly created Ninja endpoint (/secure/docs
).
03:38
Remember to skip the /
(slash). Ninja doesn’t like ending slashes, for whatever reason. And here is the elevator method. I’m going to open it up, try it out …
03:54
and there you go. Ascending to the wall. If I hadn’t authenticated with the admin, I would’ve been denied here, as I was with curl
.
04:06
django_auth
isn’t a very good way of securing an API. Usually, if you’re supporting an API, you don’t require someone to sign in as well, although it can be handy if you’re building an admin page in Vue or React and augmenting stuff you’re already doing in the Django admin for your support staff.
04:25
The more common way to secure an API is with an API key. Ninja provides several different classes for key-based authentication. The example I’m going to show you here expects the key to be inside of an HTTP header. Logically enough, you extend the APIKeyHeader
class in the security model.
04:45
In order to do this, in the class, you set the param_name
attribute to the name of the header to look for. I’ve chosen "X-API-KEY"
.
04:54
And then you implement an .authenticate()
method. This method takes an HTTP request object and the key that it found in the given header. This method is truthy.
05:07
Returning None
or False
will result in access denied. Here, I’m checking for a hard-coded key, but in the real world, you’re usually looking the key up in a database. To use your new class, you instantiate it and then pass it as a callable to the auth
parameter of your decorator. Let’s try this out.
05:29
I’m gonna pass in X-API-KEY
as a header with a value of "jonsnow"
using the -H
flag with curl
. And there it is at the bottom, one blast to the eerie-sounding horn.
05:45 One quick thing to note here, if you’re writing unit tests for your APIs. Django’s dev server and the test harness that goes with it expects CGI-style header declarations.
05:56
If you’re setting the headers in TestCase
’s client object, you’ll need to name it HTTP_X_API_KEY
, all caps, rather than what I’ve used in curl
.
06:10 This can be a tricky thing to debug if you forget it, as Django just eats the headers it doesn’t like. They just disappear. I’m obviously speaking from rather recent and rather painful experience. Hours of my life I just won’t get back. Let’s go look at this in the browser.
06:30
I’ve reloaded the page, and the /downbelow
API endpoint has appeared. There is a convenient Authorize button here in the top right corner.
06:40
It has a rather useless section for session keys—and if you wanted to dig that out of your authenticated Django session, you could paste it here—or the much more helpful APIKey
mechanism. Ninja knows what classes are in use, so it tells you what header it will set. All you have to do is type "jonsnow"
07:15 And there you have it, a secure Castle Black with a crappy password. You should never set your password the same as your username. Do you know nothing, Jon Snow?
07:27 Ninja comes with a bunch of authentication classes. There are two more API key mechanisms, one that uses the query string instead of a header and the other that uses a cookie instead of a header.
07:39
There’s also a class for HTTP Bearer and Basic Auth if you feel all old school. You can also write your own function and pass a reference to it in the auth
parameter. Like the authenticate method in the classes, the function just needs to be truthy.
07:56
Whatever your function or authenticate method returns is available in the request object under an attribute called auth
. This can be handy if you need to pass more information back to the view.
08:07
The example in the Ninja docs shows an IP allow list. It checks the user’s IP address, and instead of just returning True
, returns the value of the IP, making it easily available in the view.
08:20
Be careful with this, though. The truthiness of strings can be a bit tricky. Any non-empty string is True
, even if that string contains 0
or the word False
. With all these choices, how do you choose? My personal preference is the technique that I showed you in the code. I usually use a header-based API key. In a recent project, I had a system where a business owner would send a private link to one of their customers. Each customer got their own key, which was stored in the database, and the link contained that key in the URL.
08:54
The JavaScript on the customer’s page would grab the key out of the URL and put it in the X-API-KEY
header for any calls to the API endpoint.
09:03
The APIKeyHeader
class’s authentication method would see the header and search the database for a matching key. If a key was found, the customer was granted access to the info on the page. To provide an adequate level of security, you want the keys to be fairly long.
09:19 If you’ve ever used Google Docs and looked at the unguessable URL that they use for sharing, they’re essentially using this concept. The long key is part of their huge URL path.
09:31 Authentication is a deep topic in its own, right? For more information, see the links below. You’re almost a verifiable Ninja. One more lesson to go before wrapping things up. Next up, I’ll show you how to handle errors.
Become a Member to join the conversation.