Получение информации о пользователе, если WebAPI аутентифицирован на основе учетных данных клиента
До появления Azure AD через веб-приложение и API были -
Пользователь вводит идентификатор и пароль в форму приложения -> пользователь входит в систему (Asp.net Identity) -> с теми же учетными данными, генерирует токен (grant_type=password) в WebAPI-> сохраняет этот токен как cookie для дальнейшего использования.
Теперь с Azure AD -
У меня есть сценарий, где я должен аутентифицировать пользователя с помощью Azure AD. Webapi в Azure передает JWT, содержащий зарегистрированную информацию о пользователе. Из этой информации я извлекаю электронную почту пользователя и заставляю свое веб-приложение войти в систему (Asp.net Identity).
Web App вызывает WebAPI для получения токена, поскольку я не могу передать пароль, поэтому мой вызов токена изменился на (grant_type = client_credentials). Тем не менее, я теряю информацию об отдельных пользователях, которые вошли в веб-API. Если я проверяю context.user, он указывает на уникальный идентификатор клиента вместо любого пользователя.
Я знаю, что это грязно, но требовалось использовать те же приложения для проверки подлинности Azure AD, а также для проверки подлинности на основе форм. Пожалуйста, предложите, если есть какой-либо более простой способ добиться этого.
Изменить: я использую Owin OAuth
Я использую одно и то же приложение для проверки подлинности с помощью формы и Azure AD. Однако недавно пришлось добавить механизм регистрации на уровне веб-API, который работает для локального приложения, которое регистрирует с использованием идентификатора пользователя / пароля. Но это не работает, если webapi выдает токен с учетными данными клиента. Я попытался передать имя пользователя в качестве параметра при проверке идентификатора клиента (в ValidateClientAuthentication) и войти в систему пользователя. однако webapi не может сохранить эту информацию пользователя.
2 ответа
Вот что я сделал, чтобы исправить мою проблему - вместо присвоения утверждений идентификатора для клиента, я сгенерировал идентификатор пользователя. (_username - это локальная переменная, которая получает назначенное значение в "ValidateClientAuthentication" из параметра. Я понимаю, что это может быть не идеальным способом, но теперь мое приложение теперь работает с проверкой подлинности форм, а также с Azure AD с использованием токенов JWT)
public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindByNameAsync(_userName);
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType);
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
Поток учетных данных клиента используется для приложений или служб демона, чтобы делегировать приложение для управления ресурсом. В этом случае отсутствует информация о контексте пользователя.
Исходя из моего понимания, когда мы используем Azure AD в качестве поставщика удостоверений, мы используем протокол соединения OpenId для получения формы id_token Azure AD. OpenId connect также поддерживает гибридный поток для получения кода авторизации и id_token в одном запросе.
Если вы разрабатывали с MVC с компонентом OWIN, этот поток по умолчанию. Нам нужно только получить токен доступа с кодом авторизации (поток предоставления кода OAuth 2.0), возвращаемым Azure AD, для вызова веб-API, и для этого не требуется повторной регистрации пользователей. А чтобы получить токен с кодом авторизации, мы можем добавить уведомление, как показано ниже:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = redirectUri,
RedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
//
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
//
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed
}
});
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
// If you create the redirectUri this way, it will contain a trailing slash.
// Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, graphResourceId);
}
Полный пример кода для вызова веб-API из веб-приложения вы можете найти здесь.