# Django Azure Active Directory Sign-In 🔑
[](https://docs.djangoproject.com/en/4.1/releases/4.1.7/)
[](https://www.python.org/downloads/release/python-3112/)

[](https://analytics.umami.is/share/M19mr5L7jVhHuFnb/jv-conseil.github.io "Umami - GDPR compliant alternative to Google Analytics")
[](LICENSE)
[](https://github.com/psf/black)
[](https://github.com/JV-conseil-Internet-Consulting/django-azure-active-directory-signin/actions/workflows/codeql-analysis.yml)
[](https://codecov.io/gh/JV-conseil-Internet-Consulting/django-azure-active-directory-signin)
[](https://pypi.org/project/django-azure-active-directory-signin/)
[](https://github.com/sponsors/JV-conseil "Become a sponsor to JV-conseil")
[](https://stackoverflow.com/users/2477854/jv-conseil "Follow JV conseil on StackOverflow")
[](https://twitter.com/JVconseil "Follow JVconseil on Twitter")
[](https://mastodon.social/@JVconseil "Follow JVconseil@mastodon.social on Mastodon")
[](https://github.com/JV-conseil "Follow JV-conseil on GitHub")
Sign-in users to your Django Web app with Azure Active Directory.
## Description
`django-azure-active-directory-signin` is a Django app which wraps [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-python)
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](https://pypi.org/project/django-sslserver/).
_This project is in no way affiliated with Microsoft Corporation._
## Installation
From PyPi:
```bash
pip install django-azure-active-directory-signin
```
## Configuration
### Azure App Registration
[Register an application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app). You must have permission to manage applications in Azure Active Directory (Azure AD) on your [Azure account](https://portal.azure.com).
[Add a client secret](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#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](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#add-a-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:
```py
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`:
```py
INSTALLED_APPS += [
"azure_signin",
]
```
### Authentication backend
Configure the authentication backend:
```py
AUTHENTICATION_BACKENDS += [
"azure_signin.backends.AzureSigninBackend",
]
```
### URLs
Include the app's URLs in your `urlpatterns`:
```py
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`.
```py
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.
```py
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:
```py
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`:
```python
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](https://python-poetry.org) 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](https://docs.microsoft.com/en-us/azure/active-directory/develop/web-app-quickstart?pivots=devlang-python) (docs.microsoft.com)
- [Microsoft Graph REST API v1.0](https://docs.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#permissions) (docs.microsoft.com)
- [Enable your Python Django web app to sign in users to your Azure Active Directory](https://github.com/Azure-Samples/ms-identity-python-django-tutorial/tree/main/1-Authentication/sign-in) tenant with the Microsoft identity platform (github.com)
## Sponsorship
If this project helps you, you can offer me a cup of coffee ☕️ :-)
[](https://github.com/sponsors/JV-conseil)
Raw data
{
"_id": null,
"home_page": "https://github.com/JV-conseil-Internet-Consulting/django-azure-active-directory-signin",
"name": "django-azure-active-directory-signin",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9.16",
"maintainer_email": "",
"keywords": "authentication,azure-active-directory,azure,django,microsoft-azure,microsoft,ms-identity,msal-python,msal,oauth2,openid-connect,openidconnect,signin",
"author": "JV conseil",
"author_email": "contact@jv-conseil.net",
"download_url": "https://files.pythonhosted.org/packages/bf/74/a99cdc8080656a079037c22010424352d52d14d035c4d8bf9e42f977d749/django_azure_active_directory_signin-0.2.5.tar.gz",
"platform": null,
"description": "# Django Azure Active Directory Sign-In \ud83d\udd11\n\n[](https://docs.djangoproject.com/en/4.1/releases/4.1.7/)\n[](https://www.python.org/downloads/release/python-3112/)\n\n[](https://analytics.umami.is/share/M19mr5L7jVhHuFnb/jv-conseil.github.io \"Umami - GDPR compliant alternative to Google Analytics\")\n[](LICENSE)\n[](https://github.com/psf/black)\n[](https://github.com/JV-conseil-Internet-Consulting/django-azure-active-directory-signin/actions/workflows/codeql-analysis.yml)\n[](https://codecov.io/gh/JV-conseil-Internet-Consulting/django-azure-active-directory-signin)\n[](https://pypi.org/project/django-azure-active-directory-signin/)\n[](https://github.com/sponsors/JV-conseil \"Become a sponsor to JV-conseil\")\n[](https://stackoverflow.com/users/2477854/jv-conseil \"Follow JV conseil on StackOverflow\")\n[](https://twitter.com/JVconseil \"Follow JVconseil on Twitter\")\n[](https://mastodon.social/@JVconseil \"Follow JVconseil@mastodon.social on Mastodon\")\n[](https://github.com/JV-conseil \"Follow JV-conseil on GitHub\")\n\nSign-in users to your Django Web app with Azure Active Directory.\n\n## Description\n\n`django-azure-active-directory-signin` is a Django app which wraps [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-python)\npackage to sign in users with Microsoft's Azure Active Directory (OAuth 2.0 and OpenID Connect) in Django projects.\n\n\n\nThe app includes `login`, `logout` and `callback` authentication views,\na customizable backend to validate, create user and extend user with extra attributes,\na decorator to protect individual views to protect individual views,\nand middleware which allows the entire site to require user authentication by default,\nwith the ability to exempt specified views.\n\nThe GitHub repository provides a `demo` Django app to run local tests on `https` protocol thanks to [django-sslserver](https://pypi.org/project/django-sslserver/).\n\n_This project is in no way affiliated with Microsoft Corporation._\n\n## Installation\n\nFrom PyPi:\n\n```bash\npip install django-azure-active-directory-signin\n```\n\n## Configuration\n\n### Azure App Registration\n\n[Register an application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app). You must have permission to manage applications in Azure Active Directory (Azure AD) on your [Azure account](https://portal.azure.com).\n\n[Add a client secret](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#add-a-client-secret) in **Certificates & secrets** > **Client secrets** > **New client secret** and note it down.\n\n\n\nCopy 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**.\n\n\n\n[Add redirect URI](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#add-a-redirect-uri) like so:\n\n- `https://<your-domain>/azure-signin/callback`\n- `https://127.0.0.1:8000/azure-signin/callback`\n- `https://localhost:8000/azure-signin/callback`\n\n### Settings\n\nAdd the following to your `settings.py`, replacing the variables in braces with the values\nfrom your Azure app:\n\n```py\nINSTALLED_APPS += [\n \"azure_signin\",\n]\n\nAZURE_SIGNIN = {\n \"CLIENT_ID\": os.environ.get(\"CLIENT_ID\"), # Mandatory\n \"CLIENT_SECRET\": os.environ.get(\"CLIENT_SECRET\"), # Mandatory\n \"TENANT_ID\": os.environ.get(\"TENANT_ID\"), # Mandatory\n \"SAVE_ID_TOKEN_CLAIMS\": True, # Optional, default is False.\n \"RENAME_ATTRIBUTES\": [\n (\"employeeNumber\", \"employee_id\"),\n (\"affiliationNumber\", \"omk2\"),\n ], # Optional\n \"REDIRECT_URI\": \"https://<domain>/azure-signin/callback\", # Optional\n \"SCOPES\": [\"User.Read.All\"], # Optional\n \"AUTHORITY\": \"https://login.microsoftonline.com/\" + os.environ.get(\"TENANT_ID\"), # Optional Or https://login.microsoftonline.com/common if multi-tenant\n \"LOGOUT_REDIRECT_URI\": \"https://<domain>/logout\", # Optional\n \"PUBLIC_URLS\": [\"<public:view_name>\",] # Optional, public views accessible by non-authenticated users\n}\n\nAUTHENTICATION_BACKENDS += [\n \"azure_signin.backends.AzureSigninBackend\",\n]\n\nLOGIN_URL = \"azure_signin:login\"\nLOGIN_REDIRECT_URL = \"/\" # Or any other endpoint\nLOGOUT_REDIRECT_URL = LOGIN_REDIRECT_URL\n```\n\n### Installed apps\n\nAdd the following to your `INSTALLED_APPS`:\n\n```py\nINSTALLED_APPS += [\n \"azure_signin\",\n]\n```\n\n### Authentication backend\n\nConfigure the authentication backend:\n\n```py\nAUTHENTICATION_BACKENDS += [\n \"azure_signin.backends.AzureSigninBackend\",\n]\n```\n\n### URLs\n\nInclude the app's URLs in your `urlpatterns`:\n\n```py\nfrom django.urls import path, include\n\nurlpatterns += [\n path(\"azure-signin/\", include(\"azure_signin.urls\", namespace=\"azure_signin\")),\n]\n```\n\n## Usage\n\n### AbstractUser\n\nAdd extra attributes to users with `AZURE_SIGNIN[\"RENAME_ATTRIBUTES\"]`\nand Django `django.contrib.auth.models.AbstractUser`.\n\n```py\nfrom django.contrib.auth.models import AbstractUser\nfrom django.db import models\n\n\nclass ExtendedUser(AbstractUser):\n \"\"\"\n Extend user with extra attributes set in `AZURE_SIGNIN[\"RENAME_ATTRIBUTES\"]`\n \"\"\"\n\n email = models.EmailField(unique=True, db_index=True)\n employee_id = models.IntegerField(\n null=True, default=None, unique=True, blank=True, db_index=True\n )\n omk2 = models.CharField(max_length=5, null=True, default=None, db_index=True)\n hcm = models.CharField(max_length=7, null=True, default=None, db_index=True)\n```\n\n### Backend\n\nBackend can be subclassed to customize validation rules for user.\n\n```py\nimport logging\n\nfrom azure_signin.backends import AzureSigninBackend\n\nlogger = logging.getLogger(__name__)\n\nclass CustomAzureSigninBackend(AzureSigninBackend):\n \"Subclass AzureSigninBackend to customize validation rules for user.\"\n\n def is_valid_user(self, user: dict, *args, **kwargs) -> bool:\n \"is_valid_user\"\n output = super().is_valid_user(user, *args, **kwargs)\n try:\n \"run extra checks here...\"\n pass\n except Exception as e:\n logger.exception(e)\n logger.debug(\"is_valid_user: %s\", output)\n return output\n```\n\n### Decorator\n\nTo make user authentication a requirement for accessing an individual view, decorate the\nview like so:\n\n```py\nfrom azure_signin.decorators import azure_signin_required\nfrom django.shortcuts import HttpResponse\n\n@azure_signin_required\ndef protected_view(request):\n return HttpResponse(\"A view protected by the decorator\")\n```\n\n### Middleware\n\nIf you want to protect your entire site by default, you can use the middleware by adding the\nfollowing to your `settings.py`:\n\n```python\nMIDDLEWARE += [\n \"azure_signin.middleware.AzureSigninMiddleware\",\n]\n```\n\nMake sure you add the middleware after Django's `session` and `authentication` middlewares so\nthat the request includes the session and user objects. Public URLs which need to be accessed by\nnon-authenticated users should be specified in the `settings.AZURE_SIGNIN[\"PUBLIC_URLS\"]`, as\nshown above.\n\n### VS Code Tasks\n\nThe GitHub repository provides commands `Install`, `Launch` and `Tests` accessible through\n`Command Palette` (press `Cmd+Shift+P`) then `>Tasks: Run Tasks`.\n\n\n\n\nAll bash scripts are stored in `.bash` folder.\n\nThe virtual environment is propelled by [poetry](https://python-poetry.org) which can be installed with Homebrew `brew install poetry`.\n\n## Credits\n\nThis app is inspired by and builds on functionality in\n<https://github.com/AgileTek/django-azure-auth>, with both feature\nimprovements, code coverage and extended documentation.\n\n## Readings \ud83d\udcda\n\n- [Quickstart: Add sign-in with Microsoft to a web app](https://docs.microsoft.com/en-us/azure/active-directory/develop/web-app-quickstart?pivots=devlang-python) (docs.microsoft.com)\n- [Microsoft Graph REST API v1.0](https://docs.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#permissions) (docs.microsoft.com)\n- [Enable your Python Django web app to sign in users to your Azure Active Directory](https://github.com/Azure-Samples/ms-identity-python-django-tutorial/tree/main/1-Authentication/sign-in) tenant with the Microsoft identity platform (github.com)\n\n## Sponsorship\n\nIf this project helps you, you can offer me a cup of coffee \u2615\ufe0f :-)\n\n[](https://github.com/sponsors/JV-conseil)\n",
"bugtrack_url": null,
"license": "EUPL-1.2",
"summary": "Sign-in users to your Django Web app with Azure Active Directory.",
"version": "0.2.5",
"project_urls": {
"Bug Tracker": "https://github.com/JV-conseil-Internet-Consulting/django-azure-active-directory-signin/issues",
"Documentation": "https://jv-conseil-internet-consulting.github.io/django-azure-active-directory-signin/",
"Donate": "https://www.paypal.com/donate/?hosted_button_id=P3DGL6EANDY96",
"Homepage": "https://github.com/JV-conseil-Internet-Consulting/django-azure-active-directory-signin",
"Mastodon": "https://mastodon.social/@JVconseil",
"Repository": "https://github.com/JV-conseil-Internet-Consulting/django-azure-active-directory-signin",
"Sponsor": "https://github.com/sponsors/JV-conseil",
"Stack Overflow": "https://stackoverflow.com/users/2477854/jv-conseil",
"Twitter": "https://twitter.com/JVconseil"
},
"split_keywords": [
"authentication",
"azure-active-directory",
"azure",
"django",
"microsoft-azure",
"microsoft",
"ms-identity",
"msal-python",
"msal",
"oauth2",
"openid-connect",
"openidconnect",
"signin"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "550cc36fbc4e8d8874bc3fd1a9c6e39da0ff5e3295094c919c43822b6739872e",
"md5": "9306dd3e2368cb4015f0ec7837f6776d",
"sha256": "c74f4ebef10341377097fdf101d3d691ab961ee181ff3bf9b6cc6b70b82d0c38"
},
"downloads": -1,
"filename": "django_azure_active_directory_signin-0.2.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "9306dd3e2368cb4015f0ec7837f6776d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9.16",
"size": 19781,
"upload_time": "2023-09-18T14:12:06",
"upload_time_iso_8601": "2023-09-18T14:12:06.367021Z",
"url": "https://files.pythonhosted.org/packages/55/0c/c36fbc4e8d8874bc3fd1a9c6e39da0ff5e3295094c919c43822b6739872e/django_azure_active_directory_signin-0.2.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bf74a99cdc8080656a079037c22010424352d52d14d035c4d8bf9e42f977d749",
"md5": "11d581c5f083de08677c8688e6ee6cbf",
"sha256": "981983729e7042b6f9de110a769373b36acc953da497eed19352db9730d6feb0"
},
"downloads": -1,
"filename": "django_azure_active_directory_signin-0.2.5.tar.gz",
"has_sig": false,
"md5_digest": "11d581c5f083de08677c8688e6ee6cbf",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9.16",
"size": 19718,
"upload_time": "2023-09-18T14:12:08",
"upload_time_iso_8601": "2023-09-18T14:12:08.811596Z",
"url": "https://files.pythonhosted.org/packages/bf/74/a99cdc8080656a079037c22010424352d52d14d035c4d8bf9e42f977d749/django_azure_active_directory_signin-0.2.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-09-18 14:12:08",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "JV-conseil-Internet-Consulting",
"github_project": "django-azure-active-directory-signin",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "asgiref",
"specs": [
[
"==",
"3.7.2"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2023.7.22"
]
]
},
{
"name": "cffi",
"specs": [
[
"==",
"1.15.1"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"3.2.0"
]
]
},
{
"name": "cryptography",
"specs": [
[
"==",
"41.0.3"
]
]
},
{
"name": "django",
"specs": [
[
"==",
"4.2.5"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.4"
]
]
},
{
"name": "msal",
"specs": [
[
"==",
"1.24.0"
]
]
},
{
"name": "pycparser",
"specs": [
[
"==",
"2.21"
]
]
},
{
"name": "pyjwt",
"specs": [
[
"==",
"2.8.0"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.31.0"
]
]
},
{
"name": "sqlparse",
"specs": [
[
"==",
"0.4.4"
]
]
},
{
"name": "typing-extensions",
"specs": [
[
"==",
"4.8.0"
]
]
},
{
"name": "tzdata",
"specs": [
[
"==",
"2023.3"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.0.4"
]
]
}
],
"tox": true,
"lcname": "django-azure-active-directory-signin"
}