OWIN keeps overwriting all claims for the logged in OIDC user after about 5 minutes. I'm setting a claim called id_token
which holds the encoded JWT token so that I can send an IdTokenHint
for redirection from IdentityServer4 (but that issue also affects B2C). If you don't send an IdTokenHint
both IdentityServer4 and B2c and others will refuse to redirect you to prevent a redirection attack.
The setup seems correct. It redirects to the IdentitysServer4. I can see the claims. If I click the Logout button right away, the id_token
will still be set, and the IdTokenHint
works and IdentityServer4 redirects back to my application after sign out.
The problem comes if I wait for five minutes before clicking sign out. In that case, the only claim left is LOCAL AUTHORITY
which I never set. I think that's going to be the biggest clue to what's going on. I'm betting that triggers something in someone's mind right away. I think it's probably trying to get a refresh token after five minutes and then overwriting all claims. Maybe. I have put breakpoints wherever I can, stubbing all of the notification events to set breakpoints, and none are raised before I physically click the Logout button, and then id_token
is null (if I wait 5 minutes before click Logout).
On SecurityTokenValidated
in Startup.cs
I am setting the id_token
.
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
I created a Logout button and an OIDC controller. If before the 5 minutes, the first var instruction here, which does nothing, I can inspect and see the id_token
. After 5 minutes, id_token
is null. So I'm pretty sure this method itself isn't causing the issue.
public void SignOut()
{
var owin = HttpContext.GetOwinContext(); // here all the claims are gone except for LOCAL AUTHORITY
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
Here's where I really need it, but after waiting 5 minutes and then clicking the Logout button id_token
is null and LOCAL AUTHORITY
is set:
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var id_token_claim = n.OwinContext.Authentication.User.Claims.FirstOrDefault(x => x.Type == "id_token");
if (id_token_claim != null)
{
n.ProtocolMessage.IdTokenHint = id_token_claim.Value;
I created a minimal example and the code that I added is so small that this and the Logout button is the only changes I've made:
Startup.cs
Startup.Configuration
public void Configuration(IAppBuilder app)
{
Console.WriteLine(app.Properties);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
SaveTokens = true,
ClientId = "mvc",
Authority = "https://localhost:5001",
RedirectUri = "https://localhost:44307/",
PostLogoutRedirectUri = "https://localhost:44307/",
UsePkce = true,
RedeemCode = true,
ClientSecret = "####",
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "openid profile",
SignInAsAuthenticationType = "Cookies",
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name"
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
return Task.FromResult(0);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var id_token_claim = n.OwinContext.Authentication.User.Claims.FirstOrDefault(x => x.Type == "id_token");
if (id_token_claim != null)
{
n.ProtocolMessage.IdTokenHint = id_token_claim.Value;
}
}
return Task.FromResult(0);
}
}
});
}
Here's a tutorial of what I'm basically trying to do which is very similar to other questions on Stack and other documents that have got me this far.
https://nahidfa.com/posts/identityserver4-and-asp-.net-mvc