ASP.NET Core 3.1 using Microsoft.AspNetCore.Authentication.OpenIdConnect in an implicit flow
I am attempting to execute an OpenIDConnect implicit flow. It seems to work okay until the callback when I get the following error:
Exception: OpenIdConnectAuthenticationHandler: message.State is null or empty.
Now I suspect it's because my code can't get the State and other parameters because they are behind a hash, or URL fragment. In the browser location window I see https://localhost:44300/signin-oidc#id_token=eyJ0&State=etc. etc. (note the hash).
I understand that in implicit flows the tokens are placed behind the hash and that can be read with javascript like in Angular apps or whatnot. But I also thought response_mode=form_post would cause the authorization endpoint to POST to the callback. However, in my case I doesn't appear the authorization endpoint is honoring that or something is going wrong. Here is my F12 log:
Name Url
localhost localhost (me) GET 302
auth?client_id= authority GET 302
auth?client_id= web client auth GET 200
signonCallback authority POST 302
signin-oidc localhost(me) GET 500
In frustration I spun up IdentityServer4 locally to test implicit flows and it posts back. Not sure what's different here in the real world. There's probably a lot different but how can I cope using ASP.NET Core constructs and not resorting to some page that pulls the hash from the location bar with javascript?
Code:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = new PathString("/Authorization/AccessDenied");
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ResponseType = OpenIdConnectResponseType.IdToken; //id_token
options.ResponseMode = OpenIdConnectResponseMode.FormPost; //form_post
options.Authority = Configuration["MyApp:Authentication:Authority"];
options.GetClaimsFromUserInfoEndpoint = true;
options.ClientId = Configuration["MyApp:Authentication:ClientId"];
options.CallbackPath =
new PathString(Configuration["MyApp:Authentication:CallbackPath"]);
options.SignedOutCallbackPath =
new PathString(Configuration["MyApp:Authentication:SignedOutCallbackPath"]);
options.Scope.Clear();
options.Scope.Add("openid");
options.SaveTokens = true;
});