I am trying to enabled OpenIdConnect and Cookies in a .NET 8 web API:
builder.Services
.AddAuthentication(options =>
{
options.DefaultChallengeScheme = "MyPolicy";
options.DefaultAuthenticateScheme = "MyPolicy";
})
.AddCookie(options =>
{
// add an instance of the patched manager to the options:
options.CookieManager = new ChunkingCookieManager();
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.None;
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = "";
options.ClientId = "";
options.ClientSecret = "";
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ResponseType = "code";
options.Prompt = "login";
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.RequireHttpsMetadata = false;
})
.AddJwtBearer(options =>
{
// some configuration
})
.AddPolicyScheme("MyPolicy", "My test Policy", options =>
{
options.ForwardDefaultSelector = context =>
{
// Decide which Authentication schema to use for each request
string authorization = context.Request.Headers[HeaderNames.Authorization];
if (authorization != null && authorization.StartsWith("Bearer", StringComparison.OrdinalIgnoreCase))
{
return BearerAuthenticationSchemeName;
}
if (context.Request.Path.StartsWithSegments("/swagger"))
{
return OpenIdConnectDefaults.AuthenticationScheme;
}
return CookieAuthenticationDefaults.AuthenticationScheme;
};
});
var app = builder.Build();
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.None,
Secure = CookieSecurePolicy.Always,
});
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI();
// more middleware
When I open my /swagger
endpoint, it redirects me to the login page. I manage to log in and it then redirects me to /signin-oidc
, which is expected. However, there is an exception thrown by the OpenIdConnectAuthenticationHandler
stating that message.State is null or empty
.
System.Exception: An error was encountered while handling the remote login.
---> System.Exception: OpenIdConnectAuthenticationHandler: message.State is null or empty.
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at MyApp.Middlewares.LoggingMiddleware.InvokeAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
When I run this locally (e.g on http://localhost:5000/swagger
), it works perfectly fine, but when I open it at https://myexampleurl.whatever/swagger/
, I get the exception. I have no idea why this does not work nor what I am doing wrong here. I see that the cookie is being sent in the format: .AspNetCore.OpenIdConnect.Nonce.<somehugestring>; .AspNetCore.Correlation.<correlation>; <anotherbigstring>
and it may be worth noting that the Authority
and my myexampleurl
are different domains.
It might also be worth noting that when I run this locally and when I get redirected to /signin-oidc
, the browser does send 4 cookies: 2 AspNetCore.Correlation
and 2 AspNetCore.OpenIdConnect.Nonce
cookies, all with different values. For the other one, it doesn't send any cookies at all.
First was my cookie policy setup. When setting the SameSite
property to None
in the app.UseCookiePolicy()
middleware, the cookie must be marked as secure (CookieSecurePolicy.Always
), otherwise the browser will not store or send it in its consequent requests, which would lead to an HTTP 500
in my case. However, even after introducing said changes, I was still having trouble. So, after digging deeper, I came across a forum post on Auth0 titled "ASP.NET Core incorrect redirect url" which internally linked / led to another post here on StackOverflow "AspNetCore Azure AD Connect Callback URL is http, not https". Ultimately, the answer to it suggested to add app.UseForwardedHeaders()
prior to invoking app.UseAuthentication()
in the application's middleware. After doing that (by basically following the same logic as the post answer did), everything started to work properly.
So, after everything has been changed, this is how it all looks in my Program.cs
:
builder.Services.AddAuthentication().AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.None;
}).AddOpenIdConnect(...).AddJwtBearer(...).AddPolicyScheme(...);
var app = builder.Build();
ForwardedHeadersOptions forwardedHeadersOptions = new()
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardedHeadersOptions.KnownNetworks.Clear();
forwardedHeadersOptions.KnownProxies.Clear();
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.None,
Secure = CookieSecurePolicy.Always,
});
app.UseForwardedHeaders(forwardedHeadersOptions);
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI();
// more middleware