aspnet / AspNetKatana

Microsoft's OWIN implementation, the Katana project
Apache License 2.0
967 stars 333 forks source link

OpenIdConnect Sliding Expiration issue #480

Closed Exagram closed 10 months ago

Exagram commented 1 year ago

Goal: Add sliding expiration to my ASP.NET MVC web app (legacy) using OpenIdConnect connected to Azure B2C custom profile.

App Code:

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    CookieManager = new SystemWebCookieManager(),
    ExpireTimeSpan = TimeSpan.FromMinutes(30),
    SlidingExpiration = true
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
    ClientId = clientId,
    ClientSecret = clientSecret,
    Authority = $"{aadInstance}{tenantId}",
    Scope = scope,
    RedirectUri = redirectUri
});

App Web.config:

...
<sessionState timeout="30" ... />
...

Azure B2C Code:

<UserJourneyBehaviors>
  <SingleSignOn Scope="Tenant" />
  <SessionExpiryType>Rolling</SessionExpiryType>
  <SessionExpiryInSeconds>1800</SessionExpiryInSeconds>
</UserJourneyBehaviors>

Observations:

Understanding:

Questions:

Tratcher commented 1 year ago

Try setting UseTokenLifetime to false. The OAuth tokens themselves often have a short lifetime even if the session is still valid. https://github.com/aspnet/AspNetKatana/blob/77497960c0adafcf2adaed1dc008f020595b28ab/src/Microsoft.Owin.Security.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs#L298-L303

How does CookieAuthentication and OpenIdConnectAuthentication work under the hood? Any resources?

That's a much longer discussion 😁. Did you have a more specific question?

Exagram commented 1 year ago

You mean to tell me UseTokenLifetime=true (default) overwrites my Web.config sessionState.timeout=1800 hard-coded value with the one provided by Azure? Why would Microsoft set this boolean to true as default if the lifetime is very short?

A random blogger had a similar situation and discovered UseTokenLifetime=false didn't help out:

His solution was to keep UseTokenLifetime = true and overwrite some AuthenticationTicket properties:

// Startup.cs, on SecurityTokenValidated

// By default, OWIN copies the identity token lifetime onto the authentication ticket (UseTokenLifetime = true).
// https://docs.microsoft.com/en-us/previous-versions/aspnet/mt180971(v%3Dvs.113)
// IdentityServer4's default identity token lifetime is 5 minutes without refresh.
// https://docs.identityserver.io/en/dev/reference/client.html#token
// We want to have instead: 30 minutes plus refresh.
var sessionSection = (SessionStateSection) WebConfigurationManager.GetSection("system.web/sessionState");
context.AuthenticationTicket.Properties.ExpiresUtc = DateTimeOffset.UtcNow + sessionSection.Timeout;
context.AuthenticationTicket.Properties.AllowRefresh = true;

My colleague mentioned that the last line AllowRefresh = true fixed the issue for us because it was mysteriously set to AllowRefresh = false.

Note: I would like to gain a deeper knowledge on how these systems work rather than just getting it right:

Tratcher commented 1 year ago

Web.config's sessionState has no effect on Owin auth. Browser session and user session are distinct features.

Why would Microsoft set this boolean to true as default if the lifetime is very short?

For 'security', but after similar feedback we changed the default in the next version (AspNetCore).

14 days is the default for cookie auth, controlled by CookieAuthenticationOptions.ExpireTimeSpan. You're already updating that to 30m.