Проверка подлинности SQL Azure в качестве пользователя через API с платформой MS Identity
У меня есть веб-приложение ASP.NET Core 3.1, вызывающее веб-API ASP.NET Core 3.1, который, в свою очередь, обращается к базе данных SQL Azure. Аутентификация осуществляется через MSAL (Microsoft Identity Platform), то есть с использованием относительно нового
Microsoft.Identity.Web
Microsoft.Identity.Web.UI
библиотеки.
Цель состоит в том, чтобы гарантировать, что пользователь извлекает данные из SQL через API в контексте своего собственного имени входа, тем самым обеспечивая безопасность на уровне строк, аудит доступа и другие полезные вещи.
Мне удалось заставить процесс входа работать для веб-приложения - и благодаря этому он получает действительный токен доступа для доступа к API с использованием области, созданной мной при регистрации последнего в AD.
Когда я запускаю и API, и приложение локально из Visual Studio, все работает должным образом - приложению предоставляются правильные токены доступа для доступа к API, а API для доступа к SQL - в обоих случаях под пользователем (то есть моей) идентичностью..
Однако когда я публикую API в службах приложений в Azure и получаю к нему доступ из локальной версии веб-приложения или из его версии, размещенной в службах приложений, токен доступа, который API получает для доступа к SQL, содержит приложение API. Удостоверение (управляемое удостоверение, назначенное системой), а не удостоверение пользователя. Хотя я могу получить доступ к SQL как к приложению, это не то, что нам нужно.
Веб-приложение получает свой токен доступа с помощью
GetAccessTokenForUserAsync
ITokenAcquisition
- принимая в качестве параметра единственную область, которую я определил для API.
API получает свой токен (для доступа к SQL) следующим образом:
var token = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net", _tenantId)
... где _tenantId - это идентификатор клиента подписки.
Я добавил разрешение API «user_impersonation» базы данных SQL Azure для регистрации в AD для API, но это не помогло. Кстати, по какой-то причине Azure дает полное имя этого разрешения как
https://sql.azuresynapse.usgovcloudapi.net/user_impersonation
Я нашел несколько подобных сообщений, но в основном для более старых версий набора решений. Я надеюсь, что мне не придется писать собственный код для отправки запросов токенов - предполагается, что это будет обрабатываться библиотеками MSAL.
Должен ли я каким-то образом отдельно запрашивать область доступа SQL из веб-приложения после входа в систему или API должен делать что-то другое, чтобы получить токен доступа SQL, который идентифицирует пользователя? Почему он отлично работает при локальном запуске?
Кажется, что это должен быть очень распространенный вариант использования (самый распространенный?), Но он почти не документирован - большая часть документации, которую я нашел, относится только к используемому идентификатору приложения или не говорит вам, что делать с этой конкретной технологией. куча.
2 ответа
Наконец-то - успех! В конце концов, это была критически важная часть документации: платформа идентификации Microsoft и поток OAuth 2.0 от имени пользователя - ключевыми моментами были:
- Приложение запрашивает только токен для доступа к API.
- Затем API запрашивает токен от имени пользователя, идентифицированного с помощью первого токена, для доступа к SQL.
Ключ в том, что, поскольку API не может вызвать окно согласия для второго шага, мне пришлось использовать вкладку « Корпоративные приложения » на портале Azure для предварительного предоставления разрешений для SQL.
Так что хорошая новость в том, что это действительно работает: может быть, для некоторых это очевидно, но ИМО мне потребовалось слишком много времени, чтобы найти ответ на этот вопрос. Я напишу более полное объяснение того, как это сделать, в свое время, так как не только я борюсь с этим.
Плохая новость заключается в том, что в ходе своих исследований я обнаружил, что Azure B2C (а это следующее, что мне нужно добавить) не поддерживает этот поток «от имени» - щелкните здесь, чтобы узнать подробности . Это большой позор, поскольку я считаю, что это наиболее очевидный вариант использования! Ну что ж, вернемся к чертежной доске.
В настоящее время я работаю над аналогичной проблемой, используя веб-приложение Net5.0. Причина, по которой он работает локально, заключается в том, что вы вошли в Visual Studio с пользователем, который имеет доступ к Azure SQL, и это права, которые вы получаете в Db. IDE использует эти учетные данные вместо идентификатора управляемой службы, последний используется при загрузке приложения в Azure.
Как вы отметили, при регистрации приложения вам необходимо предоставить разрешение приложению для базы данных SQL Azure user_impersonation.
В вашем коде вам нужно запросить токен из https://database.windows.net//.default (обратите внимание на //, поскольку он необходим для конечных точек v1). Ссылаясь на /.default, вы запрашиваете все разрешения, которые вы выбрали для приложения на портале регистрации приложений.
В Startup.cs вам необходимо EnableTokenAcquisitionToCallDownstreamApi с требуемой областью действия.
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(new[]
{"https://database.windows.net//.default"})
// Adds the User and App InMemory Token Cache
.AddInMemoryTokenCaches();
services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the
// default policy
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddDbContext<MyDatabaseContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyAzureConnection")));
// The database interface
services.AddScoped<ITodos, TodoData>();
services.AddRazorPages()
.AddRazorRuntimeCompilation()
.AddMvcOptions(o =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
o.Filters.Add(new AuthorizeFilter(policy));
})
.AddMicrosoftIdentityUI();
Вам также необходимо украсить свои контроллеры
[AuthorizeForScopes(Scopes = new string[]{"https://database.windows.net//.default"}]
и включите необходимые области для этого контроллера. Для Razor он находится в верхней части модели страницы и требует ссылки на «using Microsoft.Identity.Web»;
namespace ToDoApp.Pages.Todos
{
[AuthorizeForScopes(ScopeKeySection = "AzureSQL:BaseUrl")]
public class CreateModel : PageModel
Я использую раздел в моем appsettings.json для области видимости и получаю его с помощью
ScopeKeySection
:
"AzureSQL": {
"BaseUrl": "https://database.windows.net//.default",
"Scopes": "user_impersonation"
}
Это показывает вам, где его включить для MVC, Razor и Blazor:
Наконец, вашему DbContext нужен токен, который вы могли бы передать ему из клиентского приложения (возможно ...).
Вот как я это делаю сейчас
public class MyDatabaseContext : DbContext
{
private readonly ITokenAcquisition _tokenAcquisition;
public MyDatabaseContext (ITokenAcquisition tokenAcquisition,
DbContextOptions<MyDatabaseContext> options)
: base(options)
{
_tokenAcquisition = tokenAcquisition;
string[] scopes = new[]{"https://database.windows.net//.default"};
var result = _tokenAcquisition.GetAuthenticationResultForUserAsync(scopes)
.GetAwaiter()
.GetResult();
token = result.AccessToken;
var connection = (SqlConnection)Database.GetDbConnection();
connection.AccessToken = result.token;
}
Это ошибочное решение. Если я перезапущу приложение и снова попытаюсь получить к нему доступ, я получаю сообщение об ошибке
Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user
Похоже, это связано с TokenCache. Если я выйду и снова войду или очищу кеш браузера, ошибка будет устранена. У меня есть обходной путь, который подписывает приложение в случае сбоя, но он недостаточен, поскольку я использую учетные данные приложения.
Однако он успешно подключается к базе данных SQL Azure как пользователь, а не как приложение с правами пользователя. Когда я устраню ошибку (или найду ее), я обновлю этот ответ.