17

I am using anonymous auth to allow my users to use the app without logging in. However, Firebase seems to persist these anonymous user IDs indefinitely. Is there a way to automatically purge these or set some sort of expiration rule? I don't want these one-time use IDs to live forever and clutter the actual user data from providers.

adolfosrs
  • 9,286
  • 5
  • 39
  • 67
rex
  • 3,133
  • 6
  • 35
  • 62
  • http://stackoverflow.com/a/39909854/2254886 I found a solution that might make your life easier. – Ryan Oct 07 '16 at 12:59
  • lol good one mate – rex Oct 07 '16 at 13:49
  • 1
    I posted it as an answer to your question here, but the community deleted it because I put it in multiple spots lol. Probably because I posted the answer word for word on more than one post. I just changed the answer to be different and reposted. It might get deleted but I am not sure of the rule on this since it is now technically a different answer. I'm not trying to trick the system or anything, I just want people to see the solution. – Ryan Oct 07 '16 at 15:56

5 Answers5

7

Unfortunately this is a "memory leak" (user leak?) Since there is no reasonable way to force an anonymous user to convert, these anonymous user ids will soon become zombies that serve no particular purpose (that I can think of). Furthermore, a single real user might (forgetfully) sign in as an anonymous user, yet again, after already linking their email to a previous incarnation, and then get frustrated when trying to link to their email. Overall, I find the current anonymous user implementation impractical, or at very least far from ideal.

For now I am planning to have an email address which is random but unique for a given user/device for signing in anonymous users, instead of using the builtin anonymous signin (which is disabled). In my opinion there needs to be a setting to tell Firebase to delete an anonymous user id upon sign out (they are useless at that point anyway) and/or after a predefined amount of time. In addition, it might be useful to be able to sign in again, with the same anonymous user id, until the expiration time (like by saving a token/etc.) Lastly, an attempt to link an email that is already in use should just merge the anonymous user id with the existing email/password user id through a verification step.

Kaamel
  • 1,852
  • 1
  • 19
  • 25
3

Somehow, there is a way of delete old anonymous users. I do it with a AppEngine cronjob that runs hourly.

But before you do that you have to define, what a anonymous user is. My users have to validate their email address and therefore I declare all users who are not validated to be anonymously after 90 days.

With the PubSub tick I then collect all users and delete them, here you've got a sample:

export const removeOldUsers = functions.pubsub.topic( "hourly-tick" ).onPublish( event => {
    function getInactiveUsers( users: Array<UserRecord> = [], nextPageToken?: string ) {
        let userList = users;

        return admin.auth().listUsers( 1000, nextPageToken ).then( ( result: any ) => {
            console.log( `Found ${result.users.length} users` );

            const inactiveUsers = result.users.filter( ( user ) => {
                return moment( user.metadata.lastSignInTime ).isBefore( moment().subtract( 90, "days" ) ) && !user.emailVerified;
            } );

            console.log( `Found ${inactiveUsers.length} inactive users` );

            // Concat with list of previously found inactive users if there was more than 1000 users.
            userList = userList.concat( inactiveUsers );

            // If there are more users to fetch we fetch them.
            if ( result.pageToken) {
                return getInactiveUsers( userList, result.pageToken );
            }

            return userList;
        } );
    }

    return new Promise( ( resolve ) => {
        console.info( `Start deleting user accounts` );

        getInactiveUsers().then( ( users ) => {
            resolve( users );
        } );
    } ).then( ( users: Array<UserRecord> ) => {
        console.info( `Start deleting ${users.length} user accounts` );

        return Promise.map( users, ( user ) => {
            return admin.auth().deleteUser( user.uid ).then( () => {
                console.log( "Deleted user account", user.uid, "because of inactivity" );
            } ).catch( ( error ) => {
                console.error( "Deletion of inactive user account", user.uid, "failed:", error );
            } );
        }, { concurrency: 3 } );
    } ).then( () => {
        console.info( `Done deleting user accounts` );
    } );
} );

Here I just pushed my class to npmjs @beyond-agentur-ug/firebase-delete-inactive-users

Kersten
  • 1,662
  • 3
  • 16
  • 28
  • Hi Kersten Are you able to provide detailed instructions on how to use your npm class @beyond-agentur-ug/firebase-delete-inactive-users I issue the following commands inside Functions directory: npm i --save @beyond-agentur-ug/firebase-delete-inactive-users I added the commands in index.js export const removeOldUsers = functions.pubsub.topic( "hourly-tick" ).onPublish( event => { const inactiveUsers = new InactiveUsers(); return inactiveUsers.delete().then( ( deletedUsers ) => { console.log( `Deleted ${deletedUsers.length} inactive users` ); } ); } ); – LiveRock Feb 07 '19 at 14:20
  • But when I tried to upload, I get the errors: export const removeOldUsers = functions.pubsub.topic( "hourly-tick" ).onPublish( event => { ^^^^^^ SyntaxError: Unexpected token export at createScript (vm.js:80:10) at Object.runInThisContext (vm.js:139:10) at Module._compile (module.js:599:28) – LiveRock Feb 07 '19 at 14:22
  • Hi @LiveRock looks like an JS error. Could you try the following? module.exports.removeOldUsers = functions.... – Kersten Feb 07 '19 at 20:57
  • Sorry, I am not sure what you mean by module.exports.removeOldUsers = I am new to this, so is it suppose to be in the index.js? Do you have a step-by-step tutorial for newbie? – LiveRock Feb 15 '19 at 04:15
  • You are using JS instead of TS for the cloud functions. You should then use this code: ```module.exports.removeOldUsers = functions.pubsub.topic( "hourly-tick" ).onPublish( event => { const inactiveUsers = new InactiveUsers(); return inactiveUsers.delete().then( ( deletedUsers ) => { console.log( `Deleted ${deletedUsers.length} inactive users` ); } ); } );``` – Kersten Feb 15 '19 at 07:52
2

There is no way to bulk-delete, however, the following trick worked for me:

I used Macro Recorder and it worked like a charm. Just recorded a few iterations in the console of me deleting users, set it to repeat 500 times and walked away.

Ryan
  • 1,988
  • 4
  • 21
  • 34
2

You can use Firebase's admin API to delete users programatically. You'll need to store a user list in your database as Firebase doesn't provide a query for that.

nloewen
  • 1,279
  • 11
  • 18
0

Anonymous users can be a starting point before you upgrade them to a non anonymous user (think of an e-commerce site where an anonymous user adds stuff into his cart and then on checkout, upgrades to a Google or email/password user; in this case you probably do not want to lose the user's cart). As explained, this could be useful if you want to persist data from an anonymous user to an upgraded user. If you wish to purge anonymous users, there is no automated way to do so. However as soon as you either sign out the anonymous user or sign in a non anonymous user, the state of the anonymous user will be lost.

bojeil
  • 29,642
  • 4
  • 69
  • 76