Я использую единый вход OAuth и O365 в своем веб-приложении. При запуске приложения пользователю предлагается войти в систему с помощью единого входа. Получение токена работает и вход выполнен успешно. После входа в систему пользователь взаимодействует с Graph с помощью графического клиента, который создает приложение ConfidentialClientApplication и получает доступ к хранилищу токенов сеанса. Это тоже хорошо работает. Но через 45 минут или час тот же самый клиентский вызов не удается. В хранилище токенов есть объект, но когда CCA вызывает GetAccountsAsync (), он всегда возвращает 0, поэтому сбой AcquireTokenSilentAsync. Не удалось ли сохранить некоторую соответствующую информацию в хранилище токенов? Что я здесь не так делаю и как мне это исправить? Я попытался повторно выполнить вызов и затем снова обратиться к хранилищу токенов, но результат тот же: несмотря на наличие объекта в хранилище, GetAccountsAsync возвращает 0 учетных записей.
Мне интересно, связано ли это с примерами из TokenStore, на которых я основывался. Я пытался сохранить токен в Session и в Cache, и они оба работают, когда пользователь впервые проходит аутентификацию, но в какой-то момент что-то происходит, что делает их недействительными при последующих запросах.
Из Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions() { CookieSecure = CookieSecureOption.Never, CookieName = "AppCookie", ExpireTimeSpan = TimeSpan.FromDays(7) });
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
RoleClaimType = "roles",
NameClaimType = "upn"
},
UseTokenLifetime = true,
RedirectUri = postLogoutRedirectUri,
Scope = "openid profile offline_access " + graphScopes,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl;
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
AuthorizationCodeReceived = OnAuthorization,
AuthenticationFailed = OnAuthenticationFailed
}
});
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.OwinContext.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
private async Task OnAuthorization(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
SessionTokenStore tokenStore = new SessionTokenStore(signedInUserID, context.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase);
ConfidentialClientApplication cca = new ConfidentialClientApplication(clientId, postLogoutRedirectUri, new ClientCredential(appKey), tokenStore.GetMsalCacheInstance(), null);
var accounts = await cca.GetAccountsAsync();
try
{
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, graphScopes.Split(' '));
}
catch (MsalException ex)
{
string message = "AcquireTokenByAuthorizationCodeAsync threw an exception";
context.HandleResponse();
context.Response.Redirect($"/Home/Error?message={message}&debug={ex.Message}");
}
catch (Microsoft.Graph.ServiceException ex)
{
string message = "GetUserDetailsAsync threw an exception";
context.HandleResponse();
context.Response.Redirect($"/Home/Error?message={message}&debug={ex.Message}");
}
catch (Exception ex)
{
throw ex;
}
}
Получение клиента Graph:
private static GraphServiceClient GetAuthenticatedClient()
{
try
{
return new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
SessionTokenStore tokenStore = new SessionTokenStore(signedInUserID, new HttpContextWrapper(HttpContext.Current));
ConfidentialClientApplication cca = new ConfidentialClientApplication(appId, redirectUri.ToString(), new ClientCredential(appSecret), tokenStore.GetMsalCacheInstance(), null);
var accounts = await cca.GetAccountsAsync();
try
{
var result = await cca.AcquireTokenSilentAsync(graphScopes.Split(' '), accounts.FirstOrDefault(), null, true);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
}
catch (MsalUiRequiredException ex)
{
if (ex.ErrorCode == MsalUiRequiredException.UserNullError)
{
new HttpContextWrapper(HttpContext.Current).GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = redirectUri }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
var account = await cca.GetAccountAsync(signedInUserID);
var result = await cca.AcquireTokenSilentAsync(graphScopes.Split(' '), account, null, true);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
}
else
{
throw ex;
}
}
}));
}
catch (Exception e)
{
throw e;
}
}