Authentication
Intro
Django Ninja provides several tools to help you deal with authentication and authorization easily, rapidly, in a standard way, without having to study and learn all the security specifications.
The core concept is that when you describe an api operation you can define an authentication object
from ninja import NinjaAPI
from ninja.security import django_auth
api = NinjaAPI(csrf=True)
@api.get("/pets", auth=django_auth)
def pets(request):
return f"Authenticated user {request.auth}"
In this example client will be able to call the pets
method only if it uses django session authentication (default is cookie based). Otherwise, a HTTP-401 error will be returned.
Automatic Openapi schema
Let's create an example where client in order to authenticate needs to pass a header :
Authorization: Bearer supersecret
from ninja.security import HttpBearer
class AuthBearer(HttpBearer):
def authenticate(self, request, token):
if token == "supersecret":
return token
@api.get("/bearer", auth=AuthBearer())
def bearer(request):
return {"token": request.auth}
Now go to docs at http://localhost:8000/api/docs:
When you click Authorize button you will get a prompt to input your authentication token
Now if you do test calls - the Authorization header will passed for every request.
Global authentication
In case you need to secure all methods of your api - you can pass auth
argument to a NinjaAPI
constructor:
from ninja import NinjaAPI, Form
from ninja.security import HttpBearer
class GlobalAuth(HttpBearer):
def authenticate(self, request, token):
if token == "supersecret":
return token
api = NinjaAPI(auth=GlobalAuth())
# @api.get(...)
# def ...
# @api.post(...)
# def ...
And if you need to overrule some of those methods you can do that on the operation level again by passing the auth
argument. In this example authentication will be disabled for /token
operation:
from ninja import NinjaAPI, Form
from ninja.security import HttpBearer
class GlobalAuth(HttpBearer):
def authenticate(self, request, token):
if token == "supersecret":
return token
api = NinjaAPI(auth=GlobalAuth())
# @api.get(...)
# def ...
# @api.post(...)
# def ...
@api.post("/token", auth=None) # < overriding global auth
def get_token(request, username: str = Form(...), password: str = Form(...)):
if username == "admin" and password == "giraffethinnknslong":
return {"token": "supersecret"}
Available auth options
Custom function
The "auth=
" argument accepts any Callable object. NinjaAPI passes authentication only if callable object returns a not None value. This return value will be assigned to request.auth
attribute.
def ip_whitelist(request):
if request.META["REMOTE_ADDR"] == "8.8.8.8":
return "8.8.8.8"
@api.get("/ipwhiltelist", auth=ip_whitelist)
def ipwhiltelist(request):
return f"Authenticated client, IP = {request.auth}"
API Key
Some APIs use API keys for authorization. An API key is a token that a client provides when making API calls. The key can be sent in the query string:
GET /something?api_key=abcdef12345
or as a request header:
GET /something HTTP/1.1
X-API-Key: abcdef12345
or as a cookie:
GET /something HTTP/1.1
Cookie: X-API-KEY=abcdef12345
Django Ninja comes with builtin classes to help you handle these cases.
in Query
from ninja.security import APIKeyQuery
from someapp.models import Client
class ApiKey(APIKeyQuery):
param_name = "api_key"
def authenticate(self, request, key):
try:
return Client.objects.get(key=key)
except Client.DoesNotExist:
pass
api_key = ApiKey()
@api.get("/apikey", auth=api_key)
def apikey(request):
assert isinstance(request.auth, Client)
return f"Hello {request.auth}"
In this example we take a token from GET['api_key']
and find a Client
in database that corresponds to this key. The Client instance will be set to request.auth
attribute
Note: param_name
- is the name of GET parameter that will be checked for. If not set - default "key
" will be used.
in Header
from ninja.security import APIKeyHeader
class ApiKey(APIKeyHeader):
param_name = "X-API-Key"
def authenticate(self, request, key):
if key == "supersecret":
return key
header_key = ApiKey()
@api.get("/headerkey", auth=header_key)
def apikey(request):
return f"Token = {request.auth}"
in Cookie
from ninja.security import APIKeyCookie
class CookieKey(APIKeyCookie):
def authenticate(self, request, key):
if key == "supersecret":
return key
cookie_key = CookieKey()
@api.get("/cookiekey", auth=cookie_key)
def apikey(request):
return f"Token = {request.auth}"
HTTP Bearer
from ninja.security import HttpBearer
class AuthBearer(HttpBearer):
def authenticate(self, request, token):
if token == "supersecret":
return token
@api.get("/bearer", auth=AuthBearer())
def bearer(request):
return {"token": request.auth}
HTTP Basic Auth
from ninja.security import HttpBasicAuth
class BasicAuth(HttpBasicAuth):
def authenticate(self, request, username, password):
if username == "admin" and password == "secret":
return username
@api.get("/basic", auth=BasicAuth())
def basic(request):
return {"httpuser": request.auth}
Multiple authenticators
The auth
argument allows also to pass multiple authenticators:
from ninja.security import APIKeyQuery, APIKeyHeader
class AuthCheck:
def authenticate(self, request, key):
if key == "supersecret":
return key
class QueryKey(AuthCheck, APIKeyQuery):
pass
class HeaderKey(AuthCheck, APIKeyHeader):
pass
@api.get("/multiple", auth=[QueryKey(), HeaderKey()])
def multiple(request):
return f"Token = {request.auth}"
In this case Django Ninja will first check the api key GET
, and if not set or invalid will check the header
key. And if both invalid will raise authentication error to response.