Closed tomburger closed 1 year ago
This is how our configuration is setup:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/login")
});
var authority = "https://login.microsoftonline.com/common/v2.0";
var redirectUri = "https://app.<our-domain>.net/.auth/login/aad/callback";
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ApplicationCookie);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
ClientId = clientId,
Authority = authority,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
RedirectUri = redirectUri,
PostLogoutRedirectUri = "http://www.<our-domain>.net",
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
SecurityTokenValidated = (context) =>
{
return Task.FromResult(0);
},
RedirectToIdentityProvider = (context) =>
{
context.OwinContext.Response.Redirect("/login?sso-logged-me-off");
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
// Pass in the context back to the app
var msg = context.Exception?.Message ?? "Unknown error";
context.OwinContext.Response.Redirect("/Account/SsoError?msg=" + HttpUtility.UrlEncode(msg));
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
}
}
});
}
OIDC can't interfere with sessions it does not own, it only runs when invoked.
Start with UseTokenLifetime = false to see if that helps: https://github.com/aspnet/AspNetKatana/blob/423f80e7f82708e3f78a596cf405a2a5595c12f1/src/Microsoft.Owin.Security.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs#L298-L302
@Tratcher thanks for the hint...
OIDC can't interfere with sessions it does not own, it only runs when invoked.
I know, it should not, but nevertheless it does. If I have the line app.UseOpenIdConnectAuthentication
in Startup.Auth.cs of my ASP.NET app, the session expires in 5 minutes, if I remove that single line, the session does not expire.
Start with UseTokenLifetime = false to see if that helps
Ok, I tried and it does not help. Any other idea?
How are you authenticating users and creating sessions when you don't have UseOpenIdConnectAuthentication?
We call UseCookieAuthentication
in Startup.Auth.cs
, see above, and when form with user and password is submitted, we call HttpContext.GetOwinContext().Authentication.SignIn()
. For login with OpenId, we do not call SignIn
, but Challenge
with redirect URL and we call SignIn
only after redirect comes back with the claim.
What are you passing to SignIn?
The code looks like this:
var identity = await App.Users.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
App.AuthManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, identity);
App.Users
is reference to Microsoft.AspNet.Identity.UserManager<T>
and App.AuthManager
is reference to HttpContext.GetOwinContext().Authentication
in controller, user
is model coming from binding of action, and it contains username and password.
Can out put a breakpoint in each OpenIdConnectAuthenticationNotifications event and see if any of them trigger on requests where you're doing local signin, or on the request that fails after some time?
Also, I see the cookie might be marked as IsPersistent? How long of an expiration does the client think it has?
The only notification called is RedirectToIdentityProvider
and when it is called, session is gone already (controller.User.Identity.IsAuthenticated
is false).
Here is the list of values in ProtocolMessage
(I have removed properties with null
):
{
"ClientId": "<our-client-id>",
"EnableTelemetryParameters": true,
"Nonce": "6380971290729...ZTY2LTg4OTQtMzAzNjY1YzAzNmE1",
"RedirectUri": "http://localhost:44301/.auth/login/aad/callback",
"RequestType": 0,
"ResponseMode": "form_post",
"ResponseType": "code id_token",
"Scope": "openid profile",
"SkuTelemetryValue": "ID_NET461",
"State": "OpenIdConnect.AuthenticationProperties=ktOYNghqNij4qdEiEdoj....XWVq-M0qHH8-bF2fno2Z7DQ",
"IssuerAddress": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
"Parameters": {
"client_id": "<our-client-id>",
"redirect_uri": "http://localhost:44301/.auth/login/aad/callback",
"response_type": "code id_token",
"scope": "openid profile",
"state": "OpenIdConnect.AuthenticationProperties=ktOYNghqNij4qdEiEdoj....XWVq-M0qHH8-bF2fno2Z7DQ",
"response_mode": "form_post",
"nonce": "63809712907296...zNjY1YzAzNmE1"
},
"PostTitle": "Working...",
"Script": "<script language=\"javascript\">window.setTimeout('document.forms[0].submit()', 0);</script>",
"ScriptButtonText": "Submit",
"ScriptDisabledText": "Script is disabled. Click Submit to continue."
}
From that I assume it is this call:
https://github.com/aspnet/AspNetKatana/blob/dbe159e43e2eee44f315f26268943e8ab5a4f60d/src/Microsoft.Owin.Security.OpenIdConnect/OpenidConnectAuthenticationHandler.cs#L103
Unfortunately stack trace only says RedirectToIdentityProvider
and then [External code]
.
Unfortunately stack trace only says
RedirectToIdentityProvider
and then[External code]
.
There's an option to right-click on the stack trace and check "Show External Code".
That message looks like it's coming from Challenge, not SignOut. https://github.com/aspnet/AspNetKatana/blob/dbe159e43e2eee44f315f26268943e8ab5a4f60d/src/Microsoft.Owin.Security.OpenIdConnect/OpenidConnectAuthenticationHandler.cs#L203
It's interesting that you're automatically challenging for remote auth. I'd have expected you to challenge for local auth.
The other thing to check is Fiddler, making sure the app cookie is included on all these requests.
You are right, it is coming from ApplyResponseChallengeAsync
. Here is a complete call stack (thanks for the hint):
...and yes, that request, which ends up in the RedirectToIdentityProvider
and continues then with redirect login page, that request receives cookie called .AspNet.ApplicationCookie
.
So which controller did that request go to, and why did that controller challenge for OIDC instead of app cookies?
So which controller did that request go to...
Just ordinary application controller, but it actually never reaches it. Controller is protected by [Authorize]
attribute, which diverts the flow into OIDC challenge, before reaching the controller code.
and why did that controller challenge for OIDC instead of app cookies?
...that's the whole point I guess, to find out why it is happening. ;-)
One observation I have made today is the following: the problem is not checking the cookie after that 5 minutes interval, but it must be somehow already encoded in that cookie, when it is generated. Why? When I comment out OIDC, run the server and login, then bring OIDC back in and restart the server, my session is still valid and it lasts longer than 5 minutes without problem. Only when I logout and login, the cookie, I get, lasts only 5 minutes.
Does that help?
Controller is protected by
[Authorize]
attribute, which diverts the flow into OIDC challenge, before reaching the controller code.
Ah. OIDC defaults to Active mode where it intercepts any 401 response (such as that from the Authorize attribute). You can turn that off. https://github.com/aspnet/AspNetKatana/blob/423f80e7f82708e3f78a596cf405a2a5595c12f1/src/Microsoft.Owin.Security.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs#L57
That doesn't explain why you're getting a 401 though. That would be an issue with the cookie itself, not the OIDC handler. Without the OIDC handler the 401 would be handled by CookieAuth. If that flow is automatic then you might be silently signed in without realizing it. You can hook up to the cookie auth provider events to be sure.
Turning authentication mode to passive helped with OIDC, intercepting 401. The session is still valid only 5 minutes, but now the redirect to login screen is not generated from OIDC.
But still, the initial problem stays: if we call UseOpenIdConnectAuthentication
, then authentication cookie is valid only 5 minutes, even if we logged in using username and password. If we do not call it, the authentication cookie holds longer. Can we somehow get this resolved? At best, getting UseOpenIdConnectAuthentication
to allow cookie valid for more than 5 minutes (at the end, even for people using AAD to login, we want session to last longer).
Also, should we still keep UseTokenLifetime
as false? Remember? That's what we started with.
At least something has worked. I still do not understand what would cause the cookie lifetime issue, OIDC isn't involved in reading or writing the cookie. The lifetime would have to be specified in the AuthenticationProperties when signing in and creating the cookie. UseTokenLifetime = true would explain this if the OIDC tokens were only valid for 5 min, but only when signing in with OIDC.
Hooking Cookie's OnResponseSignin will allow you to inspect the AuthenticationProperties and figure out if there is a timeout specified (and override it). The next trick would be figuring out who set it. https://github.com/aspnet/AspNetKatana/blob/423f80e7f82708e3f78a596cf405a2a5595c12f1/src/Microsoft.Owin.Security.Cookies/Provider/CookieAuthenticationProvider.cs#L22
Sorry for the late response, here is current status:
Startup.Auth.cs
, and we setup OpenIdConnect authentication first and Cookie authentication second - and then problem mentioned above went away, however then login via AAD did not work at all (redirect came back unauthenticated).In general, problem is not solved, but we have found an arrangement, where problem is not happening.
@Tratcher I leave it up to you - either we can close this issue as resolved, or if you want to investigate further, let me know, I can isolate the problem from our application codebase into standalone GitHub repo and we can investigate healthy coexistence of both authentication schemas until we find out what's wrong.
An isolated repro would help. You'd rather not maintain separate instances, right?
Hi @Tratcher ,
sorry for the delay, but I have now isolated repro for you on following repository: https://github.com/tomburger/aspnet-auth-sample
Just clone it, open solution in src
folder and run it in Visual Studio. It has a login screen and there are four users available, ringo@beat.les, john@.., paul@.. and george@.., password "LetItBe" for all four of them. After login there are two pages and you can navigate between them with button. If you wait for 5 minutes and then you click the button, you will be logged out and redirected back to login screen.
If you go to file src\App_Start\Startup.Auth.cs
and comment out OIDC part (lines 25-79), then logout after 5 minutes is not happening.
Please, notice that you are still using cookie authentication, the difference is only the call to UseOpenIdConnectAuthentication
. If you call it, your session is terminated after 5 minutes, if you do not call it, it will stay valid much longer.
Feel free to ask more questions, or send me the pull request, if you know how to fix it. We can keep the repo then as a reference for generations to come ;-)
This took me way longer to figure out than it should have 😆.
The culprit is app.UseExternalSignInCookie(DefaultAuthenticationTypes.ApplicationCookie);
https://github.com/aspnet/AspNetIdentity/blob/b7826741279450c58b230ece98bd04b4815beabf/src/Microsoft.AspNet.Identity.Owin/Extensions/AppBuilderExtensions.cs#L119-L124
UseExternalSignInCookie doesn't just set a field, it creates another Cookie Auth instance with a 5min timeout. You'd passed the same name to that one as the other cookie auth instance so it was ambiguous.
The API you want instead is SetDefaultSignInAsAuthenticationType
Sorry, @Tratcher, for late reply. I have fixed the sample and I can confirm it works. Thanks a lot for your help.
We have ASP.NET app hosted in Azure, using
CookieAuthentication
. When user logs in, session stays valid (possibly hours or days, we haven't measured exactly).When we add
OpenIdConnectAuthentication
, the session gets terminated after 5 minutes of inactivity.It is not necessary to use that OpenIdConnect to login. Even if user has used cookie based login, after 5 minutes of inactivity, the next request gets redirected to
RedirectToIdentityProvider
notification handler of OpenIdConnect, but at that time, the user session is already gone.ASP.NET is using version 4.8, Owin libraries have version 4.2.2.
Is there any settings, which would make that session to last longer? Or settings, where OpenIdConnect would not interfere at all with sessions, it does not own?