OWIN overwriting all claims for user in basic OIDC config

James Baron 21 Reputation points
2022-12-20T05:56:11.603+00:00

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

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,137 questions
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,243 questions
ASP.NET API
ASP.NET API
ASP.NET: A set of technologies in the .NET Framework for building web applications and XML web services.API: A software intermediary that allows two applications to interact with each other.
293 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 54,866 Reputation points
    2022-12-22T19:39:06.723+00:00

    you are confusing the id token with the cookie ticket token.

    n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

    adds the claim to the claims in the ticket in the authentication cookie. it has no effect on the actual id token.

    unless you add token caching (to make api calls on the behalf of the user), the id token only exits for the the actual login callback request. the callback creates and set a cookie, which is used in subsequent requests to identify the user.

    1 person found this answer helpful.
    0 comments No comments