Authentication
Changed in version 2.0: Authentication system has been completely rewritten. Please carefully read this page to understand how the new system works
Overview
Django‑modern‑rpc lets you protect procedures using small authentication predicates. A predicate is a callable that receives the Django HttpRequest and returns a truthy value when the caller is authorized, or a falsy value otherwise.
Key points:
You can configure authentication at three levels: server, namespace, and procedure.
Predicates are evaluated in their definition order with OR semantics: the first predicate that returns a truthy value authorizes the call; if all return falsy, the call fails with AuthenticationError.
The truthy value returned by the successful predicate is stored into RpcRequestContext.auth_result so it’s available to your procedure (see Accessing the authentication result below).
Changed in version 2.0: In the previous versions, all predicates had to validate incoming request to allows a procedure to be called
Configuration levels
1) Server level (default for all procedures)
Pass auth=<predicate or sequence of predicates> to RpcServer to define a default for all procedures registered on this server, unless overridden by a namespace or a procedure.
Example:
from django.http.request import HttpRequest
from modernrpc import RpcServer
def is_staff(request: HttpRequest):
# Example using Django auth
return request.user if (request.user.is_authenticated and request.user.is_staff) else None
server = RpcServer(auth=is_staff)
2) Namespace level (default within a namespace)
RpcNamespace accepts the same auth parameter. Procedures registered on the namespace inherit that default unless they override it.
from django.http.request import HttpRequest
from modernrpc import RpcNamespace
def has_api_key(request: HttpRequest):
return request.headers.get("X-API-Key") == "secret" or None
api = RpcNamespace(auth=has_api_key)
@api.register_procedure
def ping():
return "pong"
server.register_namespace(api, "api")
3) Procedure level (highest precedence)
You can override auth at registration time for a given function. This takes precedence over namespace and server.
from django.http.request import HttpRequest
def is_superuser(request: HttpRequest):
return request.user if (request.user.is_authenticated and request.user.is_superuser) else None
@server.register_procedure(name="admin.reset", auth=is_superuser)
def reset_counters():
...
Multiple predicates and evaluation order
Auth can be a single callable or any iterable (list/tuple) of callables. They are called in the order you provide.
The first truthy result short‑circuits evaluation and authorizes the request; if none return a truthy value, an
AuthenticationError is raised.
from django.http.request import HttpRequest
def via_token(request: HttpRequest):
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
return {"token": token} if token == "valid" else None
def via_session(request: HttpRequest):
return request.user if request.user.is_authenticated else None
server = RpcServer(auth=[via_session, via_token])
In the example above, via_session is tried first, then via_token if needed.
Accessing the authentication result in procedures
When a predicate returns a truthy value, that value is stored in RpcRequestContext.auth_result. To access it from
within a procedure, ask the server to inject the context into a named parameter using context_target at
registration, then read context.auth_result.
from django.http.request import HttpRequest
from modernrpc import RpcServer, RpcRequestContext
server = RpcServer()
def has_api_key(request: HttpRequest):
return request.headers.get("X-API-Key") or None
@server.register_procedure(name="echo.secure", context_target="ctx", auth=has_api_key)
def echo_secure(message: str, ctx: RpcRequestContext):
# ctx is a modernrpc.core.RpcRequestContext
api_key = ctx.auth_result # value returned by has_api_key
return {"message": message, "clear-text-api-key": api_key}
Notes and best practices
Predicates should be side‑effect free and fast; they are called on every request of protected procedures.
Return a meaningful truthy object (e.g., the authenticated user, a claims dict, or a token string) to make it usable in your procedures via
ctx.auth_result.Precedence: procedure auth > namespace auth > server auth.
Utilities
Since authentication system has been rewritten from scratch in v2, the decorators previously available to retrieve Basic Auth information from request and control the permissions of the corresponding user have been removed.
A new module modernrpc.auth contains some utility functions to help you reading authentication from request (Basic Auth, Bearer token, etc.).
-
modernrpc.auth.extract_header(request, header_name)[source]
Extract a header from a request object or raise a ValueError when it is not found
- Parameters:
request (HttpRequest)
header_name (str)
- Return type:
str
-
modernrpc.auth.extract_generic_token(request, header_name, auth_type)[source]
Extract a generic token from a request object and raise a ValueError when it is not found
- Parameters:
request (HttpRequest) – HTTP request containing the headers
header_name (str) – Header to lookup for authentication data (ex: Authorization)
auth_type (str) – Type of authentication information to extract (ex: Bearer, Basic, etc.)
- Returns:
Authentication data
- Return type:
str
-
modernrpc.auth.extract_http_basic_auth(request)[source]
Extract HTTP Basic Auth credentials from a request object. Return a tuple with username and password
- Parameters:
request (HttpRequest)
- Return type:
tuple[str, str]