Django Azure Active Directory Sign-In 🔑
Description
django-azure-active-directory-signin
is a Django app which wraps MSAL package to sign in users with Microsoft’s Azure Active Directory (OAuth 2.0 and OpenID Connect) in Django projects.
The app includes login
, logout
and callback
authentication views, a customizable backend to validate, create user and extend user with extra attributes, a decorator to protect individual views to protect individual views, and middleware which allows the entire site to require user authentication by default, with the ability to exempt specified views.
The GitHub repository provides a demo
Django app to run local tests on https
protocol thanks to django-sslserver.
This project is in no way affiliated with Microsoft Corporation.
Installation
From PyPi:
pip install django-azure-active-directory-signin
Configuration
Azure App Registration
Register an application. You must have permission to manage applications in Azure Active Directory (Azure AD) on your Azure account.
Add a client secret in Certificates & secrets > Client secrets > New client secret and note it down.
Copy your client_id, tenant_id and client_secret and store them in environment variables (see .env
folder for sample) or better still in an Azure Key Vault.
Add redirect URI like so:
https://<your-domain>/azure-signin/callback
https://127.0.0.1:8000/azure-signin/callback
https://localhost:8000/azure-signin/callback
Settings
Add the following to your settings.py
, replacing the variables in braces with the values from your Azure app:
INSTALLED_APPS += [
"azure_signin",
]
AZURE_SIGNIN = {
"CLIENT_ID": os.environ.get("CLIENT_ID"), # Mandatory
"CLIENT_SECRET": os.environ.get("CLIENT_SECRET"), # Mandatory
"TENANT_ID": os.environ.get("TENANT_ID"), # Mandatory
"SAVE_ID_TOKEN_CLAIMS": True, # Optional, default is False.
"RENAME_ATTRIBUTES": [
("employeeNumber", "employee_id"),
("affiliationNumber", "omk2"),
], # Optional
"REDIRECT_URI": "https://<domain>/azure-signin/callback", # Optional
"SCOPES": ["User.Read.All"], # Optional
"AUTHORITY": "https://login.microsoftonline.com/" + os.environ.get("TENANT_ID"), # Optional Or https://login.microsoftonline.com/common if multi-tenant
"LOGOUT_REDIRECT_URI": "https://<domain>/logout", # Optional
"PUBLIC_URLS": ["<public:view_name>",] # Optional, public views accessible by non-authenticated users
}
AUTHENTICATION_BACKENDS += [
"azure_signin.backends.AzureSigninBackend",
]
LOGIN_URL = "azure_signin:login"
LOGIN_REDIRECT_URL = "/" # Or any other endpoint
LOGOUT_REDIRECT_URL = LOGIN_REDIRECT_URL
Installed apps
Add the following to your INSTALLED_APPS
:
INSTALLED_APPS += [
"azure_signin",
]
Authentication backend
Configure the authentication backend:
AUTHENTICATION_BACKENDS += [
"azure_signin.backends.AzureSigninBackend",
]
URLs
Include the app’s URLs in your urlpatterns
:
from django.urls import path, include
urlpatterns += [
path("azure-signin/", include("azure_signin.urls", namespace="azure_signin")),
]
Usage
AbstractUser
Add extra attributes to users with AZURE_SIGNIN["RENAME_ATTRIBUTES"]
and Django django.contrib.auth.models.AbstractUser
.
from django.contrib.auth.models import AbstractUser
from django.db import models
class ExtendedUser(AbstractUser):
"""
Extend user with extra attributes set in `AZURE_SIGNIN["RENAME_ATTRIBUTES"]`
"""
email = models.EmailField(unique=True, db_index=True)
employee_id = models.IntegerField(
null=True, default=None, unique=True, blank=True, db_index=True
)
omk2 = models.CharField(max_length=5, null=True, default=None, db_index=True)
hcm = models.CharField(max_length=7, null=True, default=None, db_index=True)
Backend
Backend can be subclassed to customize validation rules for user.
import logging
from azure_signin.backends import AzureSigninBackend
logger = logging.getLogger(__name__)
class CustomAzureSigninBackend(AzureSigninBackend):
"Subclass AzureSigninBackend to customize validation rules for user."
def is_valid_user(self, user: dict, *args, **kwargs) -> bool:
"is_valid_user"
output = super().is_valid_user(user, *args, **kwargs)
try:
"run extra checks here..."
pass
except Exception as e:
logger.exception(e)
logger.debug("is_valid_user: %s", output)
return output
Decorator
To make user authentication a requirement for accessing an individual view, decorate the view like so:
from azure_signin.decorators import azure_signin_required
from django.shortcuts import HttpResponse
@azure_signin_required
def protected_view(request):
return HttpResponse("A view protected by the decorator")
Middleware
If you want to protect your entire site by default, you can use the middleware by adding the following to your settings.py
:
MIDDLEWARE += [
"azure_signin.middleware.AzureSigninMiddleware",
]
Make sure you add the middleware after Django’s session
and authentication
middlewares so that the request includes the session and user objects. Public URLs which need to be accessed by non-authenticated users should be specified in the settings.AZURE_SIGNIN["PUBLIC_URLS"]
, as shown above.
VS Code Tasks
The GitHub repository provides commands Install
, Launch
and Tests
accessible through Command Palette
(press Cmd+Shift+P
) then >Tasks: Run Tasks
.
All bash scripts are stored in .bash
folder.
The virtual environment is propelled by poetry which can be installed with Homebrew brew install poetry
.
Credits
This app is inspired by and builds on functionality in https://github.com/AgileTek/django-azure-auth, with both feature improvements, code coverage and extended documentation.
Readings 📚
- Quickstart: Add sign-in with Microsoft to a web app (docs.microsoft.com)
- Microsoft Graph REST API v1.0 (docs.microsoft.com)
- Enable your Python Django web app to sign in users to your Azure Active Directory tenant with the Microsoft identity platform (github.com)
Sponsorship
If this project helps you, you can offer me a cup of coffee ☕️ :-)