firebase / firebaseui-web

FirebaseUI is an open-source JavaScript library for Web that provides simple, customizable UI bindings on top of Firebase SDKs to eliminate boilerplate code and promote best practices.
https://firebase.google.com/
Apache License 2.0
4.58k stars 1.06k forks source link

Duplicate credential received. Please try again with a new credential. #750

Open braadworst opened 4 years ago

braadworst commented 4 years ago

PS, just tried Firefox (80.0.1 ), Never logged in there, never use firefox, so first time login. Message appears straight away

When I login to my app I often (randomly) get the message "Duplicate credential received. Please try again with a new credential." This will go away when clearing the cache, but when I sign out of my single page app and login afterwards, I will always get this message and am unable to login. I've been Googling this issue but so far only found people with this issue when trying to work with Apple credentials and not for the WEB SDK. I am only using Google and Email (see code below). Now I assume i've must have made and error but I am unable to find what is wrong in my code.

The request to: https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyAssertion?key=xxxxx Response code: 400 Response:

{
  "error": {
    "code": 400,
    "message": "MISSING_OR_INVALID_NONCE : Duplicate credential received. Please try again with a new credential.",
    "errors": [
      {
        "message": "MISSING_OR_INVALID_NONCE : Duplicate credential received. Please try again with a new credential.",
        "domain": "global",
        "reason": "invalid"
      }
    ]
  }
}

Steps to reproduce:

Log successfully into the app, logout and login again. 9 out of 10 times I will get aforementioned message.

const debug = require('debug')(__filename.split('/').slice(-3).join('/'));
const application = require('firebase/app');
const firebaseui = require('firebaseui');
require('firebase/storage');

module.exports = (update, settings) => {

  // Settings contains
  // const settings = {
  //   apiKey : 'xxxxx',
  //   authDomain : 'xxxxx.firebaseapp.com',
  //   clientId : 'xxxxx',
  //   authenticateUrl : 'xxxxxx',
  // };

  application.initializeApp(settings);
  const ui = new firebaseui.auth.AuthUI(application.auth());

  // Firebase ui options
  const options = {
    credentialHelper : firebaseui.auth.CredentialHelper.NONE,
    signInSuccessUrl : settings.authenticateUrl, // I don't even think I need this one
    callbacks : {
      signInSuccessWithAuthResult : () => {
        return false;
      }
    },
    signInOptions: [{
      provider: application.auth.EmailAuthProvider.PROVIDER_ID,
      requireDisplayName: false
    }, {
      provider: application.auth.GoogleAuthProvider.PROVIDER_ID,
      authMethod: 'https://accounts.google.com',
      clientId: settings.clientId,
      customParameters: {
        prompt: 'select_account'
      }
    }],
  };

  const exposed = {
    application,
    // Select is the DOM element to render the ui in
    loginForm(selector) {
      debug('Adding firebaseui');
      ui.start(selector, options);
    },
    // This is called later in the code when a logout button is pressed,
    // there is even a reload
    signOut : async function() {
      await application.auth().signOut();
      ui.reset();
      window.location.reload();
    }
  };
  return exposed;
};

image

bojeil-google commented 4 years ago

Hey @braadworst for the life of me, I can't duplicate this. I tried it on multiple apps (including the firebaseui demo app). I tried in various browsers including Firefox. Can you provide an MCVE to help us recreate it?

braadworst commented 4 years ago

Hi @bojeil-google thanks for your response, and I am fully aware that this is a edge case and I am not sure if an example, although I tried but failed to connect to accounts.google.com would make this any clearer. While I was thinking about this a bit more myself I did remember that during a cleanup of credentials (stupid idea) I'd removed the original Firebase ones and added some new ones. on cloud.google.com. Meaning I've generated a new OAuth 2.0 client id and hooked that in via clientId in application.auth.GoogleAuthProvider.PROVIDER_ID.

There is also authMethod although I don't know where I got that information from, surely not on the firebasui page. I have removed that and seems to be make no difference whatsoever. Which leaves me with the following settings:

const options = {
    credentialHelper : firebaseui.auth.CredentialHelper.NONE,
    callbacks : {
      signInSuccessWithAuthResult : () => {
        return false;
      }
    },
    signInOptions: [{
      provider: application.auth.EmailAuthProvider.PROVIDER_ID,
      requireDisplayName: false
    }, {
      provider: application.auth.GoogleAuthProvider.PROVIDER_ID,
      clientId: settings.clientId,
    }],
  };

This has exactly the same result, duplicate credentials, etc..., but I think the issues might be in the fact my credentials were removed. As login works most of the time I don't expect there to be anything wrong with the code itself nor the libraries.

bojeil-google commented 4 years ago

authMethod used to be used by the legacy version of one-tap SDK. If you are using the latest version of firebaseui, you don't need to include it anymore. In fact, it looks like you are not using any credential helper, so you can also remove clientId.

I don't see anything unusual in your snippet. One reason this error may happen is that the request to complete the sign in is being sent more than once. This should not happen. It may be something unusual going on in your browser or device (maybe some chrome extension doing weird stuff, etc). Try to check the browser network console on the requests being sent to test my theory. I would also try the flow out on a different browser or device.