简体   繁体   中英

Authentication Cookie Timeout when using OpenId Connect in ASP.NET Web Forms

As a matter of learning and getting up to speed with OpenID Connect I am attempting to add authentication and authorization to a legacy Web Forms application using Azure AD as the auth server.

I have been stuck on one aspect for days and cannot make any progress. I would like to do something I believe should be simple. Unlike most, I want the application to timeout and redirect the user back to the Azure login. Ideally I would like to control this timeout from within the application configuration itself. Everyone seems to be asking how not to be directed to login upon expiration but I want to do it for learning purposes and to just know that I can have control over it.

My "problem" is that when the authentication cookie expires (or is deleted from the browser) my Request.IsAuthenticated check never fails and the tokens are just refreshed. I can never get it to force a login. I feel like I have tried everything and can not find any sort of expiration that means anything. I don't even know how the tokens are getting refreshed.

I have placed this code in a page base class. I would expect that at some point the authentication cookie would expire and this code logic would force a challenge like it does when I first fire up the application. I have tried various things here. It may cause a shorter time before the tokens are refreshed but it never just expires.

private void Page_PreInit(object sender, EventArgs e)
    {

        if (!Request.IsAuthenticated)
        {
            HttpContext.Current.GetOwinContext().Authentication.Challenge(
                 new AuthenticationProperties
                 {
                     RedirectUri = "/",
                     IsPersistent = true,
                     //ExpiresUtc = DateTime.UtcNow.AddMinutes(1)
                 },
                 OpenIdConnectAuthenticationDefaults.AuthenticationType); ;
            Response.End();
        }

    }

And the startup configuration:

  public partial class Startup
{

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookies",
            CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager(),
            
            //ExpireTimeSpan = new TimeSpan(0, 1, 0),
            SlidingExpiration = false,

            //Provider = new CookieAuthenticationProvider
            //{
            //    OnResponseSignIn = context =>
            //    {
            //        context.Properties.AllowRefresh = false;
            //        context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1);
            //    },

            //}

        });

        app.UseOpenIdConnectAuthentication(
        new OpenIdConnectAuthenticationOptions
        {
            ClientId = AuthenticationConfig.ClientId,
            ClientSecret = AuthenticationConfig.ClientSecret,
            Authority = AuthenticationConfig.Authority,
            RedirectUri = AuthenticationConfig.RedirectUri,
            PostLogoutRedirectUri = AuthenticationConfig.PostLogoutRedirectUri,

            Scope = AuthenticationConfig.BasicSignInScopes + ' ' +
                     AuthenticationConfig.APIResourceUri + "access_as_user",


            SignInAsAuthenticationType = "cookie",

            RequireHttpsMetadata = false,
            UseTokenLifetime = true, // Needed to override default and allow custom auth cookie timout

            RedeemCode = true,
            SaveTokens = true,

            ResponseType = OpenIdConnectResponseType.Code,
            ResponseMode = "query",

            // ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
            // To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
            // To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
            TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = true, // This is a simplification
                NameClaimType = AuthenticationConfig.NameClaimType,
                RoleClaimType = AuthenticationConfig.RoleClaimType
            },


            Notifications = new OpenIdConnectAuthenticationNotifications()
            {
                SecurityTokenValidated = Startup.SecurityTokenValidated
            }

        });

        // This makes any middleware defined above this line run before the Authorization rule is applied in web.config
        app.UseStageMarker(PipelineStage.Authenticate);
    }

    public static Task SecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        var identity = notification.AuthenticationTicket.Identity;

        identity.AddClaim(claim: new Claim(type: "expires_at", value: notification.ProtocolMessage.ExpiresIn));
        identity.AddClaim(claim: new Claim(type: "id_token", value: notification.ProtocolMessage.IdToken));
        identity.AddClaim(claim: new Claim(type: "access_token", value: notification.ProtocolMessage.AccessToken));
        identity.AddClaim(claim: new Claim(type: "refresh_token", value: notification.ProtocolMessage.RefreshToken));

        return Task.CompletedTask;
    }

    private static string EnsureTrailingSlash(string value)
    {
        if (value == null)
        {
            value = string.Empty;
        }

        if (!value.EndsWith("/", StringComparison.Ordinal))
        {
            return value + "/";
        }

        return value;
    }
}

I would love to know how to make authentication expire that forces a login redirect. My only guess here is that the middlewhere is acting before the pre init code runs. If there is something to that then where do might I make such a check?

I'm not sure if you ever solved this problem, but here is our configuration that automatically redirects the user when they are not authenticated.

    public void ConfigureAuth(IAppBuilder app)
    {
        var secrets = DIFactory.GetInstance<IApplicationSecrets>();
        
        var clientId = secrets.Ida_ClientId().Result;
        var aadInstance = secrets.Ida_AADInstance().Result;
        var tenantId = secrets.Ida_TenantId().Result;

        var authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenantId);
        
        /* Set authentication type to "Cookies" */
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
        
        /* Add a cookie-based authentication middleware to the OWIN pipeline */
        app.UseCookieAuthentication(
            new CookieAuthenticationOptions
            {
                CookieName = "RemoteAuthCookie",
                AuthenticationType = "Cookies",
                CookieSecure = CookieSecureOption.Always,
                ExpireTimeSpan = TimeSpan.FromMinutes(2.0),
                SlidingExpiration = false,
                CookieSameSite = SameSiteMode.None,
                CookieManager = new SystemWebCookieManager(), // new SameSiteCookieManager(new SystemWebCookieManager()),
            });

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = authority,
                Scope = "openid profile",
                
                //CookieManager = new SameSiteCookieManager(new SystemWebCookieManager()),

                    // ResponseType is set to request the id_token - which contains basic information about the signed-in user
                ResponseType = OpenIdConnectResponseTypes.IdToken,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = (context) =>
                    {
                        // This ensures that the address used for sign in and sign out is picked up dynamically from the request
                        var currentUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.Path;
                        context.ProtocolMessage.RedirectUri = currentUrl;
                        return Task.FromResult(0);
                    }
                }
            });
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM