Search code examples
asp.net-mvc-4identityserver4identityserver3katananonce

Bookmarking login page with nonce


I'm trying to integrate an MVC4 web client with IdentityServer using Microsoft OWIN middleware OIDC authentication v4.0.0. When requesting an ID token from the authorize endpoint, a nonce must be supplied, and the login page served up has the nonce in the query string. If a user bookmarks this and uses it to log in the next day (for example), nonce validation in the client will fail because they'll no longer have that nonce stored, or it will have expired, etc.

This triggers the AuthenticationFailed notification in the client with this exception:

"IDX21323: RequireNonce is '[PII is hidden]'. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated."

At this point I could HandleResponse, redirect to an error page and so on. If they then try to access a protected resource again, the redirect to IdentityServer immediately returns an ID token due to the previous successful login (from its point of view I guess?) and this time the nonce validates and the user is logged into the client. But this is a rather strange experience for the user - their first attempt to log in appears to fail, they get an error, but then when they try again they don't even have to log in, they're just taken straight in.

An alternative would be to handle this type of exception in AuthenticationFailed by redirecting to the home protected resource so the happens 'seamlessly' in the background. To the user it appears as if their first login attempt worked. But I'm not sure if this is appropriate for genuine nonce validation issues. I'm also worried this may lead to redirect loops in some cases.

So to get to my question... what is the common approach to this issue of bookmarking login pages / nonces? Have I made a fundamental mistake or picked up a fundamental misunderstanding somewhere along the line which has allowed this scenario to occur?


Solution

  • Here is the code that needs to go into the call to

    UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
       AuthenticationType = "oidc",
       Authority = appSettings["ida:Authority"],
       ClientId = appSettings["ida:ClientId"],
       ClientSecret = appSettings["ida:ClientSecret"],
       PostLogoutRedirectUri = appSettings["ida:PostLogoutRedirectUri"],
       RedirectUri = appSettings["ida:RedirectUri"],
       RequireHttpsMetadata = false,
       ResponseType = "code id_token",
       Scope = appSettings["ida:Scope"],
       Notifications = new OpenIdConnectAuthenticationNotifications
       {
           AuthenticationFailed = authFailed =>
           {
               if (authFailed.Exception.Message.Contains("IDX21323"))
               {
                  authFailed.HandleResponse();
                  authFailed.OwinContext.Authentication.Challenge();
               }
    
               return Task.FromResult(true);
                        }
             },
          SignInAsAuthenticationType = "Cookies"
        }
    });