My application is using Microsoft Identity Web and is a BFF. (backend-for-frontend). It serves the SPA, takes care of auth and proxies API calls to the real API. Auth is stored in the cookie. This way the SPA doesn't need any auth, except for something like an api/users/me call to figure out who the user is. You can't even access the HTML/CSS/JS because before you access it, the SPA redirects you to the Microsoft Login.
This is not really covered in the docs or samples, so I wonder the following. Is my signout currently implemented correctly?
Please take a look at my code and the following questions about it. Note that stuff like AddMicrosoftIdentityUi() is added to my codebase, but not added here in the code to keep things short(er).
Auth setup:
// Read up on SameSite cookies for more information:
// Copied from https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/blob/f8a37e010cacbc48b63e7d8b875b18b9a2c17313/1-WebApp-OIDC/1-1-MyOrg/Startup.cs
// https://github.com/AzureAD/microsoft-identity-web/wiki/SameSite-Cookies
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
// Handling SameSite cookie according to https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
options.HandleSameSiteCookieCompatibility();
});
// We use these custom cookie, ticket and token settings to avoid them being set as session tokens, which AddMicrosoftIdentityWebAppAuthentication() would do by default.
// With these settings they are valid for 90 days which means that even if you logged out of your account somewhere else, you'd still be logged in here.
// The problem with session cookies is that the app is installable as a PWA and if you were to end your session by closing your browser and/or log out somewhere else, you'd have to log in again constantly.
// This is actually a feature of single sign out, but could also be annoying for the user.
// TODO: Perhaps we could investigate a while after launch if users would prefer real single sign out, which would mean using session cookies.
services.Configure<CookieAuthenticationOptions>(WellKnownAuthenticationSchemes.CookieName, opt =>
{
opt.Cookie.IsEssential = true;
opt.Cookie.Name = "cookie_name";
// Note: This is not secure, but required for Safari because it is a stupid browser.
// Safari does not work with SSM.Lax, which would be more secure against CSRF.
// Read more here:
// https://github.com/IdentityServer/IdentityServer4/issues/2595
// https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-5.0
// Or google stuff like "Safari login azure ad infinite loop" or "safari samesite" etc...
opt.Cookie.SameSite = SameSiteMode.None;
opt.Cookie.SecurePolicy = Environment.IsDevelopment()
? CookieSecurePolicy.SameAsRequest
: CookieSecurePolicy.Always;
opt.Cookie.HttpOnly = true;
opt.SlidingExpiration = true;
opt.ExpireTimeSpan = TimeSpan.FromDays(90);
});
services.Configure<MicrosoftIdentityOptions>(opt =>
{
opt.Events.OnTicketReceived = context =>
{
// Mark the authentication properties as persistent, so the cookie expiration settings will be applied
context.Properties.IsPersistent = true;
return Task.CompletedTask;
};
});
services.Configure<MsalDistributedTokenCacheAdapterOptions>(opt =>
{
opt.SlidingExpiration = TimeSpan.FromDays(90);
});
if (Configuration.GetValue("Features:UseDistributedCache", false))
{
var connectionString = Configuration.GetConnectionString("DistributedSqlCache");
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = connectionString;
options.TableName = "DistributedCache";
options.SchemaName = "cache";
});
}
services.AddAuthentication(WellKnownAuthenticationSchemes.CookieName);
services
.AddMicrosoftIdentityWebAppAuthentication(Configuration,
cookieScheme: WellKnownAuthenticationSchemes.CookieName)
.EnableTokenAcquisitionToCallDownstreamApi(Configuration.GetValue<string>("Scopes").Split(" "))
.AddDistributedTokenCaches();
var dataProtectionBuilder = services.AddDataProtection();
if (Configuration.GetValue("Features:PersistDataProtectionToDisk", false))
{
dataProtectionBuilder.PersistKeysToFileSystem(new DirectoryInfo(Configuration.GetValue<string>("DataProtection:Path")));
}
Logout:
// Note: The SPA has a <form method="POST" action="/logout"> which activates this.
[HttpPost("/logout")]
public IActionResult Logout()
{
return SignOut(new AuthenticationProperties
{
RedirectUri = "/"
}, WellKnownAuthenticationSchemes.CookieName, WellKnownAuthenticationSchemes.OpenIdConnect);
}
- Is my Logout good enough, or should I use the SignOut at the
/microsoftidentity/account/signoutpath? Considering I use a custom cookie auth setup. - If so, is it possible for me to change that path? Not a big problem if that can't easily be done.
- Why is the SignOut a GET request? Most people in this thread recommend using a POST. Why is a GET chosen?
- If my logout suffices, do I even need the UI package?