I've been trying to configure my Django REST Framework app to use django-auth-adfs for Microsoft Entra ID (former Azure AD B2C), but I have an error with signature verification.
Django==5.0.6
djangorestframework==3.15.1
django-auth-adfs==1.14.0
Prior to test oauth2/login page, I tried the example listed in this page
https://django-auth-adfs.readthedocs.io/en/latest/rest_framework.html
with a few additions to make it work with Microsoft Entra:
import os
from pprint import pprint
import requests
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# User credentials
user = os.getenv('USER_EMAIL')
password = os.getenv('USER_PASSWORD')
# OAuth 2.0 token endpoint
tenant_id = os.getenv('AAD_B2C_TENANT_ID')
token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/token"
# Client (application) ID and secret
client_id = os.getenv('AAD_B2C_CLIENT_ID')
client_secret = os.getenv('AAD_B2C_CLIENT_SECRET')
# API scope
api_scope = f"User.Read api://{client_id}/Backend.Read"
# Prepare the payload
payload = {
"grant_type": "password",
"response_type": "token",
"client_id": client_id,
"client_secret": client_secret,
"username": user,
"password": password,
"resource": client_id,
"scope": f"openid profile email {api_scope}",
}
# Request an access token
response = requests.post(
token_url,
data=payload,
verify=True # Ensure SSL certificates are verified
)
# Check for errors
try:
response.raise_for_status()
response_data = response.json()
access_token = response_data['access_token']
print('Access token retrieved successfully.')
except requests.exceptions.HTTPError as err:
print('Error retrieving access token:')
print(response.text)
raise SystemExit(err)
# Make a request to the API
headers = {
'Accept': 'application/json',
'Authorization': f'Bearer {access_token}',
}
api_response = requests.get(
'http://localhost:8000/api/contract',
headers=headers,
verify=True
)
# Check for errors
try:
api_response.raise_for_status()
# Print the API response
pprint(api_response.json())
except requests.exceptions.HTTPError as err:
print('API request failed:')
print(api_response.text)
raise SystemExit(err)
And the auth works in this case, I successfully getting the access_token
and using it to call my app endpoint http://localhost:8000/api/contract
.
However, when i'm trying to authorize within my api in the Chrome browser via oauth2/login
, I keep getting [django_auth_adfs:157] Error decoding signature: Signature verification failed
error.
Here is ADFS config in my DRF app settings.py
...
AUTHENTICATION_BACKENDS = (
'django_auth_adfs.backend.AdfsAccessTokenBackend',
'django_auth_adfs.backend.AdfsAuthCodeBackend',
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'django_auth_adfs.rest_framework.AdfsAccessTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 50, # Default limit if not specified in request
}
AUTH_ADFS = {
'AUDIENCE': AAD_B2C_CLIENT_ID,
'CLIENT_ID': AAD_B2C_CLIENT_ID,
'CLIENT_SECRET': AAD_B2C_CLIENT_SECRET,
# 'CLAIM_MAPPING': {'first_name': 'given_name',
# 'last_name': 'family_name',
# 'email': 'upn'},
'GROUPS_CLAIM': None,
'MIRROR_GROUPS': False,
'VERSION': 'v2.0',
'USERNAME_CLAIM': 'email',
'DISABLE_SSO': True,
'TENANT_ID': AAD_B2C_TENANT_ID,
'RELYING_PARTY_ID': AAD_B2C_CLIENT_ID,
'CREATE_NEW_USERS': True,
'LOGIN_EXEMPT_URLS': [
# '^$',
'^api',
# '^admin',
],
'SCOPES': ["openid", "profile", "email", "User.Read", "api://6aeb143f-59f8-4746-ba56-0c489aee6a1f/Backend.Read"],
}
LOGIN_URL = 'django_auth_adfs:login'
LOGOUT_URL = 'django_auth_adfs:logout'
LOGIN_REDIRECT_URL = '/admin/'
REDIRECT_URL = 'django_auth_adfs:callback'
urls.py
"""
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
path('oauth2/', include('django_auth_adfs.urls')),
]
I also tried to change urls to path('oauth2/', include('django_auth_adfs.drf_urls')),as suggested in the docs. But it cause a backend error
django.urls.exceptions.NoReverseMatch: 'django_auth_adfs' is not a registered namespace`.
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too