Как авторизоваться в Microsoft AD с помощью Curl?

У меня есть проект 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, который находится под именем моего приложения.

Можете ли вы также добавить скриншот колонки разрешений API?

Rukmini 18.07.2024 07:11

Привет @Rukmini, я добавила.

Arad Soutehkeshan 18.07.2024 09:48

Конечно проверю и опубликую ответ

Rukmini 18.07.2024 09:50

Кажется, это не связано с django или drf.

Andrew 18.07.2024 11:51

Я отредактировал теги @Andrew

Arad Soutehkeshan 18.07.2024 16:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
111
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обратите внимание: поток учетных данных клиента не поддерживает тип делегированных разрешений. Если вы используете поток учетных данных клиента для создания токена, вам необходимо предоставить разрешения API типа приложения приложению Microsoft Entra ID.

  • Поскольку вы предоставили делегированные разрешения 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 может быть причиной?

Arad Soutehkeshan 18.07.2024 16:52

Да, ваша конфигурация отдыха Django может быть проблемой

Rukmini 18.07.2024 19:40

@AradSoutehkeshan Любые обновления по проблеме, поскольку они не связаны с конфигурациями почтальона и Azure.

Rukmini 19.07.2024 05:20

Дело в том, что я прекрасно могу работать с API с помощью rest_framework просматриваемого API. Но когда я хочу сделать это с помощью почтальона или завитка, я получаю страницу входа в Microsoft, хотя, как вы сказали, я могу успешно сгенерировать токен доступа. Я попытался отключить аутентификацию Azure в своем представлении API, и мне удалось отправлять запросы и получать ответы. Но тогда в почтовых запросах у меня не будет экземпляра пользователя, который мне действительно нужен. Я использую библиотеки django_auth_adfs и rest_framework.

Arad Soutehkeshan 19.07.2024 12:44

Я отредактировал свое сообщение и включил файл settings.py для ссылки на мою конфигурацию отдыха.

Arad Soutehkeshan 19.07.2024 12:48

Я не вижу никаких изменений. Хотите ли вы отключить страницу входа в Microsoft?

Rukmini 19.07.2024 12:51

только что добавил. Нет, я не хочу его удалять. Я просто удалил его, чтобы посмотреть, получу ли я результат, и я это сделал. Итак, Django должен работать нормально. Это всего лишь этап авторизации.

Arad Soutehkeshan 19.07.2024 12:57

Можете ли вы расшифровать токен и вставить скриншот?

Rukmini 19.07.2024 13:08

Я принял ваш ответ, потому что понял, что он больше не связан с лазурью или почтальоном. Это связано с тем, как я обрабатываю токен доступа. Проверьте здесь, если вам было любопытно. Спасибо за вашу помощь!

Arad Soutehkeshan 19.07.2024 16:05

Конечно, я проверю

Rukmini 19.07.2024 16:07

Другие вопросы по теме