- ASP.NET Core Identity для стандартной аутентификации пользователя
- Внешняя проверка подлинности с помощью Microsoft 365
Поток внешнего входа работает должным образом: и пользователи могут успешно отправлять электронные письма через Microsoft Graph, однако через некоторое время токен Microsoft Graph удаляется из базы данных, хотя пользователь все еще вошел в приложение. В результате отправка электронных писем завершается сбоем из-за ошибки, связанной с отсутствием токена.
Я не ожидал такого поведения, хотя предполагалось, что за сценой произошло своего рода автоматическое обновление токена. Кроме того, когда происходит это исключение, пользователь все еще находится в системе.
Кто-нибудь сталкивался с подобной проблемой или имеет предложения по ее решению? Любые идеи или советы будут очень признательны. Ниже приведен соответствующий код, который мы уже реализовали для справки.
Код: Выделить всё
appsettings.jsonКод: Выделить всё
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",
"IsEnabled": true
},
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "mail.send"
},
Код: Выделить всё
Startup.csКод: Выделить всё
services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles()
.AddEntityFrameworkStores()
.AddErrorDescriber();
services.Configure(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = true;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(8);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = false;
});
services.ConfigureApplicationCookie(config =>
{
config.LoginPath = "/Home/Login";
});
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = connectionString;
options.SchemaName = "dbo";
options.TableName = "TbDistributedTokenCache";
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.HttpOnly = false;
options.Cookie.IsEssential = false;
});
try
{
var conf = Configuration.GetSection("AzureAd");
if (conf != null && Boolean.Parse(conf["IsEnabled"]))
{
var credentials = GetAzureAuthCredentials();
if (credentials != null)
{
var initialScopes = Configuration["MicrosoftGraph:Scopes"]?.Split(' ');
services.AddAuthentication()
.AddMicrosoftIdentityWebApp(opt =>
{
opt.ClaimActions.MapAll();
opt.GetClaimsFromUserInfoEndpoint = true;
opt.ClientId = credentials.ClientId;
opt.TenantId = credentials.TenantId;
opt.ClientSecret = credentials.ClientSecret;
opt.CallbackPath = conf["CallbackPath"];
opt.SignedOutCallbackPath = conf["SignedOutCallBackPath"];
opt.Domain = conf["Domain"];
opt.Instance = conf["Instance"];
}, cookieScheme: null
// Solves cookieScheme conflict with default aspnetcore identity cookiescheme
//https://github.com/AzureAD/microsoft-identity-web/issues/133
)
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("MicrosoftGraph"))
.AddDistributedTokenCaches();
services.AddScoped();
}
}
}
catch (Exception) { }
Код: Выделить всё
DelegatedO365ManagerКод: Выделить всё
public class DelegatedOffice365Manager : IOffice365Manager
{
private readonly GraphServiceClient _graphClient;
private Recipient[] ParseRecipients(string recipientString)
{
if (string.IsNullOrWhiteSpace(recipientString))
{
return Array.Empty();
}
return recipientString
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(email => new Recipient
{
EmailAddress = new EmailAddress
{
Address = email.Trim()
}
})
.ToArray();
}
public DelegatedOffice365Manager(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task SendEmailAsync(Office365EmailMessage email)
{
try
{
var message = new Message
{
From = new Recipient
{
EmailAddress = new EmailAddress
{
Address = email.FromAddress
}
},
Subject = email.Subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = email.BodyContent
},
ToRecipients = ParseRecipients(email.ToRecipients),
CcRecipients = ParseRecipients(email.CcRecipients),
BccRecipients = ParseRecipients(email.BccRecipients),
};
await _graphClient.Me
.SendMail(message, email.SaveToSentItems)
.Request()
.WithAuthenticationScheme(OpenIdConnectDefaults.AuthenticationScheme)
.PostAsync();
return (true, string.Empty);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
}
Код: Выделить всё
[AuthorizeForScopes(Scopes = new string[] { "mail.send" }, AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme)]
public async Task SendMailWithOffice365(O365EmailMessage email)
{
if (_o365Manager == null)
{
return new JsonResult(new { statusCode = -1, message = "Graph client not initialized" });
}
if (User.GetMsalAccountId() == null)
{
return new JsonResult(new { statusCode = -2, message = "User not authenticated with Office 365" });
}
var result = await _o365Manager.SendEmailAsync(email);
if (result.Success)
{
return new JsonResult(new { statusCode = 0, message = "OK" });
}
else
{
return new JsonResult(new { statusCode = -3, message = result.ErrorMessage });
}
}
Код: Выделить всё
function sendMailW365(params) {
// Define the URL endpoint for sending mail with Office 365
let url = baseUrl + 'api/data/SendMailWithOffice365';
$.ajax({
url: url,
data: params,
headers: { 'x-ReturnUrl': '/' },
type: "POST",
cache: false,
contentType: 'application/json',
success: function (response) {
let statusCode = response.statusCode;
let message = response.message;
// Handle different status codes
switch (statusCode) {
case 0:
// If status code is 0, mail was sent successfully
appendMessage('Send Mail', 'E-Mail sent successfully!', 'INF', 10000);
break;
case -2:
// If status code is -2, user is not authenticated with Office 365
appendMessage('Send Mail', 'User not authenticated with Office 365', 'ALR', 10000);
break;
case -3:
// If status code is -3, there was an error sending the mail, prompt re-login
appendMessage('Send Mail', "Error sending the mail. Please re-login using Office 365.", 10000);
console.log(message); // Log the error message
break;
}
},
// On error during the request
error: function (xhr, status, error) {
console.error("Error sending the mail:", error);
appendMessage('Send Mail', "Error sending the mail! Mail not sent.", 'ALR', 10000);
// Redirect to the location provided in the response header
window.location = xhr.getResponseHeader("Location");
}
});
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... en-expirat
Мобильная версия