Вход в одностраничное приложение с использованием неявного потока OAuth 2.0 в Azure Active Directory B2C.

У многих современных приложений есть интерфейс в виде одностраничного приложения, созданный обычно на языке JavaScript. Часто приложение создается с помощью таких платформ, как React, Angular или Vue.js. У одностраничных и других приложений JavaScript, выполняемых в основном в браузере, при аутентификации возникают некоторые дополнительные трудности:

  • Характеристики безопасности этих приложений отличаются от характеристик традиционных серверных веб-приложений.

  • Многие серверы авторизации и поставщики удостоверений не поддерживают запросы на общий доступ к ресурсам независимо от источника (CORS).

  • Полностраничный браузер перенаправляет пользователя из приложения, взаимодействие с которым становится агрессивным.

Рекомендуемый способ поддержки одностраничных приложений — поток кода авторизации OAuth 2.0 (с PKCE).

Некоторые платформы, такие как MSAL.js 1.x, поддерживают только потоки неявного предоставления разрешений. В таких случаях Azure Active Directory B2C (Azure AD B2C) поддерживает поток неявного предоставления авторизации OAuth 2.0. Этот поток описывается в разделе 4.2 спецификации OAuth 2.0. В неявном потоке приложение получает маркеры напрямую из конечной точки авторизации Azure AD B2C, без дополнительного обмена данными между серверами. Вся логика проверки подлинности и обработка сеансов выполняются полностью в клиенте JavaScript с помощью перенаправления страницы или всплывающего окна.

В Azure AD B2C стандартный неявный поток OAuth 2.0 выходит за рамки простой аутентификации и авторизации. В Azure AD B2C вводится параметр политики. Он позволяет использовать OAuth 2.0 для добавления в приложение политик (потоков пользователей), таких как регистрация, вход и управление профилями. В примерах HTTP-запросов в этой статье используется домен {client}.onmicrosoft.com. Замените {tenant}именем вашего арендатора, если он у вас есть. Кроме того, необходимо создать поток пользователя.

В качестве иллюстрации неявного потока входа мы предоставим следующий рисунок. Каждый шаг описан более подробно далее в этой статье.

Схема в стиле дорожки, на которой показан неявный поток OpenID Connect

Отправка запросов проверки подлинности

Если веб-приложению требуется проверять подлинность пользователя и выполнять поток пользователя, оно может направить его к конечной точке /authorize в Azure AD B2C. Пользователь предпринимает действие в зависимости от потока пользователя.

В этом запросе клиент указывает разрешения, которые необходимо получить от пользователя в параметре scope, и поток пользователя, который нужно выполнить. Чтобы понять, как работает запрос, попробуйте поместить его в браузер и запустить. Замените

  • {tenant} на имя вашего арендатора Azure Active Directory B2C;

  • 90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6 на идентификатор приложения, которое вы зарегистрировали в этом клиенте;

  • {policy} именем политики, созданной в этом арендаторе, например b2c_1_sign_in.

GET https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize?
client_id=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6
&response_type=id_token+token
&redirect_uri=https%3A%2F%2Faadb2cplayground.azurewebsites.net%2F
&response_mode=fragment
&scope=openid%20offline_access
&state=arbitrary_data_you_can_receive_in_the_response
&nonce=12345

Параметры, используемые в запросе HTTP GET, описаны в таблице ниже.

Параметр Обязательно Описание
{tenant} Да Имя клиента Azure AD B2C
{policy} Да Имя потока пользователя, который требуется выполнить. Укажите имя пользовательского процесса, созданного в клиенте Azure AD B2C. Например, b2c_1_sign_in, b2c_1_sign_upили b2c_1_edit_profile.
client_id Да Идентификатор приложения, который портал Azure назначил вашему приложению.
response_type Да Должен включать id_token для входа в OpenID Connect. Этот параметр также может содержать тип ответа token. Использование token позволяет приложению получать маркер доступа непосредственно от конечной точки авторизации, не отправляя новый запрос на вход в эту конечную точку. При использовании типа ответа token параметр scope должен содержать область, определяющую ресурсы, для которых необходимо выдавать маркер.
redirect_uri Нет Универсальный код ресурса (URI) перенаправления приложения, на который можно отправлять ответы аутентификации для их получения приложением. Он должен в точности соответствовать одному из URI перенаправления, которые вы добавили в зарегистрированное на портале приложение, но должен быть закодирован в формат URL.
response_mode Нет Указывает метод, с помощью которого результирующий маркер будет отправлен приложению. Для неявных потоков используйте fragment.
область Да Список областей с разделителями-пробелами. Одно значение область указывает на Microsoft Entra идентификатор обоих запрашиваемых разрешений. Область openid определяет разрешение на вход пользователя и получение данных о пользователе в форме маркеров идентификации. Область offline_access для веб-приложений является необязательной. Она означает, что вашему приложению потребуется маркер обновления для продолжительного доступа к ресурсам.
Состояние Нет Значение, включенное в запрос, которое также возвращается в ответе маркера. Это может быть строка любого содержимого. Как правило, с целью предотвращения подделки межсайтовых запросов используется генерируемое случайным образом уникальное значение. Этот параметр также содержит информацию о состоянии пользователя в приложении на момент перед созданием запроса на проверку подлинности (например, сведения об открытой странице или выполняемом потоке пользователя).
nonce Да Значение, включенное в запрос (созданное приложением), которое входит в состав полученного маркера идентификации в качестве утверждения. Приложение может проверять это значение, чтобы устранить атаки с воспроизведением маркеров. Это значение обычно представляет собой случайную уникальную строку, которую можно использовать для определения источника запроса.
prompt Нет Требуемый тип взаимодействия с пользователем. В настоящее время поддерживается только значение login. При использовании этого параметра пользователь должен ввести учетные данные по запросу. Единый вход не сработает.

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

Когда пользователь завершит свой поток пользователя, служба Azure AD B2C отправит в приложение ответ через параметр redirect_uri. Она использует метод, указанный в параметре response_mode. Какие бы действия пользователь ни выполнял и каким бы ни был его поток, ответ всегда будет одинаковым.

Успешный ответ

Успешный ответ с использованием response_mode=fragment и response_type=id_token+token выглядит следующим образом (разрывы строк — для удобства чтения):

GET https://aadb2cplayground.azurewebsites.net/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&token_type=Bearer
&expires_in=3599
&scope="90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6 offline_access",
&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&state=arbitrary_data_you_sent_earlier
Параметр Описание
access_token Маркер доступа, запрошенный приложением от AAD B2C.
token_type Значение типа маркера. Azure AD B2C поддерживает только тип Bearer (Носитель).
expires_in Срок действия маркера доступа (в секундах).
область Области, для которых действителен маркер. Области также можно использовать, чтобы кэшировать маркеры для последующего использования.
id_token Маркер идентификации, запрошенный приложением. Вы можете использовать маркер идентификации для проверки личности пользователя и запуска сеанса пользователя. Дополнительные сведения о маркерах идентификации и их содержимом см. в справочнике по маркерам Azure AD B2C.
state Если в запрос включен параметр state, идентичное значение должно содержаться и в ответе на этот запрос. Приложение должно проверить, совпадают ли значения параметра state в запросе и ответе.

Сообщение об ошибке

Сообщения об ошибках также можно отправлять на универсальный код ресурса (URI) перенаправления, чтобы приложение могло их правильно обработать:

GET https://aadb2cplayground.azurewebsites.net/#
error=access_denied
&error_description=the+user+canceled+the+authentication
&state=arbitrary_data_you_can_receive_in_the_response
Параметр Описание
error Код, используемый для классификации типов произошедших ошибок.
error_description Конкретное сообщение об ошибке, с помощью которого можно определить первопричину возникновения ошибки аутентификации.
state Если в запрос включен параметр state, идентичное значение должно содержаться и в ответе на этот запрос. Приложение должно проверить, совпадают ли значения параметра state в запросе и ответе.

Проверка маркера идентификации

Получение маркера идентификации не является достаточным для прохождения пользователем аутентификации. Проверьте подпись маркера идентификации и убедитесь, что утверждения в маркере соответствуют требованиям вашего приложения. В Azure AD B2C для подписи маркеров и проверки их правильности используются веб-маркеры JSON Web Token (JWT) и шифрование с открытым ключом.

Для проверки маркеров JWT доступны многие библиотеки с открытым исходным кодом в зависимости от выбранного языка. Рекомендуется изучить доступные библиотеки с открытым исходным кодом, а не реализовывать логику проверки самостоятельно. Сведения, приведенные в данной статье, помогут вам узнать, как правильно использовать эти библиотеки.

Служба Azure AD B2C имеет конечную точку метаданных OpenID Connect. Приложение может использовать конечную точку для получения сведений об Azure AD B2C в среде выполнения. Эти сведения включают конечные точки, содержимое маркеров и ключи подписи маркеров. Для каждого потока пользователя в клиенте Azure AD B2C есть документ метаданных JSON. Например, документ метаданных для потока пользователя с именем b2c_1_sign_in в арендаторе fabrikamb2c.onmicrosoft.com находится в каталоге:

https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_sign_in/v2.0/.well-known/openid-configuration

Одно из свойств этого документа конфигурации — jwks_uri. Значение для того же потока пользователя будет таким:

https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_sign_in/discovery/v2.0/keys

Определить, какой поток пользователя был задействован для подписания маркера идентификации (и откуда получать метаданные), можно любым из следующих двух способов.

  • Имя потока пользователя указано в утверждении acr в id_token. Сведения об анализе утверждений маркера идентификации см. в справочнике по маркерам Azure AD B2C.

  • Вы также можете закодировать поток пользователя в значении параметра state при отправке запроса, а затем декодировать параметр state и определить, какой поток был использован.

После получения документа метаданных из конечной точки метаданных OpenID Connect можно использовать открытые ключи RSA-256, расположенные в этой конечной точке, для проверки подписи маркера идентификации. В каждый момент времени в этой конечной точке может находиться множество ключей, каждый из которых определяется kid. Заголовок id_token также содержит утверждение kid. Оно указывает на ключ, который был использован для подписи маркера идентификации. Дополнительные сведения см. в справочнике по маркерам Azure AD B2C, включая раздел Проверка маркеров.

После проверки подписи маркера идентификации для некоторых утверждений требуется проверка. Пример:

  • Проверьте утверждение nonce во избежание атак с использованием воспроизведения маркеров. Его значение должно совпадать с указанным вами в запросе на вход.

  • Проверьте утверждение aud, чтобы убедиться, что для вашего приложения был выдан маркер идентификации. Его значением должен быть идентификатор приложения вашего приложения.

  • Проверьте утверждения iat и exp, чтобы убедиться, что срок действия маркера идентификации не истек.

Еще несколько проверок, которые необходимо выполнить, подробно описаны в спецификации OpenID Connect Core. Может также потребоваться проверить дополнительные утверждения в зависимости от вашего сценария. Ниже приведены некоторые из стандартных проверок:

  • Обеспечение регистрации пользователя или организации в приложении.

  • Предоставление пользователю необходимого уровня авторизации и привилегий.

  • Обеспечение определенной надежности проверки подлинности, например с помощью Microsoft Entra многофакторной проверки подлинности.

Дополнительные сведения об утверждениях в маркере идентификации см. в справочнике по маркерам Azure AD B2C.

После проверки маркера идентификации можно запустить сеанс пользователя. В приложении используйте утверждения в маркере идентификации для получения сведений о пользователе. Эти сведения можно использовать для отображения, записей, авторизации и т. д.

Получение маркеров доступа

Если ваши веб-приложения будут только выполнять потоки пользователей, следующие несколько разделов можно пропустить. Сведения в следующих разделах применяются только к веб-приложениям, которым необходимо выполнять вызовы с проверкой подлинности к веб-API, которые защищены с помощью Azure AD B2C.

Теперь, когда вы выполнили вход пользователя в spa, вы можете получить маркеры доступа для вызова веб-API, защищенных Microsoft Entra ID. Даже если вы уже получили маркер с помощью типа ответа token, этот метод можно использовать для получения маркеров для дополнительных ресурсов. При этом пользователям не нужно повторно выполнять вход.

В обычном потоке веб-приложения необходимо отправить запрос к конечной точке /token. Но конечная точка не поддерживает запросы CORS, поэтому получить обновленный маркер с помощью вызовов AJAX нельзя. Вместо этого для получения новых маркеров для других веб-API можно использовать неявный поток данных в скрытом HTML-элементе iframe. Ниже приведен пример (разрывы строк — для удобства чтения):

https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize?
client_id=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6
&response_type=token
&redirect_uri=https%3A%2F%2Faadb2cplayground.azurewebsites.net%2F
&scope=https%3A%2F%2Fapi.contoso.com%2Ftasks.read
&response_mode=fragment
&state=arbitrary_data_you_can_receive_in_the_response
&nonce=12345
&prompt=none
Параметр Необходим? Описание
{tenant} Обязательно Имя клиента Azure AD B2C
{policy} Обязательно Пользовательский процесс для выполнения. Укажите имя пользовательского процесса, созданного в клиенте Azure AD B2C. Например, b2c_1_sign_in, b2c_1_sign_upили b2c_1_edit_profile.
client_id Обязательно Идентификатор приложения, назначенный вашему приложению на портале Azure.
response_type Обязательно Должен включать id_token для входа в OpenID Connect. Этот параметр также может содержать token типа ответа. Если использовать token, то приложение сможет получить маркер доступа непосредственно от конечной точки авторизации, не отправляя новый запрос на вход в эту конечную точку. При использовании типа ответа token параметр scope должен содержать область, определяющую ресурсы, для которых необходимо выдавать маркер.
redirect_uri Рекомендуемая Универсальный код ресурса (URI) перенаправления приложения, на который можно отправлять ответы аутентификации для их получения приложением. Он должен в точности соответствовать одному из универсальных кодов ресурса (URI) перенаправления, зарегистрированных на портале, но иметь форму URL-адреса.
область Обязательно Список областей с разделителями-пробелами. Для получения маркеров укажите все области, которые необходимы для целевого ресурса.
response_mode Рекомендуемая Указывает метод, с помощью которого результирующий маркер будет отправлен приложению. Для неявных потоков используйте fragment. Можно указать два других режима (query и form_post), но в неявном потоке работать нельзя.
Состояние Рекомендуемая Значение, включенное в запрос, которое также возвращается в ответе маркера. Это может быть строка любого содержимого. Как правило, с целью предотвращения подделки межсайтовых запросов используется генерируемое случайным образом уникальное значение. Этот параметр также включает информацию о состоянии пользователя в приложении перед созданием запроса на проверку подлинности. Например, сведений об открытой на тот момент странице или представлении.
nonce Обязательно Значение, включенное в запрос и созданное приложением, которое включено в полученный маркер идентификации в качестве утверждения. Приложение может проверять это значение, чтобы устранить атаки с воспроизведением маркеров. Обычно это значение представляет собой случайную уникальную строку, которая определяет источник запроса.
prompt Обязательно Для обновления и получения маркеров в скрытом запросе iframe используйте prompt=none. При этом iframe не перестает отвечать на странице входа приложения, сразу же возвращая данные.
login_hint Обязательно Для обновления и получения маркеров в скрытом запросе iframe включите в эту подсказку имя пользователя. Это позволяет различать сеансы пользователя в определенный момент времени. Вы можете извлечь имя пользователя из более раннего сеанса входа, используя утверждение preferred_username (для получения утверждения preferred_username требуется область profile).
domain_hint Обязательно Может иметь значение consumers или organizations. Для обновления и получения маркеров в скрытом запросе iframe включите в запрос значение domain_hint. Чтобы определить, какое значение использовать, извлеките утверждение tid из маркера идентификатора более раннего сеанса входа (для получения утверждения tid требуется областьprofile). Если значение утверждения tid — 9188040d-6c67-4c5b-b112-36a304b66dad, следует использовать domain_hint=consumers. В противном случае используйте коллекцию domain_hint=organizations.

Если задать параметр prompt=none, то этот запрос успешно выполнится или немедленно завершится с ошибкой, и вы вернетесь к своему приложению. В случае успеха ответ будет отправлен в ваше приложение на URI перенаправления с помощью метода, заданного в параметре response_mode.

Успешный ответ

Успешный ответ с использованием response_mode=fragment выглядит как в следующем примере.

GET https://aadb2cplayground.azurewebsites.net/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&state=arbitrary_data_you_sent_earlier
&token_type=Bearer
&expires_in=3599
&scope=https%3A%2F%2Fapi.contoso.com%2Ftasks.read
Параметр Описание
access_token Маркер, запрошенный приложением.
token_type Маркер всегда будет иметь тип Bearer (Носитель).
state Если в запрос включен параметр state, идентичное значение должно содержаться и в ответе на этот запрос. Приложение должно проверить, совпадают ли значения параметра state в запросе и ответе.
expires_in Срок действия маркера доступа (в секундах).
scope Области, для которых действителен маркер доступа.

Сообщение об ошибке

Сообщения об ошибках также можно отправлять на универсальный код ресурса (URI) перенаправления, чтобы приложение могло их правильно обработать. Для prompt=none ожидаемая ошибка выглядит как в следующем примере.

GET https://aadb2cplayground.azurewebsites.net/#
error=user_authentication_required
&error_description=the+request+could+not+be+completed+silently
Параметр Описание
error Строка кода ошибки, которую можно использовать для классификации типов возникающих ошибок. и реагирования на них.
error_description Конкретное сообщение об ошибке, с помощью которого можно определить первопричину возникновения ошибки аутентификации.

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

Маркеры обновления

Срок действия маркеров идентификации и доступа достаточно короткий. Поэтому приложение должно быть готово периодически обновлять их. Неявные потоки не разрешают получать маркер обновления из соображений безопасности. Чтобы обновить токен любого типа, вы можете использовать неявный поток данных в скрытом HTML-элементе iframe. В запрос авторизации добавьте параметр prompt=none. Чтобы получить новое значение id_token, обязательно используйте response_type=id_token и scope=openid, а также параметр nonce.

Отправка запроса на выход

Чтобы пользователь вышел из приложения, перенаправьте его на конечную точку выхода в Azure AD B2C. Затем вы можете очистить сеанс пользователя в приложении. Если вы не перенаправите пользователя, он сможет повторно выполнить аутентификацию в приложении без ввода учетных данных, поскольку для него сохранится действующий сеанс единого входа в AAD B2C.

Можно просто перенаправить пользователя на точку end_session_endpoint, которая указана в том же документе метаданных OpenID Connect, описанном выше в разделе Проверка маркера идентификации. Пример:

GET https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/logout?post_logout_redirect_uri=https%3A%2F%2Faadb2cplayground.azurewebsites.net%2F
Параметр Обязательно Описание
{tenant} Да Имя клиента Azure AD B2C.
{policy} Да Поток пользователя, который вы хотите использовать для выхода пользователя из приложения. Это должен быть тот же поток пользователя, который приложение использовало для входа пользователя.
post_logout_redirect_uri Нет URL-адрес, по которому необходимо перенаправить пользователя после успешного выхода. Если он не указан, Azure AD B2C показывает пользователю универсальное сообщение.
Состояние Нет Если в запрос включен параметр state, идентичное значение должно содержаться и в ответе на этот запрос. Приложение должно проверять идентичность значений state в запросе и ответе.

Примечание

При перенаправлении пользователя на end_session_endpoint очищается часть его данных состояния единого входа через Azure AD B2C. Хотя фактически пользователь не выходит из сеанса поставщика удостоверений социальных сетей. Если пользователь выберет тот же поставщик удостоверений для последующего входа, проверка подлинности для него будет выполнена автоматически, без ввода учетных данных. Например, если пользователь хочет выйти из приложения Azure AD B2C, это не означает, что он также хочет полностью выйти из учетной записи Facebook. Тем не менее для локальных учетных записей необходимо правильно завершить сеанс пользователя.

Дальнейшие действия

См. пример кода: Вход с помощью Azure AD B2C в одностраничное приложение JavaScript.