Skip to content

Authentication

Introduction

Django Ninja provides several tools to help you handle authentication and authorization easily, quickly, and in a standard way, without having to study and learn all the security specifications.

The core idea 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, the client will only be able to call the pets method if it uses Django session authentication (which is cookie-based by default), otherwise an HTTP-401 error will be returned. If you only want to authorize superusers, you can use from ninja.security import django_auth_superuser instead.

Automatic OpenAPI schema

Here is an example where the client needs to pass a header in order to authenticate:

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 the http://localhost:8000/api/docs documentation.

Swagger UI Auth

Now, when you click the Authorize button, you will be prompted to enter your authentication token.

Swagger UI Auth

When you make a test call, every request will pass the authorization header.

Global authentication

If you need to protect all of your API's methods, you can pass the auth argument to the 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 override some of the methods, you can do it again at the operation level by passing the auth argument. In this example, authentication will be disabled for the /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 authentication options

Custom function

The "auth=" argument accepts any callable. NinjaAPI will only pass authentication when the callable returns a value that is truthy (converts to a boolean True). This return value will be assigned to the request.auth attribute.

def ip_whitelist(request):
    if request.META["REMOTE_ADDR"] == "8.8.8.8":
        return "8.8.8.8"


@api.get("/ipwhitelist", auth=ip_whitelist)
def ipwhitelist(request):
    return f"Authenticated client, IP = {request.auth}"

API keys

Some APIs use API keys for authorization. An API key is a token that a client provides when making an API call to identify itself. 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 built-in classes to help you handle these situations.

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 get a token from GET['api_key'] and find the corresponding Client in the database. The Client instance will be set as the request.auth attribute.

Note: param_name is the name of the GET parameter that will be checked. If not set, the default "key" is 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}"
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 authentication

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 also allows you 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 from GET, and if it is not set or invalid, it will check the header key. If both are invalid, it will raise an authentication error to the response.

Router authentication

Use the auth argument on a router to apply an authenticator to all operations declared within it:

api.add_router("/events/", events_router, auth=BasicAuth())

Or using the router constructor

router = Router(auth=BasicAuth())

Custom exceptions

Raising an exception that has an exception handler will return that handler's response in the same way as an operation:

from ninja import NinjaAPI
from ninja.security import HttpBearer

api = NinjaAPI()

class InvalidToken(Exception):
    pass

@api.exception_handler(InvalidToken)
def on_invalid_token(request, exc):
    return api.create_response(request, {"detail": "Invalid token supplied"}, status=401)

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        if token == "supersecret":
            return token
        raise InvalidToken


@api.get("/bearer", auth=AuthBearer())
def bearer(request):
    return {"token": request.auth}

Async authentication

Django Ninja has basic support for asynchronous authentication. While the default authentication classes are not async-compatible, you can still define your custom async authentication callable and pass it with auth.

async def async_auth(request):
    ...


@api.get("/pets", auth=async_auth)
def pets(request):
    ...

For more information, see Error handling .

Success

Continue to the next section Testing

Comments


本文总阅读量次