You typically use the following two ASP.NET Core authentication handlers to implement remote authentication:
Furthermore the BFF plumbing relies on the configuration of the ASP.NET Core default authentication schemes. This describes how the two handlers share the work.
OpenID Connect for challenge and signout - cookies for all the other operations:
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
options.DefaultSignOutScheme = "oidc";
})
.AddCookie("cookie", options => { ... })
.AddOpenIdConnect("oidc", options => { ... });
The OIDC handler connects the application to the authentication / access token system.
The exact settings depend on the OIDC provider and its configuration settings. We recommend:
builder.Services.AddAuthentication().AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://demo.duendesoftware.com";
// confidential client using code flow + PKCE
options.ClientId = "spa";
options.ClientSecret = "secret";
options.ResponseType = "code";
// query response type is compatible with strict SameSite mode
options.ResponseMode = "query";
// get claims without mappings
options.MapInboundClaims = false;
options.GetClaimsFromUserInfoEndpoint = true;
// save tokens into authentication session
// to enable automatic token management
options.SaveTokens = true;
// request scopes
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("API");
// and refresh token
options.Scope.Add("offline_access");
});
The OIDC handler will use the default sign-in handler (the cookie handler) to establish a session after successful validation of the OIDC response.
The cookie handler is responsible for establishing the session and manage authentication session related data.
Things to consider:
builder.Services.AddAuthentication().AddCookie("cookie", options =>
{
// set session lifetime
options.ExpireTimeSpan = TimeSpan.FromHours(8);
// sliding or absolute
options.SlidingExpiration = false;
// host prefixed cookie name
options.Cookie.Name = "__Host-spa";
// strict SameSite handling
options.Cookie.SameSite = SameSiteMode.Strict;
});
The SameSite cookie is a feature of modern browsers that restricts cookies so that they are only sent to pages originating from the site where the cookie was originally issued. This prevents CSRF attacks and helps with improving privacy, because cross-site requests will no longer implicitly include the user’s credentials.
If you configure SameSiteMode.Strict
, this means that if a user originates from an external site and is redirected or linked to the BFF application, then the authentication cookie is not sent automatically. So, the application will consider the user to be not logged in, even though there may be a valid authentication cookie in the cookie jar. If the user refreshes the page, or visits a link on your site that forces a complete page reload, then the authentication cookie will be sent along normally again.
This also happens when you have an identity provider that’s hosted on a different site than the BFF, in combination with SameSiteMode.Strict
. After successful authentication at the IdP, the user will be redirected back to the BFF site. The server will then place an authentication cookie in the browser, but the browser will not automatically include it in subsequent requests until the full page is manually reloaded by the user. This means the user appears to still be logged out, even though the cookie is there.
So, if you have an Identity Provider that’s hosted under a different site than your BFF, you may want to configure your cookie policy to be SameSiteMode.Lax
.
Chrome will make an exception for cookies set without a SameSite
attribute less than 2 minutes ago. Such cookies will also be sent with non-idempotent (e.g. POST) top-level cross-site requests despite normal SameSite=Lax
cookies requiring top-level cross-site requests to have a safe (e.g. GET) HTTP method. Support for this intervention (“Lax + POST”) will be removed in the future. (source: chromestatus)