Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Auth : Admin custom tokens cannot be refreshed after one hour

I think the Firebase Admin SDK is missing a very important function (or maybe its documentation).

TL; DR : How can you refresh custom token with the Admin SDK?

The documentation (https://firebase.google.com/docs/auth/admin/manage-sessions) says:

Firebase Authentication sessions are long lived. Every time a user signs in, the user credentials are sent to the Firebase Authentication backend and exchanged for a Firebase ID token (a JWT) and refresh token. Firebase ID tokens are short lived and last for an hour; the refresh token can be used to retrieve new ID tokens.

Ok. But how? There is no mention how to replace the refresh token with a new custom token. There are lots of documentation regarding how you can revoke a refresh token etc...

There is however a REST api function that says, (https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token)

Exchange a refresh token for an ID token You can refresh a Firebase ID token by issuing an HTTP POST request to the securetoken.googleapis.com endpoint.

However, the access_token (JWT) you get from this API call is not accepted neither. And the format of the JWT's are not even similar. Below are two samples of custom tokens retrieved (decoded) : i. with the admin.auth().createCustomToken(uid) method of Admin SDK

{
  "uid": "9N5veUXXXXX7eHOLB4ilwFexQs42",
  "iat": 1521047461,
  "exp": 1521051061,
  "aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
  "iss": "[email protected]",
  "sub": "[email protected]"
}

ii. with the https://securetoken.googleapis.com/v1/token?key=[API_KEY] call

{
  "iss": "https://securetoken.google.com/XXX",
  "aud": "XXX",
  "auth_time": 1521047461,
  "user_id": "9N5veUXXXXX7eHOLB4ilwFexQs42",
  "sub": "9N5veUXXXXX7eHOLB4ilwFexQs42",
  "iat": 1521051719,
  "exp": 1521055319,
  "email": "[email protected]",
  "email_verified": false,
  "firebase": {
    "identities": {
      "email": [
        "[email protected]"
      ]
    },
    "sign_in_provider": "password"
  }
}

There are plenty of questions raised about this topic. Maybe someone from Firebase team can answer it once and for all. See the links below

Thanks for your time!!

  • How to use the Firebase refreshToken to reauthenticate?

  • Firebase - Losing Auth Session After An Hour

  • How to handle custom firebase token expiry in Firebase 3.x.x

  • Firebase authentication with custom token

  • Handling one hour token expiration in Firebase generated from iOS used for node auth

like image 982
Kemal Yalcinkaya Avatar asked Mar 14 '18 20:03

Kemal Yalcinkaya


2 Answers

You need to exchange a custom token for an Id Token and a Refresh token, this is mentioned here. The call should include the custom token and the property "returnSecureToken" as true. If this property is not added or is false, you will only get the ID Token.

After doing that, you can use the Refresh token to get a new ID Token once it expires. See the documentation.

Both, the custom token and the ID token, are short lived (1 hour) but the purpose is different, that is why the formats are different. You use the Id Token to make authenticated calls, whereas the custom token is only used to start the session and get an ID Token and Refresh token.

Keep in mind that if you are using an SDK, this whole work is being handled by the SDK.

like image 73
Gerardo Avatar answered Sep 18 '22 16:09

Gerardo


You do not refresh already existing Custom Tokens but rather create new ones and exchange them for Access or Refresh Tokens. Here is how I did it in a working project I am currently using

GENERATING A CUSTOM TOKEN FROM FIREBASE CLOUD FUNCTIONS

Assuming you have your firebase project and a Cloud Functions for Firebase all set up.

This is how the Cloud Functions index.ts file would look like:

import * as functions from 'firebase-functions';
import * as admin from "firebase-admin";

// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript

export const getCustomToken = functions.https.onRequest((request, response) => {
  if (admin.apps.length < 1) {   //Checks if app already initialized
    admin.initializeApp();
  }
  const uid = "USER_UUID"; //e.g. GVvCdXAC1FeC4WYTefcHD8So3q41

  admin.auth().createCustomToken(uid)
    .then(function(customToken) {
      console.log(customToken.toString);
      response.send(customToken);
    })
    .catch(function(error) {
      console.log("Error creating custom token:", error);
    });
});

The http GET request would look like:

https://us-central1-<ProjectID>.cloudfunctions.net/getCustomToken

The response would look like:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImlhdCI6MTU0MTMwOTY3MiwiZXhwIjoxNTQxMzEzMjcyLCJpc3MiOiJlbWFsbC02OWU3MEBhcHBzcG90LmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzdWIiOiJlbWFsbC02OWU3MEBhcHBzcG90LmdzZXJ2aWNlYWNjb3VudC5jb20iLCJ1aWQiOiJHVnZDZFhBQzFGZUMyV1lUZWZjSEQ4U28zcTQzIn0.hsazo6ELKbLHwPfP2d9rEykKXsBB1CdB1pCQKIVX8_Xo7tnJ0S80LQbE17ktOJ_FTr4MIllVjOLhS3kpWtKYX6Ju4kNMZ2ROLJz1bvwwgcw5unrRdQHEa3SLuyW1HvaOwKiDeYpTx2lwhZnkuBEvcoo1VcbllfYfFLIR_Y47eticONO572EL4GcIuw-RGRx1AXJR-rigRE3bj6_Ohc-PLIVXdH5v1z8fpctM2MA4NxoOZXsBDGH_ZW2Kn4NRBZYo_IT99VJU8Ypsbi_6eJguhDlbl5oWp5_NEEIEuZrN9oLaHL-PUvB8_h10lvQ6c5yP-aFKwC_EHaKBnkz7vXt8Gw

Most probably you would have to enable the IAM(Identity and Access Management) if not enabled and set up the Service Account Credential. Check the Troubleshooting.


EXCHANGING CUSTOM TOKEN FOR REFRESH & ACCESS TOKENS

The http POST request would look like:

https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=<Firebase Project Web API Key>

with a body like:

{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImlhdCI6MTU0MTMwOTY3MiwiZXhwIjoxNTQxMzEzMjcyLCJpc3MiOiJlbWFsbC02OWU3MEBhcHBzcG90LmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzdWIiOiJlbWFsbC02OWU3MEBhcHBzcG90LmdzZXJ2aWNlYWNjb3VudC5jb20iLCJ1aWQiOiJHVnZDZFhBQzFGZUMyV1lUZWZjSEQ4U28zcTQzIn0.hsazo6ELKbLHwPfP2d9rEykKXsBB1CdB1pCQKIVX8_Xo7tnJ0S80LQbE17ktOJ_FTr4MIllVjOLhS3kpWtKYX6Ju4kNMZ2ROLJz1bvwwgcw5unrRdQHEa3SLuyW1HvaOwKiDeYpTx2lwhZnkuBEvcoo1VcbllfYfFLIR_Y47eticONO572EL4GcIuw-RGRx1AXJR-rigRE3bj6_Ohc-PLIVXdH5v1z8fpctM2MA4NxoOZXsBDGH_ZW2Kn4NRBZYo_IT99VJU8Ypsbi_6eJguhDlbl5oWp5_NEEIEuZrN9oLaHL-PUvB8_h10lvQ6c5yP-aFKwC_EHaKBnkz7vXt8Gw","returnSecureToken":true}

The response would look like:

{
    "kind": "identitytoolkit#VerifyCustomTokenResponse",
    "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijk4Njk0NWJmMWIwNDYxZjBiZDViNTRhZWQ0YzQ1ZWU0ODMzMjgxOWEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZW1hbGwtNjllNzAiLCJhdWQiOiJlbWFsbC02OWU3MCIsImF1dGhfdGltZSI6MTU0MTMxMDkzOSwidXNlcl9pZCI6IkdWdkNkWEFDMUZlQzJXWVRlZmNIRDhTbzNxNDMiLCJzdWIiOiJHVnZDZFhBQzFGZUMyV1lUZWZjSEQ4U28zcTQzIiwiaWF0IjoxNTQxMzEwOTM5LCJleHAiOjE1NDEzMTQ1MzksImVtYWlsIjoiYUBhLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJhQGEuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiY3VzdG9tIn19.KnMU0SHoMkMOwGBOfwnwMYCyFAGZycC1zA5pva47i4TylGdZyz-93h3KyWA_EYHGZtI29YWfarUG0-6K_sLORttMzKy3t9jBcvhgWN8G9zE8DHg0DuOeaDxDfKY8-W-CBgh8wiTSOfz-CRTT9spXoP_9PigdWFKiwmgP_vvOGStONFjUMh2hSNaRhHAj_0nlFxQuBsoP9eV3uGm1ycC3z8e5AHVbvE7VgIxK27OcKY4z9n1IrBADp9gxM6ESlOYE2y_bfP2i_WIv_4ZQ3fA0aeKhSjhO7AhKUVvZ8FphqzlHF_q966QIglLf9vkVVzQCo-9YdD9j_GRea88tj3P5PQ",
    "refreshToken": "AEXAG-dZJD0zYr-RehU4qXLDRwf1SueYHPeQv6WHQ-w3SW8oFPU27EwdcrBcRP1p4hbTMIjeTTOub9buL20c3dxQvjpCzI4gda73jhHhigLFq6LZGU_S0VXW-9_gG_Vrcx25g2SAiMEt3WuLlP5h0R4h6Eo_DeX2F15vGQMxqplqcOSNGptN-r0",
    "expiresIn": "3600",
    "isNewUser": false
}

Good luck,

like image 21
ArtiomLK Avatar answered Sep 17 '22 16:09

ArtiomLK