Auto login on app start using refresh/custom token

9,225 views
Skip to first unread message

Sanjay Tibrewal

unread,
Dec 6, 2016, 12:25:55 AM12/6/16
to Firebase Google Group
Hi,

I have set up an Ionic App using Firebase to register using the following steps - 
  1. Take phone number and send a code through SMS from the backend
  2. User enters the code that backend validates and calls createCustomToken to generate a custom token for the user that is sent to the front end
  3. From the front end the user signs in with firebase.auth().signInWithCustomToken(<customToken from step 2>)
All this is working fine.

Now the user may close the App and when he comes back I want the user to automatically log in without having to go through the phone number and SMS code again.

I see that firebase stores the session information in LocalStorage where a refreshToken is also there. I have two questions - 
  1. What is the life of this original token and how do I change that to what I want? Some articles I have read seem to indicate it is 1 hr by default but not sure
  2. When the user restarts the App, how do I perhaps use what is in LocalStorage or otherwise to authenticate the user and transparently log him in?
I have read through a bunch of articles but don't seem to have clear picture of how this works. 

Thanks for any insights and pointers to relevant documentation/article.

-Sanjay

Jacob Wenger

unread,
Dec 6, 2016, 1:00:57 PM12/6/16
to fireba...@googlegroups.com
Hey Sanjay,

Let's see if I can shed some light on some of these topics.

You only need to call signInWithCustomToken() once and the user will be signed in indefinitely. Every time you restart the app, you can use onAuthStateChanged() to detect that the user is still signed in. The original custom token you created (docs for that are here) expires after one hour. However, once you sign in with that custom token on the client, the user will be signed in indefinitely.

If the onAuthStateChange() method is not firing with the signed-in user upon app restart, then that is a bug. If that is the behavior you are seeing, please send us more information about what versions of Ionic, Angular, and Firebase you are using.

In case you are interested and since you've done some digging into the internals already, there are actually three types of tokens involved here:
  1. Custom token - You create this on your custom server with a custom payload and send it to the client device to be used with signInWithCustomToken(). This token expires after one hour.
  2. ID tokens - Short-lived tokens which actually allow you to interact with Firebase services like the Realtime Database and Storage. An ID token is passed down to the client device as a result of the signInWithCustomToken() method. These tokens expire after one hour, but are automatically refreshed by the Firebase SDK using the refresh token (see next numbered bullet). These are the tokens you should use to identify users on your own server (if needed; and described here).
  3. Refresh token - This is a long-lived token which is used to generate new ID tokens. It is also passed down to the client device as a result of the signInWithCustomToken() method.
Hope that helps!

Cheers,
Jacob

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/59e014fb-1c72-4bea-9cb3-888f766aa8a2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Sanjay Tibrewal

unread,
Dec 7, 2016, 2:32:37 PM12/7/16
to Firebase Google Group
Thanks Jacob - that worked perfectly as I wanted!

And thank you for the awesome explanation of the different types of tokens. If it is already somewhere in the documentation then my bad that I missed it. If it is not there then would recommend this be added as this will be very helpful for people trying to grasp the nuances of the different tokens. At least they have this post from you now and hopefully they will find it when needed.

Thanks again.

Sanjay.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.

Jacob Wenger

unread,
Dec 7, 2016, 2:45:29 PM12/7/16
to fireba...@googlegroups.com
Hey Sanjay,

I would say around 75% of what I wrote is scattered in the documentation. We have full pages dedicated to custom tokens and ID tokens. But we don't make any reference to the refresh token since we consider it an implementation detail and end users should never really need to interact with it. However, we are always looking to improve our documentation and clearly we need to make the docs better so users like yourself can grok all this information without needing to find my response above. Thanks for the feedback and helping shed some light on these issues! I'll see what I can do to improve our docs.

Cheers,
Jacob

To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

Chris Dawson

unread,
Dec 8, 2016, 1:14:43 AM12/8/16
to Firebase Google Group
Does the method signInWithPopup have the same behavior (meaning, call it again to get into the onAuthStateChanged callback)? I'm getting strange behavior on one browser where I can't login and am falling into the error callback. It is tricky to troubleshoot. It works on other browsers and devices.

Is there an example bit of JS code that handles the initial login and then the next time the user comes back the same code does not need to do the pop-up and can retrieve the user information? I'm not finding a way to do this and not sure from the documentation how to write my code.

Jacob Wenger

unread,
Dec 8, 2016, 2:00:17 PM12/8/16
to fireba...@googlegroups.com
Hey Chris,

When your page, loads, you'll want to use the onAuthStateChanged() method to determine if a user is signed in. If they are, then just carry on with your app. Otherwise, sign them in:

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    // User is signed in
    // Show them the authenticated content...
  } else {
    // No user is signed in
    // Let's sign them in
    signUserIn();
  }
});

Elsewhere in your code, you'd define a signUserIn() function which looks like this:

var signUserIn = function() {
  var provider = new firebase.auth.FacebookAuthProvider();
  firebase.auth().signInWithPopup(provider).catch(function(error) {
    console.log("Error signing user in:", error);
  });

  // We don't need to include a then() block above since, if the sign in
  // is successful, our onAuthStateChanged() method will be called with
  // the signed-in user.
};

You can find the above information (and more) in our Authentication docs.

Cheers,
Jacob

On Wed, Dec 7, 2016 at 1:28 PM, Chris Dawson <xrda...@gmail.com> wrote:
Does the method signInWithPopup have the same behavior (meaning, call it again to get into the onAuthStateChanged callback)? I'm getting strange behavior on one browser where I can't login and am falling into the error callback. It is tricky to troubleshoot. It works on other browsers and devices.

Is there an example bit of JS code that handles the initial login and then the next time the user comes back the same code does not need to do the pop-up and can retrieve the user information? I'm not finding a way to do this and not sure from the documentation how to write my code.
--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.

Dan Simmons

unread,
Jan 9, 2017, 10:41:15 AM1/9/17
to Firebase Google Group
Hey Jacob,

I have pretty much the exact same scenario as Sanjay, including using a passwordless OTP system to generate a custom token. Luckily I was able to find this thread, and it's recent enough that it felt like it made sense to continue here in lieu of creating an entire new thread being, that my questions are so logically tangential.

For what it's worth, perhaps the one difference in my situation that's worth noting is that I'm also using Firebase Web within React Native.

I understand that, upon authenticating in with a custom token, I'll get back both a token and a refresh token. I also understand that, upon initialization, Firebase does some "magic" behind the scenes to hide the token refreshing (if necessary, outside of the hour window).

My remaining questions are as follows:

  • What am I to do for a long-lived session? Here, as mentioned, I'm operating within a "native" app. I also have a separate web client using Firebase Auth that's expected to have long-lived sessions as well (used for hours at a time, without refreshing or otherwise "closing the tab").

For both of the above (native app and web client), I've exposed respective APIs that use the JWT (obtained by authenticating) as an API access token. So I validate the token on the server-side, and, if valid, respond appropriately. However, outside of an hour, the original token that was obtained "client-side" (including in the case of the native app) is no longer valid, as the expiry window has been exceeded. So in this case, I'd like to use the refresh token to obtain a new JWT on the client-side, valid for the next hour, and re-issue the request.

What I don't want to do is have the user re-authenticate (or otherwise bother the user). Keep in mind, as I've mentioned above, the user hasn't refreshed the page or closed the native app respectively. Is there a better or more robust way of doing this than to initialize a "setInterval()" for maybe slightly under one hour, at which point I'd use the "forceRefresh: true" option to get a new token?

  • I see the error conditions that are possible with onAuthStateChanged, but it's not clear to me from the documentation under what conditions event emitter emits non-error events. Obviously, at least once, when the app is initialized to rehydrate the user (if applicable). But given that this is an observable as opposed to a one-time operation like a Promise, over the lifetime of the application, under what other circumstances would this emitter fire? Similarly, are there any guarantees as to how many times this emitter will fire (eg. at least once, 1...n, etc...)
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

Kato Richardson

unread,
Jan 9, 2017, 12:34:11 PM1/9/17
to Firebase Google Group
Hi Dan,

Keep in mind that this happens automatically. You shouldn't need to refresh the tokens on your own. 

You only need to call signInWithCustomToken() once and the user will be signed in indefinitely.

Note that once the token is verified at your server, there's no reason to be concerned about it expiring. Your server should utilize the admin SDKs (perhaps with limited privileges by uid) and not try to authenticate as a client, and thus when the token expires, as long as you've already verified it's validity, there's nothing to do here.

One simple way to avoid this mess on the server is to have the clients write to the database and have the server listen there, instead of exposing RESTful APIs and such. See firebase-queue for a good strategy here. Since security rules enforce authentication, your server doesn't need to handle all of this verification; if the client can write to a given DB path, they are verified according to the rules you've set there.

onAuthStateChanged() will be called whenever authentication state changes. Thus, if the user is signed out or signed in at any point after the app starts, it will get invoked with the new user object (or null in the case of a sign out event). This can obviously happen after the app is initialized, thus the subscription model rather than a one-time callback.

☼, Kato


To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--

Kato Richardson | Developer Programs Eng | kato...@google.com | 775-235-8398

Dan Simmons

unread,
Jan 9, 2017, 1:42:25 PM1/9/17
to Firebase Google Group
Hi Kato,

I appreciate the prompt reply! I think we may have had a misunderstanding in terms of what I originally wrote however.


Keep in mind that this happens automatically. You shouldn't need to refresh the tokens on your own. 

    You only need to call signInWithCustomToken() once and the user will be signed in indefinitely.
 
Note that once the token is verified at your server, there's no reason to be concerned about it expiring. Your server should utilize the admin SDKs (perhaps with limited privileges by uid) and not try to authenticate as a client, and thus when the token expires, as long as you've already verified it's validity, there's nothing to do here.

In reverse order:

  • All I'm doing is validating the token on the server, using the Admin SDK. The token is coming from and being produced by the client, in the form of authenticating and providing the resultant token in the request headers. Expiration being an issue is the result of having come from the client-side: if the client maintains the same original token for longer than an hour, the server (rightly) now considers it invalid.
  • Is this the case without refreshing the page for longer than an hour? I could do an additional experiment and wait an hour to verify, but I don't believe this is the behavior I've seen in the past, having left the app open for over an hour. In the event that it is the case, how do I know when the token has been updated? Do I get an additional event emitted from the onAuthStateChanged() observable at which point?
One simple way to avoid this mess on the server is to have the clients write to the database and have the server listen there, instead of exposing RESTful APIs and such. See firebase-queue for a good strategy here. Since security rules enforce authentication, your server doesn't need to handle all of this verification; if the client can write to a given DB path, they are verified according to the rules you've set there.

I'm not sure refreshing an expired token (again, on the client-side) is "a mess" -- it's very simple. I just need to figure out how to be able to do it myself it it's not done for me automagically (again, after the original token has expired, and without having to refresh the page).

But more importantly, telling my external API consumers to "look the data up in or write to our FIrebase database" isn't a viable option.

onAuthStateChanged() will be called whenever authentication state changes. Thus, if the user is signed out or signed in at any point after the app starts, it will get invoked with the new user object (or null in the case of a sign out event). This can obviously happen after the app is initialized, thus the subscription model rather than a one-time callback.

  • Would this ever fire as the result of an external action?
  • If I could state the original question another way, it'd be along the lines of, "Does this observable ever tell me something that I don't already know?". So like, in the case of the user logging out, I don't really need the observable to tell me that. But would it fire if the user changed his or her password in a different "app session" (eg. a user is logged in in a web app, and user changes his/her password on the corresponding mobile app).
  • Going back to some of my above questions, does this fire once an hour when the token is refreshed (assuming this happens for me automatically)?

Kato Richardson

unread,
Jan 9, 2017, 3:24:26 PM1/9/17
to Firebase Google Group
Well, we're moving out of my auth depth in general, and others may chime in with more flavor.


Returns the current token if it has not expired, otherwise this will refresh the token and return a new one.

So I'd expect after an hour, that you'd get back a new auth id token to send to the server, rather than a stale and expired one. Of course, if there's any possibility it's close to expiring, I'd probably pass the boolean to force refresh.

As for onAuthStateChanged(), it's not getting called if the user signs in or out from another device, since they don't share the same auth session. However, browser tabs share session info and it will be triggered if signed in/out from another tab.

I hope that helps!

☼, Kato



To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Dan Simmons

unread,
Jan 9, 2017, 11:10:36 PM1/9/17
to Firebase Google Group
Hey Kato,

Thanks again for the prompt reply!

Additionally, I appreciate the pointer to that specific blockquote within the documentation -- I must have glossed over that. That's definitely what I was looking for.

I guess it's worth noting that, one prior assumption I had, is that I'm managing most of the app state myself (using React + a state atom, using Redux). So, upon initial authentication, I do call `getToken()` and store it in my app state. What I'm not (currently) doing is calling `getToken()` every time I need to use the token, which it sounds like I might. I think what I'd assumed is that `onAuthStateChanged()` would be fired again when the token was refreshed.

Although it's easy enough to make that change, it'd be great if I didn't have to inject another dependency into my API / network request logic. The latter is going to make testing more complicated than if I, for instance, just had a function that accepts a string (versus having to do some sort of dependency injection / mock out Firebase).

It'd be awesome if there was a way to receive an event if/when the token is refreshed so that I can update my state accordingly. That way, I can just store and pass around strings (the current token) as opposed to having to make a "proxy" method call, especially where it won't be necessary most of the time (within the expiration window).


In any case, I sincerely appreciate your help!

Best,
Dan

Reply all
Reply to author
Forward
0 new messages