80

I use the JS library call firebase.auth().signInWithEmailAndPassword(email, password) and get back a User object. The User object contains a refreshToken.

I use curl 'https://docs-examples.firebaseio.com/rest/saving-data/auth-example.json?auth=TOKEN' to make calls to Firebase.

The token will eventually expire. In order to make it look like the application (iOS and macOS) has persistent login, I want to refresh the token, how do I do that with using either the REST or JS library? I can't find any calls in the documentation that allow me to use the refreshToken to get a new token.

kgaidis
  • 12,375
  • 4
  • 70
  • 84

4 Answers4

59

When you make call from a browser .getIdToken(true) will automatically refresh your token. Make call like this:

firebase.auth().currentUser.getIdToken(/ forceRefresh / true)
  .then(function(idToken) {
    
  }).catch(function(error) {

});

More info here https://firebase.google.com/docs/reference/js/firebase.User#getIdToken

krupesh Anadkat
  • 1,353
  • 1
  • 14
  • 25
Yevgen
  • 4,164
  • 3
  • 23
  • 33
  • 7
    Wouldn't currentUser be NULL on a fresh launch of an app? I don't know how it is on a regular browser, but I do not store any cookies or local data. I only store the refreshToken and anything else that I specifically need. – kgaidis Jul 06 '16 at 21:13
  • 2
    Login is needed to be performed first to use that code. If you use sign in with firebase it should work. – Yevgen Jul 07 '16 at 05:52
  • 2
    This! But `.getToken` is deprecated now, you must use `.getIdToken`. – Nick Rameau Jul 12 '17 at 04:20
  • I got 403 (Permission denied error) even though currentUser is not null (i.e. the user was signed in). Do you know the reason ? – Takamitsu Mizutori Mar 13 '20 at 08:51
  • 2
    @TakamitsuMizutori I had the same issue, the token couldn't be refreshed. I solved it by enabling the Token Service API for my Google API key. – Louis Ameline Apr 18 '20 at 16:33
  • @Louis Ameline It works! Never thought about the API key. Thank you so much – Takamitsu Mizutori May 04 '20 at 01:59
  • 2
    I think this answer is wrong as of February, 2022. After an hour, unless the user logs out and logs back in, the tokens getIdToken returns will be invalid. – stevehs17 Feb 08 '22 at 00:13
46

** UPDATE ** this is also now documented in Firebase REST docs under Exchange a refresh token for an ID token section:

https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token


Currently the only way I found to do this is here: https://developers.google.com/identity/toolkit/reference/securetoken/rest/v1/token

You must make an HTTP request:

POST https://securetoken.googleapis.com/v1/token?key=YOUR_KEY

Where YOUR_KEY can be found in the Google developers console > API Manager > Credentials. It's under the API Keys section.

Make sure request body is structured in the following format:

grant_type=refresh_token&refresh_token=REFRESH_TOKEN

Where REFRESH_TOKEN is the refresh token from Firebase user object when they signed in.

You must set the header Content-Type: application/json or you will get errors (e.g. "MISSING_GRANT_TYPE").

The POST call will return a new idToken (used to be called access_token)

Jithin
  • 3,102
  • 1
  • 23
  • 41
kgaidis
  • 12,375
  • 4
  • 70
  • 84
  • Should this request work for refreshing Gmail API's token? @kgaidis – ArtStyle Aug 27 '16 at 00:46
  • @ArtStyle I am not familiar with Gmail API, so I don't know – kgaidis Aug 27 '16 at 01:53
  • This works but unfortunately does not trigger firebase's [onIdTokenChanged](https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onIdTokenChanged) listener... I feel it should – Luiz Oct 08 '17 at 22:04
  • I am using the same way and i am getting the id_token. But when i try to fetch values from the DB using the id_token , i get an error saying 'permission denied'. – Kumar KS Nov 16 '18 at 16:13
  • how to use the obtained data to set the new token? – Killy Dec 05 '18 at 11:13
  • Will the new, google-identity token be invalidated if the firebase user is deleted or invalidated? @kgaidis – Lane Sep 03 '19 at 00:53
  • I got 403 (Permission denied) error when I do this. Can I call this API from web client javascript ? – Takamitsu Mizutori Mar 13 '20 at 08:50
  • 1
    `access_token` = `id_token` – Rakka Rage May 30 '20 at 15:48
  • Im using Restrict keys for requests to the specified websites. But If add the ```https://securetoken.googleapis.com``` to my restricted list I'm not able to call the API and I have to remove all other restricted URLs to make a call. In that case, my key would be accessible for every address. Any suggestion? – ikarayel Oct 15 '20 at 13:40
  • 1
    thanks so much you made my day, I wish I could have voted a 100 times. My app kept on yelling token expired throughout development, till you came to my aid – Chukwu3meka Nov 12 '20 at 18:14
  • For me, it did not work with `Content-type: application/json` and I had to go back to the old `x-www-form-urlencoded`. – Spoutnik16 Apr 20 '22 at 07:05
13

I guess most people here are looking for a way to persist their authentication not in a browser but e.g. on a node backend. Turns out there actually is a way to do this:

  1. Trade the refresh-token for an access-token (using google's public api)
  2. Trade the access-token for a custom-token (using a firebase-function, see below)
  3. Login with custom-token

Here's the essence of the code:

const requestP = require('request-promise');
const fsP = require('fs').promises;

const refreshToken = await fsP.readFile('./refresh_token.txt');
const res = await requestP.post({
  headers: {'content-type': 'application/x-www-form-urlencoded'},
  url: 'https://securetoken.googleapis.com/v1/token?key=' + firebaseConf.apiKey,
  body: 'grant_type=refresh_token&refresh_token=' + refreshToken,
  json: true
});
const customToken = await requestP.post({
  headers: {'content-type': 'text/plain'},
  url: 'https://<yourFirebaseApp>.cloudfunctions.net/createCustomToken',
  body: {token: res.access_token},
  json: true
});
await firebaseApp.auth().signInWithCustomToken(customToken);

And the firebase function:

export const createCustomToken = functions.https.onRequest(async (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');

  try {
      const token = JSON.parse(request.body).token;
      const decodedToken = await admin.auth().verifyIdToken(token);
      const customToken = await admin.auth().createCustomToken(decodedToken.uid);
      response.send(customToken);
  } catch(e) {
      console.log(e);
      response.sendStatus(500);
  }
});
krupesh Anadkat
  • 1,353
  • 1
  • 14
  • 25
Martin Cremer
  • 4,582
  • 2
  • 27
  • 36
  • In my case the custom token expires after one hour, what did you do to solve that? – realappie Aug 04 '19 at 17:44
  • I just read your answer again, I guess what I need to persist is the refresh token and not the custom token. You are continuously creating custom tokens. – realappie Aug 05 '19 at 15:20
  • Yes, you should store the refresh-token, like: firebaseApp.auth().onAuthStateChanged((user) => { if(user) { fs.writeFileSync('./refresh_token.txt', refreshToken); } }); – Martin Cremer Aug 05 '19 at 15:47
  • but the refresh token changes from one user to the other, how do you save it in a text file? – Ayyash Aug 28 '19 at 05:30
  • is this safe to the public to create custom tokens from refresh tokens? – Michael Xu Mar 17 '21 at 20:59
  • to avoid any confliction you should replace 'const res = await requestP.post' with 'const anotherRes = await requestP.post' if you have another res inside the same function – Shalabyer Apr 07 '22 at 22:11
2
// Create a callback which logs the current auth state
function authDataCallback(authData) {
  if (authData) {
    console.log("User " + authData['uid'] + " is logged with token" + authData['ie']);
  } else {
    console.log("User is logged out");
  }
}
// Register the callback to be fired every time auth state changes
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.onAuth(authDataCallback);

Event onAuth will be called on page refresh, if user was logged out authData will be null, else not. You can find token in authdata['ie']. In the screenshot bellow I have printed the token after auth and authdata object, how you can see authData['ie'] and token are similar.

authdata.ie and token

Nikita
  • 942
  • 1
  • 13
  • 35