Correlation failed when logging in for the second time after hitting the back button on the browser - oauth-2.0

Ok, first question ever so be gentle. I've been struggling with this for about a week now and i am finally ready to accept defeat and ask for help.
Here's what is happening. I have an IdentityServer4 IDP, an API and an MVC Core Client. I am also interacting with 2 external OAuth2 IDPs provided by the business client.
My problem scenario is this:
The user logs in through my IDP(or potentially one of the external ones)
Once the user is in the Mvc Client they hit the back button on their browser
Which takes them back to the login page(whichever one they used)
They reenter the credentials(login again)
When redirected back(either to the MVC in the case of my IDP, or my IDP in the case of one of the external IDPs) i get RemoteFailure event with the message:correlation failed error
The problem seems, to me, to be the fact that you are trying to login when you are already logged in(or something). I've managed to deal with the case of logging in at my IDP, since the back button step takes the user to my Login action on the Controller(I then check if a user is authenticated and send them back to the MVC without showing them any page) but with the other two IDPs the back button does not hit any code in my IDP. Here are the config options for one of the OAuth2 external IDPs:
.AddOAuth(AppSettings.ExternalProvidersSettings.LoginProviderName, ExternalProviders.LoginLabel, o =>
{
o.ClientId = "clientId";
o.ClientSecret = "clientSecret";
o.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
o.CallbackPath = PathString.FromUriComponent(AppSettings.ExternalProvidersSettings.LoginCallbackPath);
o.AuthorizationEndpoint = AppSettings.ExternalProvidersSettings.LoginAuthorizationEndpoint;
o.TokenEndpoint = AppSettings.ExternalProvidersSettings.LoginTokenEndpoint;
o.Scope.Add("openid");
o.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
//stuff
},
OnRemoteFailure = async context =>
{
if (!HostingEnvironment.IsDevelopment())
{
context.Response.Redirect($"/home/error");
context.HandleResponse();
}
}
};
}
The other one is the same. Since the error is exactly the same regardless of the IDP used, i am guessing it is not something native to OIDC but to OAuth middleware and the code(config options) they share, so i am not going to show the OIDC config on the MVC client(unless you insist). Given how simple the repro steps are i thought i would find an answer and explanation to my problem pretty fast, but i was not able to. Maybe the fix is trivial and i am just blind. Regardless of the situation, i would apreciate help.

I could reproduce your issue.
When the user goes back to the login screen after successfully logging in,
it might well be that the query parameters in the URL of that page are no longer valid.
Don't think this is an issue specific to Identity Server.
You may read
https://github.com/IdentityServer/IdentityServer4/issues/1251
https://github.com/IdentityServer/IdentityServer4/issues/720
Not sure how to prevent this from happening though.

Related

Allauth user_logged_in signal does not trigger in a TestCase

I'm using allauth for user management in my Django web app and I have functionality which finds session data stored when the user was not logged in and stores it when they next log in on the same session.
It is triggered on login via the standard allauth signal:
#receiver(user_logged_in)
def update_on_login(request, user, **kwargs):
print('Signal triggered')
# Do some stuff with session data here, e.g.:
for session_key in request.session.items():
...
This works perfectly when logging in via a web browser, but in TestCase functions the signal is not triggered at all when logging in with:
self.client = Client()
logged_in = self.client.login(username=self.username, password=self.password)
(I've also tried force_login and force_authenticate, but info on these indicates that they actually skip all real validation and just assume the user is logged in).
I think I've understood that client.login doesn't work because the test client doesn't really handle the request in the same way as a web browser would, but I can't say I really understand it.
I've also seen some indication that RequestFactory might be able to help in someway, but I haven't been able to trigger the signal with this either:
request = RequestFactory().post(reverse('account_login'), { 'username': self.username, 'password': self.password })
request.user = AnonymousUser()
response = login(request)
I've seen some comments about needing to call middleware as well, but pretty out of date and nothing clear enough for me to understand and try.
Any suggestions to point me in the right direction would be much appreciated.
Thanks in advance.

Getting sometimes double POST request to IIS 10 webserver

Since we migrated to an Amazon EC instance we're getting sometimes double POST requests to our SaaS application. I have no idea where this is coming from and why this is happening. I've been searching and looking at different options, but can't find the root cause. We migrated from IIS7 to IIS10 on a Windows Server 2022 Datacenter.
Here is an example of an SEQ logging session at 1 client:
SEQ Log
You can see the endpoint was requested multiple times (= OK), but there is also a double POST request at 18:19:41 and 18:18:27. This is logged from within the ASP.NET MVC controller. If I look at the IIS 10 logs, I see the same thing. So the request seems to be initiated from the browser and not doubled in the pipeline.
The MVC controller looks something like this (simplified):
if (ViewData.ModelState.IsValid){
try
{
NHibernateSession.Current.Transaction.Begin();
foreach (var item in deliveryDTosToSave){
var delivery = new Delivery
{
//copying over the DTO to the delivery
};
_deliveryRepository.SaveNew(delivery, userCode, item.Index);
}
NHibernateSession.Current.Transaction.Commit();
return RedirectToAction("Add", "Delivery", new { tab, pharmacyId });
}
catch (RuleException ex)
{
NHibernateSession.Current.Transaction.Rollback();
}
}
On the client-side, the javascript to submit is something like this:
$('#formDeliveries').submit(function () {
if (!canSubmit) {return false;}
$("#loading").show();
canSubmit = false;
});
Things I looked for:
user double-clicking on the submit button: we've blocked this through JS after initial submit. I interviewed several users, and they are not double-clicking.
bug in browser: last user I logged in used Edge 107, which is recent. I can't find anything on the matter
HTTPS redirects: the website is HTTPS-only and users have to be authenticated to use it.
HTTP/3 fall-back scenario's: could be possible as we've enabled HTTP/3 when migrating to Amazon. However users are seeing this behaviour only sometimes and not always from the same browser.
We've tried to simulate the behaviour, but cannot find it. We've logged into the users computer and tried to simulated it while looking at the network requests through the developers utils, but cannot simulate it. I was hoping to see it happening live, so we can rule out any javascript items. If the network log in developer tools shows only 1 POST, it probably has to do something with the IIS pipeline.
Any advice would be greatly appreciated.

How to not make the user re-login every time browser closes?

This might be a basic/dumb question, but I don't know the right keyword to Google. What I want is that when I authenticate the user to my web app, they can close the browser, and when they open it back, they can still use my website - they are not logged out (yet).
I have been following the tutorials in IdentityServer docs (https://identityserver4.readthedocs.io/en/latest/quickstarts/2_interactive_aspnetcore.html), and so far I have managed to get the whole IDP-API-Client working. I have inspect the token that I get from IDP, it's valid for 2 weeks, so what am I missing here, why do I get logged out when I close the browser?
My guess is that I need to store the token to the cookie, but how do I save it, and how do I force the web application to always check for the cookie?
The IS4 tutorial has this:
services.AddAuthentication(o =>
{
o.DefaultScheme = "Cookies";
o.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", o =>
{
o.Authority = "https://localhost:5001";
o.ClientId = "mymvcclient";
o.ClientSecret = "mymvcclientsecret";
o.ResponseType = "code";
o.SaveTokens = true;
o.Scope.Add("myapi");
o.Scope.Add("offline_access");
});
I assume that's just creating a cookie, but how do I specify for it to save my token, and read the token from the cookie when user opens my web application?
Yea there is an option called ExpireTimeSpan in your cookie handler, which defaults to 14 days. You can change it to anytime longer than that by just setting a value to it:
...
.AddCookie("Cookies", options => {
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = false;
})
.AddOpenIdConnect("oidc", options => {
...
options.UseTokenLifetime = false;
...
})...
The above sets the cookie expiration to 30 days, instead of the default 2 weeks.
You also want to make sure the UseTokenLifetime option from the OIDC is set to false, which is the default for now I think. Tokens coming back from the Identity Server, for example, tend to have short lives. If you set it to true, which was default before, then it would override whatever expiration you set earlier.
The best term to look for on this is probably "persistent sessions", or just session management in general. This is something handled by ASP.NET Core, not really IdentityServer. There are several mechanisms to maintain session state on the server, which you'd need for example not to lose all sessions when a server restart happens.
I've had the best luck using the ITicketStore interface, which allows persisting the sessions to a database. The cookie ends up with a session ID which is validated on each request for expiration.

Kentor MVC Logout don't call logout url

I've an issue on this feature.
SignIn action works well with ADFS and return to AuthServices/Acs
But Logout action don't call ADFS and redirect directly to returnUrl parameters (checked it with fiddler).
I'm calling this link : /AuthServices/Logout?ReturnUrl=~/&Status=LoggedOut
web.config is set up as this :
<kentor.authServices entityId="https://localhost:2181/AuthServices" returnUrl="https://localhost:2181/">
<identityProviders>
<add
entityId="https://ADFS DOMAIN/adfs/services/trust"
signOnUrl="https://ADFS DOMAIN/adfs/ls"
logoutUrl="https://ADFS DOMAIN/adfs/ls/?wa=wsignout1.0"
binding="HttpPost"
allowUnsolicitedAuthnResponse="true"
metadataLocation="https://ADFS DOMAIN/FederationMetadata/2007-06/FederationMetadata.xml"
wantAuthnRequestsSigned="true">
<signingCertificate fileName="~/App_Data/*****.cer" />
</add>
</identityProviders>
</kentor.authServices>
If I launch https://ADFS DOMAIN/adfs/ls/?wa=wsignout1.0 on another tabs, it is working, I return on signin page from my website.
So it seems to be an internal issue to retrieve logouturl and send it ?
Thanks for helps.
There are a number of requirements that need to be met before logout request will be issued:
You need to have a http://kentor.se/AuthServices/LogoutNameIdentifier claim and its issuer has to match the IDP that you're trying to logout from.
You need to have http://kentor.se/AuthServices/SessionIndex claim.
Your AuthServices IDP configuration needs a logoutUrl (I see you've specified this but probably it's easier to let AuthServices read it from the metadata)
You have specified a ServiceCertificate with either Signing or Both usage (i.e. not just Encryption)
Your AuthServices IDP configuration has DisableOutboundLogoutRequests =
false (this is the default)
Missing claims (first two points) is the most likely issue if you have some claims transformation happening during login or you are not retaining the original ClaimsIdentity. See also the documentation regarding ClaimsAuthenticationManager, e.g. https://github.com/KentorIT/authservices/blob/master/doc/ClaimsAuthenticationManager.md
You can turn on logging and see which of these points are failing:
https://github.com/KentorIT/authservices/blob/v0.21.2/Kentor.AuthServices/WebSSO/LogOutCommand.cs#L155-L170

CI 2.0.3 session heisenbug: session is lost after some time 20 minutes, only on server redirect, nothing suspicious in the logs

I can't seem to make any progress with this one. My CI session settings are these:
$config['sess_cookie_name'] = 'ci_session';
$config['sess_expiration'] = 0;
$config['sess_expire_on_close'] = FALSE;
$config['sess_encrypt_cookie'] = FALSE;
$config['sess_use_database'] = TRUE;
$config['sess_table_name'] = 'ci_sessions';
$config['sess_match_ip'] = FALSE;
$config['sess_match_useragent'] = FALSE;
$config['sess_time_to_update'] = 7200;
$config['cookie_prefix'] = "";
$config['cookie_domain'] = "";
$config['cookie_path'] = "/";
$config['cookie_secure'] = FALSE;
The session library is loaded on autoload. I've commented the sess_update function to prevent an AJAX bug that I've found about reading the CI forum.
The ci_sessions table in the database has collation utf8_general_ci (there was a bug that lost the session after every redirect() call and it was linked to the fact that the collation was latin1_swedish_ci by default).
It always breaks after a user of my admin section tries to add a long article and clicks the save button. The save action looks like this:
function save($id = 0){
if($this->my_model->save_article($id)){
$this->session->set_flashdata('message', 'success!');
redirect('admin/article_listing');
}else{
$this->session->set_flashdata('message', 'errors encountered');
redirect('admin/article_add');
}
}
If you spend more than 20minutes and click save, the article will be added but on redirect the user will be logged out.
I've also enabled logging and sometimes when the error occurs i get the message The session cookie data did not match what was expected. This could be a possible hacking attempt. but only half of the time. The other half I get nothing: a message that I've placed at the end of the Session constructor is displayed and nothing else. In all the cases if I look at the cookie stored in my browser, after the error the cookie's first part doesn't match the hash.
Also, although I know Codeigniter doesn't use native sessions, I've set session.gc_maxlifetime to 86400.
Another thing to mention is that I'm unable to reproduce the error on my computer but on all the other computers I've tested this bug appears by the same pattern as mentioned above.
If you have any ideas on what to do next, I'd greatly appreciate them. Changing to a new version or using a native session class (the old one was for CI 1.7, will it still work?) are also options I'm willing to consider.
Edit : I've run a diff between the Session class in CI 2.0.3 and the latest CI Session class and they're the same.
Here's how I solved it: the standards say that a browser shouldn't allow redirects after a POST request. CI's redirect() method is sending a 302 redirect by default. The logical way would be to send a 307 redirect, which solved my problem but has the caveat of showing a confirm dialog about the redirect. Other options are a 301 (meaning moved permanently) redirect or, the solution I've chosen, a javascript redirect.

Resources