12
votes

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!!

3
@kamal Yalcinkaya has you got the solution for ios. I'm struggling for the last 3 days. if you find the solution please post here it helps me a lot.Ramkumar chintala
Is it possible to use the REST AUTH api in the context of a service account? All of the other REST APIs for administration pass an oauth2 access token, not idTokens. I can sign a jwt payload with my service credentials, but what payload am I supposed to be passing, the user I want to act on behalf of? Basically I want to be able to do what i can do manually from the console but via REST HTTP and not depending on getting a short lived idToken from the client. My user doesn't have to open the app for me to be able to delete the account on console.Ryan Romanchuk
I have been trying to exchange a custom token (no admin sdk) for idToken in order to take administrative actions, but it doesn't like my token. Maybe it's just a malformed payload, but i'm not even sure if i'm even conceptually correct anymore. Here's where i'm at gist.github.com/rromanchuk/c61aa6e59420416e9469abc7d3c1c2feRyan Romanchuk

3 Answers

5
votes

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.

3
votes

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,

0
votes

How can you refresh custom token with the Admin SDK?

Answer: If you are using the Android SDK for Firebase, you should NEVER have to do this. If you are asking this question and are using the iOS or Android SDKs you probably have a set up issue. The SDKs will handle all the refreshing of tokens if you are SETUP CORRECTLY. I was using the FirebaseAuth's signInWithCustomToken and was running into the same issue.

An overview of Firebase tokens
The SDK has 1 hour to USE THE CUSTOM TOKEN If you read the whole conversation and ignore the complaining, it lays out the issue. See samtstern comment around July 6th

Once FirebaseAuth's signInWithCustomToken function is called, the SDK will take care of keeping the tokens up to date IF YOU ARE SETUP correctly. For more info

The SHA1 cert from your ANDROID app has to be in your Firebase Admin console. Once you add the SHA1 cert you will need to download the google-services.json file and add it to your app. I took over a firebase account for an app still in development and ran into this problem.