39

I have created some users using firebase.auth().signInWithEmailAndPassword and would like to stop signUp now, but keep signIn working. I tried some rules on users and even to stop writing to firebase at all. However registration was still possible. Disabling Email/Password within console disables login too.

{
  "rules": {
        ".read": true,
        ".write": false,
      }
}

Any ideas how to apply security rules to users in Firebase 3?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
ZimZim
  • 399
  • 1
  • 3
  • 3

7 Answers7

67

Firebase explicitly separates authentication (signing in to the app) from authorization (accessing database or storage resources from the app).

You cannot disable sign-up without disabling sign-in for all users, which is not what you want.

In a typical scenario, developer will start securing database/file access based on the authenticated user. See the relevant section in the docs for database security and storage security.

If your use-case is that you only want specific users to have access, you'll probably want to implement a whitelist: a list of users that are allowed to access the data.

You can do that in your security rules:

{
  "rules": {
        ".read": "auth.uid == '123abc' || auth.uid == 'def456'",
        ".write": false,
      }
}

Or (better) by putting the list of allowed uids in your database and referring to that from your security rules:

"allowedUids": {
    "123abc": true,
    "def456": true
}

And then:

{
  "rules": {
        ".read": "root.child('allowedUids').child(auth.uid).exists()",
        ".write": false,
      }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you for the fast response. It solves my problem. – ZimZim Jul 13 '16 at 17:07
  • 1
    Nice workaround. Still wish they add in this function. Gonna be a big mess if we have like 200 tester and have to add them manually like this – Tree Nguyen Jun 15 '17 at 00:17
  • 1
    I take this approach for most of our big demo projects and it's actually quite scalable. If adding the UIDs in the database console becomes too much work, create a simple web admin page that adds them. And then of course only allow admins (which you track on a similar whitelist) to write to this location. – Frank van Puffelen Jun 15 '17 at 03:27
  • 3
    thanks! It still seems like allowing you to disable new registrations is a reasonable feature, but oh well – SeanMC Mar 27 '18 at 18:27
  • A probably more efficient way is to use custom claims - this way you keep the roles in the users instead of adding the permitted user IDs to the database https://firebase.google.com/docs/auth/admin/custom-claims – Gabriel Apr 10 '18 at 08:46
  • Custom claims would indeed another way to accomplish this (and more) scenario(s) nowadays (they didn't exist yet when I wrote this answer). For an example of using custom claims, see this answer: https://stackoverflow.com/a/46457457/209103 – Frank van Puffelen Apr 10 '18 at 13:16
  • Hi FrankvanPuffelen, would disabling new user creation from Cloud Identity based on @Dharmaraj answer is a legit solution? – M. A. Mar 11 '22 at 11:05
  • I haven't tried it myself, but yes that might be an valid option these days (though it didn't exist when I wrote my answer). Keep in mind though that you'll be on a different pricing scheme when using Google Cloud Identity Platform. – Frank van Puffelen Mar 11 '22 at 15:20
25

Well it's been days since the question was asked but maybe this could help for those who are still wondering for the answer, we still can't simply disable the new account creation but we can do using Firebase functions:

Here is the workaround for auto-disable new users using cloud functions.

const functions = require('firebase-functions');
const admin = require('firebase-admin');
    
exports.blockSignup = functions.auth.user().onCreate(event => {
  return admin.auth()
    .updateUser(event.uid, {disabled: true})
    .then(userRecord => console.log(`Auto blocked user: ${userRecord.toJSON()}`))
    .catch(error => console.log(`Error auto blocking: ${error}`));
});

Remembering that this function is fired when you create users using the Firebase web console, or by 3rd parties. So you have to create, await the function, then enable the user.

Muhammad Ovi
  • 1,053
  • 14
  • 26
  • This helped me :) – Jeff Adam Apr 10 '19 at 22:13
  • 2
    It works. However it allows to do a first login using for example google provider and signInWithPopup() method. It looks the account disable function comes afterwards. Although, this still can be overcome because after signInWithPopup() call we have a promise of UserCredential type which has an additionalUserInfo.isNewUser fields and we can logout the user immediately. – Lukasz Frankowski Feb 12 '20 at 06:19
  • this should be something simple to do. A simple button on the console to enable or disable new user creation! – savram Oct 21 '20 at 02:37
  • Need to add `const functions = require('firebase-functions');` as well. – savram Oct 21 '20 at 02:54
  • How do I deal with this error: firebase_config and gcloud_project environment variables are missing – savram Oct 21 '20 at 03:23
  • nvm you need to use "firebase deploy --only functions" and not try to run it with node. – savram Oct 21 '20 at 05:05
24

Update: Sign-ups and account deletion can now be disabled right from Firebase console.

enter image description here


If you enable the Cloud Identity API then you can disable sign up and delete account actions for users and limit them to the Admin SDK.

enter image description here

You can visit https://console.cloud.google.com/customer-identity/settings to disable them.

You might get this notice while enabling the API on an existing project:

enter image description here

Once disabled, using the createUserWithEmailAndPassword method returned a 400 error:

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

Do note that you can still use Admin SDK to create new users.

The documentation for the same can be found here

Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
5

If you want to remove the signup option from FirebaseUI in Android app than you have to add the following provider:

new AuthUI.IdpConfig.EmailBuilder().setAllowNewAccounts(false).build());

and the function will look like the following:

private void FireBaseLoginUI() {
        List<AuthUI.IdpConfig> providers = Collections.singletonList(
                new AuthUI.IdpConfig.EmailBuilder().setAllowNewAccounts(false).build());

        startActivityForResult(
                AuthUI.getInstance()
                        .createSignInIntentBuilder()
                        .setAvailableProviders(providers)
                        .setLogo(R.drawable.app_logo)
                        .setTheme(R.style.AuthUITheme)
                        .build(),
                RC_SIGN_IN);
}
ThexXTURBOXx
  • 381
  • 1
  • 3
  • 16
  • This is the correct answer, but if you implement it like you described, it gives you the following error : java.lang.IllegalArgumentException: Each provider can only be set once. password was set twice So to get rid of it you need to use just on of the Email providers like this : List providers = Arrays.asList( new AuthUI.IdpConfig.EmailBuilder().setAllowNewAccounts(false).build()); – Brahim Belghmi Sep 08 '20 at 12:23
1

I use this:

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
const admin = require("firebase-admin");

admin.initializeApp();

exports.blockSignup = functions.auth.user().onCreate(event => {
    if (process.env['allowCreation'] === "false")
        return admin.auth().deleteUser(event.uid);
    else return Promise.resolve("letUserCreate");
});

After creating the users I want, go to https://console.cloud.google.com/functions and change the environment variable to false, redeploy, done.

Alternatively you could add the user to firestore and only allow to sign up if the user exists on firestore. Like so:

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
const admin = require("firebase-admin");

admin.initializeApp();

exports.blockSignup = functions.auth.user().onCreate(user => {
    return new Promise((resolve, reject) => {
        admin.firestore().collection('users').doc(user.email).get().then(doc => {
            if (doc.exists) {
                resolve("letUserCreate");
            }
            else {
                reject(admin.auth().deleteUser(user.uid))
            }
        }).catch(reason => {
            console.error(reason)
            reject(admin.auth().deleteUser(user.uid))
        })
    });
});
savram
  • 500
  • 4
  • 18
1

I don't know if Google added this option since the other answers were posted, or if it's just so buried in the documentation that most people don't know about it, but there is a disableSignup option for FirebaseUI Auth. https://github.com/firebase/firebaseui-web/blob/master/README.md#configure-email-provider

The usage looks something like this:

var uiConfig = {
    callbacks: {
        signInSuccessWithAuthResult: (authResult, redirectUrl) => {
            // User successfully signed in.
            // Call your success handler.
            handleLoginSuccess(authResult.user)
            .then( () => {
                return true;
            })
            .catch( err => {
                return false;
            });
        },
        signInFailure: (err) => {
            console.error(`[signInFailure] ERROR: ${err.message}`);
        },
        uiShown: () => {
            // The widget is rendered.
            // Hide the loader.
            document.getElementById('loader').style.display = 'none';
        }
    },
    signInSuccessUrl: "/",
    signInOptions: [
        {
            provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
            requireDisplayName: true,
            disableSignUp: {
                status: true,
                adminEmail: 'help@example.com',
                helpLink: 'https://example.com/login-help'
            }
        }
    ]
};

ui.start("#firebaseui-auth-container", uiConfig);

Hope this helps future searchers who end up here.

TheRealMikeD
  • 166
  • 1
  • 8
  • I have been looking for this option for 2 years now! So many users accidentally created new accounts just because they spelled their email wrong ... – lhermann Feb 12 '23 at 06:35
0

As mentioned above, it's important to differentiate between authentication and authorization. Therefore, this can also be done slightly off of Firebase. If you are building a web app for example and you want to allow access to a given page to a specific list of users then you can handle it within your web app.

This example is for executing an onCall firebase function. It only executes if the user UID is 12345

exports.ILoveConsole = functions.https.onCall((message, context) => {

//message is just a text
//context hold the user auth information

    'use strict';
        if (context && context.auth && context.auth.uid) {
            if(context.auth.uid === "12345"){
                console.log("I love security");
            }
            else{
                console.log("You are authenticated but not authorized");
            }
        }
        else {
            console.log("You are neither authenticated nor authorized");
        }
});

Note: If you want to do a list of users, you can do a for loop function to check the array of your authorized users in browser or call a firebase function. They both should run before loading the page

s.khetcho
  • 113
  • 7