Why is Firebase onDisconnect firing every hour? - javascript

I have a Firebase app using the javascript web SDK. Our clients are using the custom authentication method. We have implemented a presence system using onDisconnect. We are seeing that onDisconnect fires once every hour, and in most cases the client didn't actually disconnect. This is a problem for us because we are tracking and presenting connect/disconnect events for our clients, and it makes it look like the clients are disconnecting when they aren't.
My guess is that there is some sort of authentication token refresh that is happening every hour, which causes onDisconnect to fire. However, I don't know how to dig in deeper to confirm that or find a different root cause. Can anyone help me figure this out?
It was suggested that this is a duplicate of this question, but that is about basic onDisconnect operation, and this is about a repeatable observation that onDisconnect fires every hour although the client has not disconnected.

I have no idea how this onDisconnect function is implemented. It would be helpful to explain how you determine the disconnection. Firebase Id tokens expire every hour and need to be refreshed (automatically refreshed after when calling any user method). If you are using real time database, they will try to refresh on expiration. You can add a listener in onAuthStateChanged when a token is issued and set a timer to automatically refresh the token (getToken(true)) before it expires, ensuring the disconnection does not trigger.
Also, the onDisconnect will fail during that disconnection because Firebase consider the token expired.

Since you're using Firebase Web SDK, you could obtain current token and check its expiration timestamp by calling:
firebase.auth().currentUser.getToken(false); // pass false to retrieve cached token
You could setup interval to periodically check the token expirationTime and try manually refreshing token just before it expires (for example 60 seconds before it expires) by calling:
firebase.auth().currentUser.getToken(true); // pass true to force token refresh
This should prevent onDisconnect event (.info/connected listener) from firing due to token expiration.

I believe the onDisconnect firing every hour is a bug in the Firebase JS SDK. Normally Firebase Auth will proactively refresh the token before it reaches expiration if consumers such as a database connection exist. If the refresh doesn't happen automatically, the connection gets closed at expiration, and the subsequent reconnect will request a new token, causing it to be refresh then.
I've confirmed that manually starting the proactive refresh logic avoids the hourly disconnect.
I've filed a bug against the SDK: https://github.com/firebase/firebase-js-sdk/issues/5498

Related

Office-Addin MSAL Single Sign In SSO: How to Refresh the Access Token?

How does one monitor the access token for expiry and refresh it in an Office-Addin Single Sign on Solution?
MSAL Single Sign On in an Office-Add in is a complicated beast. The access token is obtained by calling:
OfficeRuntime.auth.getAccessToken()
The access token is then swapped for an access token for my API using the Microsoft Identity On-Behalf-Of-Flow. All good.
But access tokens expire. The OfficeRunetime.auth does not appear to have anything that manages the expiry and refresh of access tokens. Unlike its counterpart MsalService (which runs in a normal Javascript/Angular web app - not an Office-Addin) and manages the tokens - and can not be used in an Office-Addin.
Do I simply continue to call OfficeRuntime.auth.getAccessToken() in an HTTP Interceptor every time prior to an API call? I understand that getAccessToken() does first consult the cache for an AccessToken, do I infer then that it is managing the expiry of that token and going back to identity server to get a new one?
Or am I completely on the wrong track?
After a lot of reading and prototyping, I'll answer my own question.
Firstly, I am a beginner when it comes to MSAL, tokens, claims, scopes etc and all matters authentication, and I've painfully waddled my way through it all. I welcome all feedback to this answer, and will update the answer if necessary
Here is what I have observed: But firstly, in conclusion to my question
OfficeRuntime.auth.getAccessToken() manages and refreshes the token when it expires (for MSExcel).
Background
Firstly, I am using MSExcel. The Microsoft Documentation seems to
group Excel/Word/PowerPoint together and deals with Outlook
differently. I am grateful for #Eugene Astafiev answer to this
question from the Outlook perspective, but I found the opposite to
be true for Excel
The office Add-in OfficeRuntime.auth.getAccessToken() returns,
what Microsoft docs calls, a 'bootstrap' token being an access token that also contains an identity token. The bootstrap token can be used in the On-Behalf-Of Flow (in which it is called the 'assertion' token) to swap it for other access tokens for different scopes. In my app, I swap if for an access token for my API, lets call it the APIAccessToken
Both the bootstrap token and the identity token within it expire at
the same time, after about 87 minutes. The expiry time is recorded
on the Identity Tokens 'exp' claim. I don't know if this is
configurable somewhere, but the key is that both expire together.
The APIAccessToken (the one returned from the OBO flow) also expires at the same time as the bootstrap token (well almost, it seems to expire randomly up to 10 mins after its official expiry time). Every time I call the OBO flow, the generated APIAccesstoken returned is different- but only in the later half of it. Although they are different, and without any regard to what time they were generated, they all still expire at the same time as the bootstrap token (assertion token) that was used to be swapped for them, and they all remain valid, and useable until the expiry time (even though they are different)
It is not possible to get the APIAccessToken in any other way in the Excel Addin than using the OBO Flow. This is because of the challenges with IFrames and login redirects in Office Excel. Excel provides the getAccessToken() and a dialog box that runs in its own instance.(see MS Excel docs on Single Sign In for Office-Addin) The access token is only useful for OBO flow
The fallback authentication strategy outlined in the MS Docs https://learn.microsoft.com/en-us/office/dev/add-ins/develop/sso-in-office-add-ins is absolutely necessary, as you will always fallback when the user has not granted consent for your SPA and your API to access their profile. However, as currently documented it is not sufficient. It does not get consent for MSOffice15, and this leads to ongoing login problems. I have raised a Stack Overflow question on this here Office-Addin Single Sign In, How to manually add MSOffice15 consent and Error 13005 and currently have implemented a work around (I'll post it in response at that link). The fallback must manage three consents. MSOffice15, Your SPA and Your API.
Refreshing The Token
1. Every call to OfficeRuntime.auth.getAccessToken() returns the same bootstrap token with the same expiry time until it expires. When it expires I confirm that this function then calls the Identity Server Endpoint and obtains a new bootstrap token, with a new Identity token, whose expiry time is again ~ 87mins. This makes sense, since the Microsoft Documentation states that it is stored in Cache, and is retrieved by OfficeRuntime.auth.getAccessToken(). This makes this function call light-weight and can be invoked often.
MySolution
I derived a new class (inherited) from the official MSALInterceptor class. On every call to a protected resource I call the OfficeRuntime.auth.getAccessToken() If the token's expiry date has changed then I know that a new bootstrap token has been issued. I then call some middleware code to get a new APIAccessToken
I hope this saves someone some time! And leaving on a great note, I have finally got the entire Single Sign On login solution working for an Office-Addin SPA (angular) that manages all consents (office, SPA and my API). Took over 8 weeks work. Now its Friday 3pm - and I'm sneaking past the boss and going home :-)
I've faced with the same problem recently too. You (user) must restart Outlook to get a new access token (not expired). Office JavaScript API doesn't provide any method or property for that. The best what you could do is to handle exceptions and notify users to restart the host.
The cache is an in-memory cache. It is only cleared on reboot of Outlook. The tokens will automatically time out after the TTL for the token has expired, but can depend on the server implementation.
You can post or vote for an existing feature request on Tech Community where they are considered when the Office dev team goes through the planning process.

Node.js - Should I refresh cookie with each request/response to update expiration time?

Authentication method
In my Node.js (w/ Express.js) back-end, I authenticate users using JWT that is stored in a cookie with HttpOnly flag. The cookie expires in N hours. A middleware checks if JWT is valid and either calls next() function or sends a 401 status.
Current behavior
If cookie expires, user must log in again, even if he was still using the app.
Desired behavior
I want the cookie to expire in N hours but as long as user is using the app, expiration time must be updated. User should log in again only if N hours have passed from the last time he interacted with the app.
Question
Should I send a new cookie with each response, even if the only thing that changes is expiration time? Is this considered a good practice?
what you need is called refresh-token
you can find more detail about refresh tokens on:
https://www.rfc-editor.org/rfc/rfc6749#section-1.5 and
https://developer.okta.com/docs/guides/refresh-tokens/main/

Race condition trying to sync access token in multiple tab

Here’s what I’m facing. Currently, refresh tokens are stored in httpOnly cookie, and on every SSR, we refresh the token and send the whole response to the browser, the access token is only saved in memory, then the browser will continue for renewing it. The problem arises when we have 2 tabs, the access tokens will go out of sync. The initial tab’s access token will be unusable.
To work around this, we can either use localStorage to sync the tokens or use non-httpOnly cookie to store the access tokens. Or, manage tokens server-side, But both would have a problem, let’s say Tab 1 sent a request using the current access token, then I open Tab 2, the token is refreshed, Tab 1 request just reached the API, and the token is already invalidated.
I can’t find a proper solution unless we build more server-side logic into NextJS. Or perhaps if we don’t invalidate access token when refreshing and let it dies off the expiry. That way, we have ample time to fight the race condition.
Or, we retry all requests using exponential back off. Actually this kinda solves everything, and there’s a package ready for it. Except we’ll need to rewrite many parts to adopt the new library, and also there would be false alarms in the logs.
Or, we just ignore this and hope the race condition won’t appear, although it seems to me it’s going to occur pretty easily.
We are facing this problem in the company I work for.
Our solution is to store the refresh token in localStorage. If the token is regenerated, the other open tabs would detect a change in localStorage, retrieve the stored token (which now is a brand new one, generated by some other tab) and from that point on use it. The token is stored in memory until another change in localStorage is triggered.
To learn how to detect a change in storage: https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event
Honestly, there is no standard and robust solution to that as far as I know, and based on my research on the Internet.
Interesting problem. In single page apps each tab would be independent and have its own access token that it could store in HTML5 session storage or memory.
In server side web apps cookies are usually shared across all tabs and can cause conflicts.
I would try to follow the SPA model - there is no good reason why using your app on tab 2 should invalidate the token on tab 1.
They are different sessions and should be independent - as they would be if running one tab in Chrome and the other in Firefox.
Keep access tokens short lived (<= 60 minutes) so that after logout on tab 1 the user does not stay logged in on tab 2 for long.

How to schedule push notifcations for react native expo?

I am trying to send push notifications to a user at a scheduled time. Say they set the date for an event and they want to be notified 30 minutes before, that is when I would like to send them a notification. I am using firebase as my backend and the project is built with expo.
I am curious how I would use expo's notification system if I am using firebase cloud messaging because it says I need separate permission from firebase (I already have the expo token for each user). I have looked into node cron/schedule and also react-native push notification but I am unsure which would be the best solution and where I would deploy the solution (such as running a cloud function).
I assume I need some type of function that takes the token, message body, title, and date and then sets it up to schedule it either to the server or locally. And then that function would be called when they press the button to receive the notification. They can also change the date of the event so it would need to switch the date if the user did that.
Any advice would be greatly appreciated as I have been researching this for days and still am unsure of the best approach.
One possible approach:
In your backend, schedule a cron job that runs every minute (or every 15 seconds) and checks against database which events have start time within next 30 mins.
Once you have the events, find out the users registered for those events and collect their user ids.
As you mention that you already have stored the tokens, so I assume that those tokens exist in some table against the user id. (e.g. mapping of user-id and tokens). Look up this table to fetch the tokens of those users.
Prepare the notification payload and call firebase messaging to send notification against the token. For example, at this point you can call the sendToDevice() function from Firebase SDK: firebase.messaging().sendToDevice(tokens, payload);
Now you can implement these steps in your backend (e.g. Nodejs) or you can deploy a cloud function for this and setup scheduling for this cloud function.
Let me know if you need any further help!

Checking session expiration in the browser

I am working on an app that must use session timeouts.
The problem is that users regularly miss the session timeout and lose data. I already implemented a small session keeper in javascript that will renew the session every one minute if there has been some activity in the browser, but users are still somehow losing data (assumably they are half filling out a form, walking away from their machines and coming back after the session has expired and submitting the form.)
I would like to find some way to warn them that their session has expired. The problem is that I can't figure out exactly how to do it. I can't check the expiration of the session cookie in javascript, because it's an HttpOnly cookie, and if I do some kind of AJAX request to check the status of the session, it will automatically set a new expiration for the session.
Can anyone see a way around these obstacles?
This isn't a direct answer about checking session time-outs... but I've found garlic.js to help when I'm worried about users losing their work in forms. Basically, it's a JS library that takes care of saving form data in the user's browser's local storage until the form is submitted. So that, in case the browser closes or the session expires, the data is not lost. So this may be a good backup solution for the from data getting lost part.
UPDATE:
What I typically do to avoid form submission after session timeout is to set a javascript timer that will auto redirect the user to a session expired page (with an easy log in again button) a few seconds after the typical session timeout length. (You could reset this timer with your AJAX polling when there is activity within the page.) This combined with garlic.js, combined with "deep dive" functionality (whereby you store authenticated URLs in the session when they're accessed so that after a timeout and log back in you return the user to the last page they were on) creates a pretty seamless timeout, log-in, resume where you left off scenario.

Categories

Resources