2

I found a strange error while I developing system using Firebase with service url contains user data.

User data is below.

    {
      "uid": "kt9Hcp2FbYbBvvIeSHHa1RbvHcv2",
      "displayName": "Anonymous 901",
      "photoURL": null,
      "email": null,
      "emailVerified": false,
      "identifierNumber": null,
      "isAnonymous": true,
      "providerData": [

      ],
      "apiKey": "MyApiKeyString",
      "appName": "MyAppName",
      "authDomain": "my.auth.domain",
      "stsTokenManager": {
        "apiKey": "MyApiKeyString",
        "refreshToken": "refreshTokenString",
        "accessToken": "accessTokenString",
        "expirationTime": 1532451863076
      },
      "redirectEventId": null
    }

I encode the above anonymous user data and include it in the service url. ( http://myserviceurl?userdata=encodedUserData )

Inside the system receives that url, firebase creates a user object with that user data contained in the url.

The purpose of this url is to use specific user's information in any browser.

However, when I call that service url, sometimes system creates user object well, sometimes got error - 400 Bad request errors with https://www.googleapis.com/identitytoolkit/v3/relyingparty/setAccountInfo?key=MyApiKeyString

And error data is below,

    {
      "error": {
        "code": 400,
        "message": "TOKEN_EXPIRED",
        "errors": [
          {
            "message": "TOKEN_EXPIRED",
            "domain": "global",
            "reason": "invalid"
          }
        ]
      }
    }

Few hours later it works well, I changed nothing though.

I could not find the exact error point, but I suspect error occurs while observing authentication state or before this step.

Here is code snipets

    @bind
    private makeUserLoadingPromise(): Promise<void> {
      let unSubscribe: () => void;
      return new Promise<void>((resolve, _reject) => {
        const onInitialized = this.makeOnInitializedAuthStateChanged(resolve);
        unSubscribe = this.auth.onAuthStateChanged(onInitialized);
      }).then(() => {
        unSubscribe();
        this.auth.onAuthStateChanged(this.onAuthStateChanged);
      });
    }

    @bind
    private makeOnInitializedAuthStateChanged(resolve: () => void) {
      return (user: firebase.User | null) => {
        this.user = user;
        resolve();
      };
    }

    @bind
    private onAuthStateChanged(user: firebase.User | null) {
      this.user = user;
    }

Or maybe it relates with expirationTime?

I couldn't find any hints about this situation.

Any advice would be appreciated.

KENdi
  • 7,576
  • 2
  • 16
  • 31
Woojin Jeong
  • 199
  • 1
  • 3
  • 11

1 Answers1

0

It is not clear what you are doing, but it appears that you are using the API incorrectly and insecurely. The plain user object contains a refresh token that is indefinite. Passing it around via URL is a really bad idea.

First don't rely on internal implementations, it is subject to change. To get the user's information on your backend, the right way to do it, is to get the user's ID token using officially supported API, eg user.getIdToken(), then pass it to your server.

On your server, you verify it via the Firebase Admin SDK: admin.auth().verifyIdToken(idToken). Then you know this is a real authenticated user. If you need the full user info, you can then look it up using the decoded user id in the token: admin.auth().getUser(decodedIdToken.sub).

bojeil
  • 29,642
  • 4
  • 69
  • 76