У меня есть проект django, который использует каталог Azure Active для аутентификации, и я настроил его API с помощью rest_framework. На данный момент я пытаюсь получить доступ к этому API с помощью Curl. Я могу сгенерировать токен доступа, но когда я отправляю запрос с его использованием, я получаю страницу входа в Microsoft, как будто у меня нет токена доступа.
Я сгенерировал токен доступа, используя следующий код:
curl -X POST https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token -H "Content-Type: application/x-www-form-urlencoded" -d "client_id = {client_id}" -d "client_secret = {client_secret}" -d "grant_type=client_credentials" -d "redirect_uri=https://{my_domain}/oauth2/callback" -d "scope=api://{client_id}/.default openid profile email"
Затем, используя полученный токен jwt, я сделал:
curl -X GET -L https://<my_domain>/api/benches/1/status/ -H "Authorization: Bearer <token>"
И я получаю страницу входа в Microsoft, первые несколько строк которой выглядят так:
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html dir = "ltr" class = "" lang = "en">
<head>
<title>Sign in to your account</title>
.
.
.
Кроме того, результат -v
(подробный) выглядит следующим образом:
Note: Unnecessary use of -X or --request, GET is already inferred.
* Host <my_domain>:443 was resolved.
* IPv6: (none)
* IPv4: ip
* Trying ip:443...
* Connected to <my_domain> (ip) port 443
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server accepted http/1.1
* using HTTP/1.x
> GET /api/benches/1/status/ HTTP/1.1
> Host: <my_domain>
> User-Agent: curl/8.7.1
> Accept: */*
> Authorization: Bearer <token>
>
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
< HTTP/1.1 302 Found
< Server: nginx/1.14.1
< Date: Wed, 17 Jul 2024 16:08:34 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 0
< Connection: keep-alive
< Location: /oauth2/login?next=/api/benches/1/status/
< X-Frame-Options: DENY
< Vary: Cookie
< X-Content-Type-Options: nosniff
< Referrer-Policy: same-origin
<
* Request completely sent off
* Connection #0 to host <my_domain> left intact
Кроме того, я делаю это для своей организации. Возможно ли, что в моем приложении Azure отсутствует разрешение, которое мне может понадобиться?
Вот мой файл settings.py Django для моей конфигурации REST:
"""
Django settings for core project.
Generated by 'django-admin startproject' using Django 5.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from pathlib import Path
import os
from dotenv import load_dotenv
load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['my_domain']
AUTHENTICATION_BACKENDS = (
'django_auth_adfs.backend.AdfsAuthCodeBackend',
'django_auth_adfs.backend.AdfsAccessTokenBackend',
)
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Installed apps
'django_auth_adfs',
'bench',
'api',
'rest_framework',
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
# Other Middlewares
'django_auth_adfs.middleware.LoginRequiredMiddleware',
]
ROOT_URLCONF = "core.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [
BASE_DIR / 'static/templates',
],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "core.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "GB"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
STATIC_ROOT = '/root/to/static'
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
LOGIN_URL = 'django_auth_adfs:login'
LOGOUT_URL = 'django_auth_adfs:logout'
LOGIN_REDIRECT_URL = 'https://my_domain/oauth2/callback'
# Client secret is not public information. Should store it as an environment variable.
client_id = os.getenv('client_id')
client_secret = os.getenv('client_secret')
tenant_id = os.getenv('tenant_id')
AUTH_ADFS = {
'AUDIENCE': client_id,
'CLIENT_ID': client_id,
'CLIENT_SECRET': client_secret,
'CLAIM_MAPPING': {'first_name': 'given_name',
'last_name': 'family_name',
'email': 'upn'
},
'GROUPS_CLAIM': 'roles',
'MIRROR_GROUPS': True,
'USERNAME_CLAIM': 'email',
'TENANT_ID': tenant_id,
'RELYING_PARTY_ID': client_id,
'LOGIN_EXEMPT_URLS': [
'^api', # Assuming you API is available at /api
],
}
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'django_auth_adfs.rest_framework.AdfsAccessTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
Вот скриншот моего лезвия разрешений. Я использую Benches.Change.All, который находится под именем моего приложения.
Привет @Rukmini, я добавила.
Конечно проверю и опубликую ответ
Кажется, это не связано с django или drf.
Я отредактировал теги @Andrew
Обратите внимание: поток учетных данных клиента не поддерживает тип делегированных разрешений. Если вы используете поток учетных данных клиента для создания токена, вам необходимо предоставить разрешения API типа приложения приложению Microsoft Entra ID.
Вы получаете сообщение об ошибке, поскольку предоставили делегированные разрешения API и используете поток учетных данных клиента для создания токена.
Чтобы устранить ошибку. сгенерируйте токен, используя поток кода авторизации, а затем вызовите API.
Я предоставил приложению разрешения API:
Настройте URL-адрес перенаправления как веб-страницу с помощью https://oauth.pstmn.io/v1/callback'
в приложении. Вы также можете передать свой собственный URL-адрес перенаправления.
Теперь сгенерируйте токен доступа, выбрав поток кода авторизации:
Grant type: Authorization code
Callback URL: https://oauth.pstmn.io/v1/callback
Auth URL: https://login.microsoftonline.com/TenantId/oauth2/v2.0/authorize
Token URL : https://login.microsoftonline.com/TenantId/oauth2/v2.0/token
Client ID : ClientId
Client Secret : ClientSecret
Scope: api://ClientID/Benches.Change.All
Нажмите «Создать токен доступа», и вы будете перенаправлены в браузер для входа:
Токен доступа будет сгенерирован:
Расшифруйте токен доступа в jwt.ms: Добро пожаловать! и убедитесь, что утверждение scp имеет ценность Benches.Change.All
Теперь используйте этот токен доступа для вызова API, и он пройдет успешно и без ошибок.
Спасибо за ваш ответ, но, как я уже сказал в здесь, это еще один мой пост по той же проблеме, я все еще получаю html-код входа в Microsoft после отправки запроса GET с моим токеном JWT, хотя у меня есть область действия и aud упоминался выше, когда я его декодировал. Считаете ли вы, что моя конфигурация покоя Django может быть причиной?
Да, ваша конфигурация отдыха Django может быть проблемой
@AradSoutehkeshan Любые обновления по проблеме, поскольку они не связаны с конфигурациями почтальона и Azure.
Дело в том, что я прекрасно могу работать с API с помощью rest_framework просматриваемого API. Но когда я хочу сделать это с помощью почтальона или завитка, я получаю страницу входа в Microsoft, хотя, как вы сказали, я могу успешно сгенерировать токен доступа. Я попытался отключить аутентификацию Azure в своем представлении API, и мне удалось отправлять запросы и получать ответы. Но тогда в почтовых запросах у меня не будет экземпляра пользователя, который мне действительно нужен. Я использую библиотеки django_auth_adfs и rest_framework.
Я отредактировал свое сообщение и включил файл settings.py для ссылки на мою конфигурацию отдыха.
Я не вижу никаких изменений. Хотите ли вы отключить страницу входа в Microsoft?
только что добавил. Нет, я не хочу его удалять. Я просто удалил его, чтобы посмотреть, получу ли я результат, и я это сделал. Итак, Django должен работать нормально. Это всего лишь этап авторизации.
Можете ли вы расшифровать токен и вставить скриншот?
Я принял ваш ответ, потому что понял, что он больше не связан с лазурью или почтальоном. Это связано с тем, как я обрабатываю токен доступа. Проверьте здесь, если вам было любопытно. Спасибо за вашу помощь!
Конечно, я проверю
Можете ли вы также добавить скриншот колонки разрешений API?