Microsoft Graph API: получение ErrorAccessDenied для мультитенантного приложения с разрешением приложения - PullRequest
1 голос
/ 13 апреля 2020

Я пишу приложение-демон для своих клиентов (несколько арендаторов), которые используют outlook.

Я использую два разрешения приложения, для которых требуется согласие администратора - Mail.ReadBasic.All и User.Read.All. Моему приложению сначала нужно прочитать все идентификаторы пользователей, а затем получить все метаданные их электронных писем.

Я создал нового арендатора для office365, чтобы проверить это, назовем его - test, и отправил пару писем между 2 пользователями. Итак, сначала я перенаправляю администратора тестовой организации на конечную точку adminconsent, где он / она предоставляет разрешения приложения моему приложению. Это URL, который я использую:

https://login.microsoftonline.com/organizations/v2.0/adminconsent?
client_id=<the app ID>
&state=<some state>
&redirect_uri=<my redirect URL as written in the app configuration>
&scope=https://graph.microsoft.com/.default

После вызова этой конечной точки я вижу свое приложение в списке в тестовой организации под приложениями Enterprise и вижу, что соответствующие разрешения были предоставлены администратором.

Поскольку я не получаю код из этого потока (необходим для потока аутентификации oAuth2), мне нужно снова попросить администратора войти в систему. Я использую этот URL для этого:

https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize?
client_id=<same app ID>
&response_type=code
&redirect_uri=<same redirect URL>
&scope=https://graph.microsoft.com/.default+offline_access+openid+profile
&state=<some state>

После успешного входа в систему я получаю код обратно на свой URL перенаправления и после другого запроса я получаю токен доступа. Используя этот токен доступа, я пытаюсь получить доступ к любому из следующих API:

Но я получаю ErrorAccessDenied с сообщением: Access is denied. Check credentials and try again.

Дополнительная информация: я использую python и пакет MSAL для сборки приложения (используя class - ConfidentialClientApplication) и URL-адреса для потока аутентификации (но не для конечной точки adminconsent, поскольку я не мог выяснить, как это сделать)

Знаете, что я делаю неправильно? Я схожу с ума по этому поводу ...: (

1 Ответ

2 голосов
/ 14 апреля 2020

На этой странице должно быть описано все, что вам нужно: https://docs.microsoft.com/graph/auth-v2-service

URL разрешения администратора должен быть указан c для клиента клиента. Вы можете использовать слово common , если хотите разрешить вход в систему любому арендатору.

https://login.microsoftonline.com/{tenant}/adminconsent

Вы также должны URL-адрес закодировать параметр redirect_uri (и все другие параметры). По какой-то причине пример в этом документе не является URL-кодированным, но здесь значение должно быть URL-кодированным. Вы не должны видеть двоеточия, косые черты, амперсанды и т. Д. c. для этого параметра.

Другой пример, который запрашивает указанные c области для согласия администратора (вместо значения по умолчанию, которое является всеми областями, которые вы указали при регистрации клиентского приложения AAD), см. https://docs.microsoft.com/azure/active-directory/develop/v2-admin-consent .

  1. Вы получите обратный вызов на URI перенаправления, чтобы указать, что все работает. Сюда входит идентификатор клиента, предоставивший вам согласие администратора.

  2. После этого вы инициируете отдельный запрос запроса токена для идентификатора клиента, идентификатора клиента приложения и указанной c запрошенной области действия. Затем он вернет токен доступа с соответствующей областью, который вы можете использовать непосредственно во всех вызовах API. Вы можете сделать это так: https://docs.microsoft.com/azure/active-directory/develop/scenario-daemon-acquire-token?tabs=python#acquiretokenforclient -api

# The pattern to acquire a token looks like this.
result = None

# First, the code looks up a token from the cache.
# Because we're looking for a token for the current app, not for a user,
# use None for the account parameter.
result = app.acquire_token_silent(config["scope"], account=None)

if not result:
    logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
    result = app.acquire_token_for_client(scopes=config["scope"])

if "access_token" in result:
    # Call a protected API with the access token below.
    print(result["token_type"])
else:
    print(result.get("error"))
    print(result.get("error_description"))
    print(result.get("correlation_id"))  # You might need this when reporting a bug.

Надеюсь, что это поможет. В статье выше есть все детали.

...