Session resume problem with Strophe attach and Ejabberd - javascript

I'm having a lot of difficulty getting strophe's 'attach()' function working.
I am working on a social network where users will be surfing pages and at the same time keep their chat connection on. I don't want to reconnect/reauthorize on every page so as per this link, http://groups.google.com/group/strophe/browse_thread/thread/430da5e788278f3a/93c48c88164f382f?show_docid=93c48c88164f382f&fwc=1, i am storing the SID and RID into a cookie onunload.
On the next page when i try to use the new SID and RID (after incrementing it by 1) my session is already destroyed. Ejabberd reports "Error on HTTP put. Reason: bad_key"
WTF is happening?

Without more logging information from ejabberd or Firebug, this will be hard to diagnose. Is the correct RID actually stored in the cookie? What does ejabberd think is the last stanza you sent, and what RID value does it have? What is the first stanza and the first RID value on the new page? How long between those two stanzas?
Updated: The reason the session gets canceled is due to the security model of BOSH. Effectively, the SID and RID pair are secret. If you know the pair, you can attach to the session. In order to keep people form being able to guess, the RID is picked randomly from a very large space. If you send an RID outside a very small window from the current RID, it will disconnect the session. The window is usually (RID, RID + 5) or so.

I managed to solve the problem.
The rid was being double incremented between the page loads. By incrementing it only once the session attach started working.
I don't know why the session got cancelled, but here's what happened :
Because the RID is +2 more than the previous ones, ejabberd stores the request in buffer and does not forward it to clients
The next 2 requests also get stored
The one after that causes ejabberd to cancel the session
Any clues?

Related

How can I safely implement idle timeout when using identityserver4 and aspnet identity?

I'm working on an identityserver4 login site (server UI) with .NET Identity in .NET Core 2.2 Razor Pages, I have a javascript modal alert that warns users of a pending idle timeout, and then when reaching timeout it redirects the user to the logout screen by setting window.location
The trouble I have is that the OnGet in the quick start sample shows a user prompt to log out as at this point logoutId is null. I want to log out without prompting the user.
For the time being I have worked around this by passing an "autoLogout" parameter to my Logout page which bypasses the check for logoutId and sets ShowLogoutPrompt = false. I'm aware that this somewhat defeats the purpose of checking for logoutId to ensure that it is safe to sign-out without prompt.
Is there a better way to do what I'm trying to do?
Edit 16 Jul 2019:
It seems as though the "right" way to handle idle timeout is to set the application cookie's token expiry (to say 20 minutes) and enable SlidingExpiration so that the token is renewed when the user refreshes. For good info on this see this blog post, this github issue thread (including comments from Brock Allen), and this info in the MS docs.
My trouble is that this solution has two huge drawbacks.
SlidingExpiration only refreshes the cookie if the user is >50% through the token's TimeSpan (see SlidingExpiration info in MS docs here). So if they refresh 9m59s into a 20 minute token they will timeout after just 10 minutes instead of 20. One workaround would be to set the token lifetime to 40 minutes, which would give the user at least 20 minutes of idle time, but they could have up to 40 minutes of idle time which is not acceptable.
One of my requirements is a modal to warn the user of an impending timeout and give them the option to continue/log out. To do this using this cookie approach I would need to read the token expiry time from the cookie in my Javascript (or at least in my Razor Page in C#) to enable me to time when to show the warning. Even without the modal requirement I'd need to know when the token has expired so that I could cause a page refresh to send the user to the login screen. I'm attempting to read the expiry time using the following code but it fails to read the correct expiry time after a token refresh until the page is refreshed a second time, I don't know why.
#(DateTime.Parse(((await Context.AuthenticateAsync()).Properties.Items)[".expires"]))
Another less significant drawback to the cookies approach is that if I manage to implement a modal popup and the user opts to continue, then the page will need a refresh to get a new token, at which point any unsaved data would be lost. I guess if they time out then unsaved data would be lost anyway though so this is a relatively minor point compared with the above.
I'm thinking of going back to my original solution which has the desired functionality but would be open to abuse by an attacker who noticed my autoLogout parameter in the idle timeout javascript and could then use it to provide a hotlink to the logout page. At the moment taking that risk feels like my best option.
I feel like I've been down a rabbit hole on this one and still have no good solution. It amazes me that what I imagine to be a common use case (idle timeout with a warning allowing the user to continue/log out) is so poorly catered for with this authentication technology. Am I missing something? Do I have the wrong end of the stick?
I'm posting my final solution here. It works but I don't like it much. For references, details on why I think it's a bit hacky, and of what I think the main drawbacks are see the 16th Jul edit to my original question above.
In ConfigureServices after adding identityserver I set the cookie's SlidingExpiration = true; ExpireTimeSpan = AppSettings.IdleTimeoutMins (see this blog for how I set up AppSettings):
// Rename the .AspNetCore.Identity.Application cookie and set up for idle timeout
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "xxxx.Application";
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(_config.GetValue<int>("AppSettings:" + nameof(AppSettings.IdleTimeoutMins)));
});
I have a Partial Razor Page in which I have javascript code to display a modal alert to the user with a count down timer. I get the timeoutSeconds from AppSettings.IdleTimeoutMins and also have a setting to determine when to show the warning. For more detail on this bit (and its pros and cons) see my other question and answer here: How to get ASP.NET Identity authentication ticket expiry in Razor Page? The warning message gives the user the option to "Continue" which refreshes the page (and therefore the authentication ticket) or "Log Out", which sends them to the Log Out confirmation page. If the clock runs down then the page is refreshed, which causes them to be returned to the Log In screen.
At the top of the Partial:
#inject RussellLogin.Services.IAppSettings AppSettings;
#using Microsoft.AspNetCore.Authentication;
Getting the (assumed) number of seconds remaining on the ticket:
secondsRemaining = "#(DateTime.Parse(((await AuthenticationHttpContextExtensions
.AuthenticateAsync(Context))
.Properties
.Items)[".expires"])
.Subtract(DateTime.Now)
.TotalSeconds)";
// If secondsRemaining is less than half the expiry timespan then assume it will be re-issued
if (secondsRemaining < timeoutSeconds / 2) {
secondsRemaining = timeoutSeconds;
}

How to get the refreshed token in a Linnworks embedded app?

I thought I'd try posting my problem here given the non-existent support that Linnworks provide.
I've created a private embedded app within Linnworks that displays orders in a spreadsheet format. The app is built with Vue.js and uses axios to pull the data from Linnworks APIs. Everything is working as it should be here, except that I'm only returning 100 orders at a time to keep things quick.
I've added a "load more orders" button which appends an additional 100 orders to the end of the sheet, but after a period of inactivity, this causes a "401 unauthorised error" because the token has expired.
Because it's an embedded app, Linnworks store the token within the src of the iframe when the app is initialised, so when it has expired, it doesn't get physically refreshed by the system.
<iframe src="https://example.com/sheet.html?token=9b11e8ff-4791-aca5-b58d-f6da84e996a6"></iframe>
Is there a way of getting the refreshed token without reloading the entire app?
Tokens have a default TTL of 30 minutes, just poll the API with a simple method like /api/Main/Ping to keep your token/session active
I got the following response from Linnworks, which fixed the problem:
After further investigation, this appears to be due to the the pinging of the AuthorizeByApplication call. To help reduce the risk of being returned a 401 Unauthorised "Token has expired. please re-verify the user", it is recommended that when the application is opened, call AuthorizeByApplication and save the response.
Once the session has been created, AuthorizeByApplication should not have to be called again. The token returned in this session has a TTL of 30 minutes. If this token is used in a further call, the TTL of the token is reset back to 30 minutes. Therefore, as suggested in the response of your Stackoverflow question, briefly calling "api/Main/Ping" will reset the 30 minute TTL with little impact on your applications performance.
To Prevent Applications From Using Expired Tokens:
Upon launching application, call AuthorizeByApplication and save session response.
To keep the session from ending, call "api/Main/Ping" using the saved session token to reset the TTL of the saved session.
For any calls made by the application, use the original saved session token.

how to detect authentication timeout from client script

I have an ASP.NET application that is using forms authentication with a timeout set to five minutes. On my page I have a button, that when clicked, makes an AJAX call to an operation that lives on the service named in my .svc file. How do I know, from the client javascipt that the application has timed out? Or, how can I detect this in the global.asax; maybe in the application_beginrequest?
If you're talking about the session timeout. When this occurs, the IHttpSessionState.IsNewSession property should be set to true.
If you're referring to the auth timeout, then you have to check the AuthenticationTicket for expiration.
A variation on your approach: have a separate client script that first checks for authentication expiration by requesting a special page/handler which returns JSON structure to indicate the authentication status of the current user. Only after knowing that the user is still active do you then run your main ajax action. It's one more request but keeps you from entangling the timeout logic with the main ajax logic. You can also use separately in a "warning, your session will time out in x minutes" popup.
See this question and my answer there for more details about how to set it up, but the key point is that if you don't want the check for expiration to extend the sliding expiration you have to configure the expiration page/handler as a separate virtual directory with forms authentication set with slidingExpiration=false.

Reliability of ASP Session Timeout warning

I'm looking to warn the user when his or her session times out (I'm having weird timeout problems) and I found the following code:
<%
advanceWarning = 2
jsTimeout = (session.timeout - advanceWarning) * 60000
%>
<script>
window.setTimeout("alert('Session is about to expire');",<%=jsTimeout%>);
</script>
Is this reliable?
Well, it will catch the case where the session times out on the server, assuming no other requests used session in the mean time, and the cookie doesn't expire.
So if you call a web service method or something that uses session, the session time out will be reset and you will have to catch that separately. And if your session cookie expiry (assuming you're doing it that way) is less than your timeout, then session may be lost then too.
In classic ASP, the Session is managed using cookies. You can see more documentation on that here: http://w3schools.com/asp/asp_ref_session.asp. I used to use that site a lot back in the day. If you want more control over the user's session state, you can access the cookies directly (via Request.Cookies and Response.Cookies). http://w3schools.com/asp/asp_ref_response.asp. This may be a better solution in some situations, depending on how much content you are trying to store and how long you want it to persist. If you don't want the session to be refreshed as a 'sliding window', you can always set a timestamp variable in either the session or the cookies collections and gain more control over the timeout that way. One advantage to using the cookies directly is that you can access cookies directly with javascript and can avoid the spaghetti code situation. As I don't know your end goal, I can't say for sure if this will be helpful to you, but the javascript window.setInterval creates a recurring function call and could be used to do asynchronous callbacks to monitor the session state. However, if all you want to do is throw a warning alert(), that is probably overkill and the existing code will work fine.
The above code will not work if there are multiple windows open as you are setting the expiry time from the server and that is valid only for the instant that the request is serviced. This will result in different windows showing the alert at different times. Checking the cookie expiry with some JavaScript periodically is a much better idea.

How to delete data from database when session dont exists

I'm developing a chat module for my application...
I'm opening a window for users to chat, is there way that when users close the chat window, I can update status of that record...i mean event for the closed browser?
I know that default session time is 24mins,
so after 24mins of inactivity, user will be kicked out from the site and will asked to login once again.
How to delete/flush the data in my database, when user has no activity for 24mins (when user is logged out from the session due to inactivity)?
1) You'll need javascript onUnload event for this one. It'll send an asynchronous query to your webserver, setting the offline status of the user. However, you should not rely solely on this event and also set up the 24 mins auto-offline timeout because it is not guaranteed that the user is using javascript.
2) I think your best option here is running a cron job (every 30 mins or so?) that queries your database, identifies the users whose last activity was more than 24 mins ago and then deletes the associated data.
Store every chat entry's timestamp in UNIX_TIMESTAMP. When a new chat entry incoming check every entries timestamp where timestamp is smaller than now - 24 mins. Kick users.
1) use the unload event
2) if you are developing a chat, i guess that you have a periodical function that calls the server constantly to retrieve the messages. Each time this function is called, the inactivity time will be reset, even if the user haven't sent a message. If you want to logout the user when he doesn't write anything in 24min you cant rely on the php sessions.
What you can do is: save in the db, the last time the user wrote a message on the chat, and each time you use your periodical function, validate if the user hasn't wrote anything in the last 24 mins
Use a javascript function for the event
window.onbeforeunload = myLogoutFunction;
Note: This javascript will not work on browser crash.
Have a user_log database table and fill the login and "logout" dates in it.
When there is a user with not updated logoff date, then you can assume there was something wrong with his connection.
$where = " AND `users_id`='".$response['userfound']['id']."'";
$where .= " AND `logoffdatetime`='0000-00-00 00:00:00'";
After 24min php session is gone (default php.ini settings). It wont be much usable. But you can still save into the user_log table.
You should not need the flush database.
Keeping the chat users via database alive is bad idea. Instead use a small file with timestamp in it.
Here some other useful tips
Detect if the user coming back without logout
$user_navigates = false;
if(isset($_SERVER['HTTP_REFERER']) && basename($_SERVER['HTTP_REFERER']) != _PAGE)
$user_navigates = true;
save also page refreshes into session
if(isset($_GET['pagerefreshed']))
$_SESSION['pagerefreshed'] = $_GET['pagerefreshed'];
save the logging out user_id into session, so you can use it restore things. For instance no need to reload page.
$_SESSION['loggedout']['user_id'] == $login->user_id

Categories