Wocha Docs

Django quickstart

Add Wocha authentication to a Django project with OIDC authentication backend for browser sessions and JWT validation for Django REST Framework API endpoints.

Add Wocha authentication to a Django project: OIDC authentication backend for browser sessions and JWT validation for Django REST Framework API endpoints.

Prerequisites

  • Python 3.10+
  • Django 4.2+
  • Django REST Framework (for API mode)
  • A Wocha project with OAuth applications (Wocha Console)

What you'll build

A Django app with browser OIDC login and optional DRF JWT validation for API endpoints.

your-app.local

Welcome back

After sign-in

✓ Session stored securely

✓ User profile available in your app

✓ Organisation context ready for multi-tenant features

Installation

pip install django djangorestframework mozilla-django-oidc PyJWT[crypto] httpx wocha

Environment variables

WOCHA_ISSUER=https://your-tenant.auth.wocha.ai
WOCHA_CLIENT_ID=your-client-id
WOCHA_CLIENT_SECRET=your-client-secret
WOCHA_AUDIENCE=your-api-audience          # optional — API JWT validation
WOCHA_API_KEY=wocha_mgmt_...
WOCHA_TENANT=your-tenant-slug

Register redirect URI: http://localhost:8000/oidc/callback/

OIDC authentication backend

Add to settings.py:

import os
 
AUTHENTICATION_BACKENDS = [
    "mozilla_django_oidc.auth.OIDCAuthenticationBackend",
    "django.contrib.auth.backends.ModelBackend",
]
 
OIDC_RP_CLIENT_ID = os.environ["WOCHA_CLIENT_ID"]
OIDC_RP_CLIENT_SECRET = os.environ["WOCHA_CLIENT_SECRET"]
OIDC_OP_ISSUER = os.environ["WOCHA_ISSUER"]
OIDC_OP_AUTHORIZATION_ENDPOINT = f"{OIDC_OP_ISSUER}/oauth2/auth"
OIDC_OP_TOKEN_ENDPOINT = f"{OIDC_OP_ISSUER}/oauth2/token"
OIDC_OP_USER_ENDPOINT = f"{OIDC_OP_ISSUER}/userinfo"
OIDC_OP_JWKS_ENDPOINT = f"{OIDC_OP_ISSUER}/.well-known/jwks.json"
OIDC_RP_SCOPES = "openid profile email offline_access"
OIDC_RP_SIGN_ALGO = "RS256"
LOGIN_REDIRECT_URL = "/dashboard/"
LOGOUT_REDIRECT_URL = "/"
 
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "mozilla_django_oidc.middleware.SessionRefresh",
    "django.contrib.messages.middleware.MessageMiddleware",
]
 
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "mozilla_django_oidc",
    "rest_framework",
    "myapp",
]

Add URLs in urls.py:

from django.contrib import admin
from django.urls import include, path
 
urlpatterns = [
    path("admin/", admin.site.urls),
    path("oidc/", include("mozilla_django_oidc.urls")),
    path("", include("myapp.urls")),
]

Create a simple protected view in myapp/views.py:

from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.shortcuts import render
 
 
def home(request):
    return render(request, "home.html")
 
 
@login_required
def dashboard(request):
    return JsonResponse({"email": request.user.email, "username": request.user.username})

Add routes in myapp/urls.py:

from django.urls import path
from . import views
 
urlpatterns = [
    path("", views.home, name="home"),
    path("dashboard/", views.dashboard, name="dashboard"),
]

Sign-in link in a template:

<a href="{% url 'oidc_authentication_init' %}">Sign in with Wocha</a>

JWT validation for DRF

Create myapp/authentication.py:

import os
from functools import lru_cache
 
import httpx
import jwt
from jwt import PyJWKClient
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
 
 
@lru_cache
def get_jwk_client() -> PyJWKClient:
    issuer = os.environ["WOCHA_ISSUER"].rstrip("/")
    return PyJWKClient(f"{issuer}/.well-known/jwks.json")
 
 
class GreetJWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        auth_header = request.headers.get("Authorization", "")
        if not auth_header.startswith("Bearer "):
            return None
 
        token = auth_header.removeprefix("Bearer ").strip()
        issuer = os.environ["WOCHA_ISSUER"].rstrip("/")
        audience = os.environ.get("WOCHA_AUDIENCE")
 
        try:
            signing_key = get_jwk_client().get_signing_key_from_jwt(token)
            decode_kwargs = {
                "algorithms": ["RS256", "ES256"],
                "issuer": issuer,
            }
            if audience:
                decode_kwargs["audience"] = audience
 
            claims = jwt.decode(token, signing_key.key, **decode_kwargs)
        except jwt.PyJWTError as exc:
            raise AuthenticationFailed("Invalid or expired token") from exc
 
        user = GreetAPIUser(claims)
        return (user, claims)
 
 
class GreetAPIUser:
    """Lightweight user object backed by JWT claims."""
 
    def __init__(self, claims: dict):
        self.claims = claims
        self.id = claims.get("sub")
        self.email = claims.get("email", "")
        self.is_authenticated = True
        self.is_anonymous = False

Configure DRF in settings.py:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "myapp.authentication.GreetJWTAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
}

Create an API view in myapp/api.py:

from rest_framework.decorators import api_view
from rest_framework.response import Response
 
 
@api_view(["GET"])
def me(request):
    user = request.user
    return Response({
        "user_id": user.id,
        "email": user.email,
        "org_id": user.claims.get("org_id"),
    })

Register in myapp/urls.py:

from . import api
 
urlpatterns = [
    # ... existing routes ...
    path("api/me/", api.me, name="api-me"),
]

Management API

Use wocha for server-side admin tasks:

import os
from wocha import WochaClient
 
wocha = WochaClient(
    tenant=os.environ["WOCHA_TENANT"],
    api_key=os.environ["WOCHA_API_KEY"],
)
 
# In a management command or admin view:
users = wocha.users.list(page_size=25)

Testing the integration

Browser login

  1. Run migrations and start the server:

    python manage.py migrate
    python manage.py runserver
  2. Visit http://localhost:8000 and click Sign in with Wocha.

  3. Complete login — you should reach /dashboard/ with an authenticated session.

API mode

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" http://localhost:8000/api/me/

Next steps

On this page