Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Handle Anonymous Users in Firebase Auth

To handle anonymous users in Firebase, call signInAnonymously() to create a temporary account with a unique UID. The user can interact with your app immediately without providing credentials. When they are ready to create a permanent account, use linkWithCredential() or linkWithPopup() to upgrade the anonymous account while preserving their UID and all associated data. Always implement account linking to prevent data loss when anonymous users convert.

What you'll learn

  • How to create anonymous accounts with signInAnonymously()
  • How to detect and identify anonymous users in your app
  • How to link anonymous accounts to permanent credentials
  • How to write security rules that work with anonymous users
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read10-15 minFirebase (Spark and Blaze plans), firebase v9+ modular SDKMarch 2026RapidDev Engineering Team
TL;DR

To handle anonymous users in Firebase, call signInAnonymously() to create a temporary account with a unique UID. The user can interact with your app immediately without providing credentials. When they are ready to create a permanent account, use linkWithCredential() or linkWithPopup() to upgrade the anonymous account while preserving their UID and all associated data. Always implement account linking to prevent data loss when anonymous users convert.

Managing Anonymous Users in Firebase Auth

Anonymous authentication lets users try your app without creating an account upfront. Firebase assigns a real UID to anonymous users so they can save data, use Firestore, and interact with your app. When they decide to sign up, you link their anonymous account to a permanent provider, preserving their UID and all data. This tutorial covers the full lifecycle from anonymous sign-in to account conversion.

Prerequisites

  • A Firebase project with Authentication enabled
  • Anonymous sign-in provider enabled in Firebase Console > Authentication > Sign-in method
  • The firebase npm package installed (v9 or later)
  • Basic knowledge of JavaScript/TypeScript

Step-by-step guide

1

Sign in anonymously

Call signInAnonymously() to create a temporary user account. Firebase generates a unique UID for this anonymous user, and the onAuthStateChanged listener fires with the user object. The user can now read and write to Firestore, Storage, and other Firebase services using this UID. Anonymous sessions persist across page refreshes by default.

typescript
1import { getAuth, signInAnonymously, onAuthStateChanged } from 'firebase/auth'
2
3const auth = getAuth()
4
5async function signInAsGuest() {
6 try {
7 const userCredential = await signInAnonymously(auth)
8 const user = userCredential.user
9 console.log('Anonymous UID:', user.uid)
10 console.log('Is anonymous:', user.isAnonymous) // true
11 return user
12 } catch (error: any) {
13 console.error('Anonymous sign-in failed:', error.code)
14 throw error
15 }
16}

Expected result: A new anonymous user is created with a unique UID, and isAnonymous is true.

2

Detect whether the current user is anonymous

Use the isAnonymous property on the user object to determine if the current user signed in anonymously. This lets you show different UI states: anonymous users see a prompt to create an account, while permanent users see their profile. Check this property in your auth state listener or when rendering protected components.

typescript
1import { getAuth, onAuthStateChanged } from 'firebase/auth'
2import { useEffect, useState } from 'react'
3
4function useAuthState() {
5 const [user, setUser] = useState<any>(null)
6 const [isAnonymous, setIsAnonymous] = useState(false)
7 const [loading, setLoading] = useState(true)
8
9 useEffect(() => {
10 const auth = getAuth()
11 const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
12 setUser(firebaseUser)
13 setIsAnonymous(firebaseUser?.isAnonymous ?? false)
14 setLoading(false)
15 })
16 return () => unsubscribe()
17 }, [])
18
19 return { user, isAnonymous, loading }
20}
21
22// Usage in a component:
23// const { user, isAnonymous } = useAuthState()
24// if (isAnonymous) show 'Create account' banner

Expected result: The hook correctly identifies anonymous users and updates when they convert to permanent accounts.

3

Link the anonymous account to email/password

When an anonymous user decides to create an account, use linkWithCredential() to upgrade their anonymous account to a permanent email/password account. This preserves the same UID so all their existing Firestore data, Storage files, and other references remain valid. The user does not need to sign out and sign back in.

typescript
1import { getAuth, EmailAuthProvider, linkWithCredential } from 'firebase/auth'
2
3const auth = getAuth()
4
5async function linkEmailPassword(email: string, password: string) {
6 const user = auth.currentUser
7 if (!user || !user.isAnonymous) {
8 throw new Error('No anonymous user to link')
9 }
10
11 const credential = EmailAuthProvider.credential(email, password)
12
13 try {
14 const result = await linkWithCredential(user, credential)
15 console.log('Account linked. UID preserved:', result.user.uid)
16 console.log('Is anonymous:', result.user.isAnonymous) // false
17 return result.user
18 } catch (error: any) {
19 if (error.code === 'auth/email-already-in-use') {
20 console.error('This email is already registered. Sign in instead.')
21 }
22 throw error
23 }
24}

Expected result: The anonymous account is upgraded to email/password with the same UID. isAnonymous becomes false.

4

Link the anonymous account to a social provider

You can also link anonymous accounts to social providers like Google or GitHub using linkWithPopup() or linkWithRedirect(). The process is similar to linkWithCredential but uses the provider's OAuth flow. After linking, the user has a permanent account and can sign in with the social provider in the future.

typescript
1import { getAuth, GoogleAuthProvider, linkWithPopup } from 'firebase/auth'
2
3const auth = getAuth()
4
5async function linkGoogleAccount() {
6 const user = auth.currentUser
7 if (!user || !user.isAnonymous) {
8 throw new Error('No anonymous user to link')
9 }
10
11 const provider = new GoogleAuthProvider()
12
13 try {
14 const result = await linkWithPopup(user, provider)
15 console.log('Google account linked. UID:', result.user.uid)
16 return result.user
17 } catch (error: any) {
18 if (error.code === 'auth/credential-already-in-use') {
19 console.error('This Google account is linked to another user.')
20 // Consider merging data from anonymous to existing account
21 }
22 throw error
23 }
24}

Expected result: The Google account is linked and the anonymous user becomes a permanent Google-authenticated user.

5

Write security rules that work with anonymous users

Anonymous users have a valid request.auth object with a real UID. Your security rules can grant different levels of access based on whether the user is anonymous. Use request.auth.token.firebase.sign_in_provider to check the authentication method. Grant anonymous users read access but require a permanent account for sensitive writes.

typescript
1// firestore.rules
2rules_version = '2';
3service cloud.firestore {
4 match /databases/{database}/documents {
5
6 // Public data: any authenticated user (including anonymous) can read
7 match /products/{productId} {
8 allow read: if request.auth != null;
9 allow write: if request.auth != null
10 && request.auth.token.firebase.sign_in_provider != 'anonymous';
11 }
12
13 // User data: scoped to their UID (works for both anonymous and permanent)
14 match /carts/{userId}/{document=**} {
15 allow read, write: if request.auth != null
16 && request.auth.uid == userId;
17 }
18
19 // Orders: require a permanent account
20 match /orders/{orderId} {
21 allow create: if request.auth != null
22 && request.auth.token.firebase.sign_in_provider != 'anonymous';
23 allow read: if request.auth != null
24 && request.auth.uid == resource.data.userId;
25 }
26 }
27}

Expected result: Anonymous users can browse and save cart items but must create a permanent account to place orders.

6

Clean up abandoned anonymous accounts

Anonymous accounts that are never linked accumulate in your auth user list. With Firebase Identity Platform (upgrade from standard Auth), anonymous accounts auto-delete after 30 days of inactivity. Without Identity Platform, write a scheduled Cloud Function using the Admin SDK to list and delete anonymous users that have not been active for a specified period.

typescript
1// Cloud Function to clean up old anonymous accounts
2import { onSchedule } from 'firebase-functions/v2/scheduler'
3import { getAuth } from 'firebase-admin/auth'
4import { initializeApp } from 'firebase-admin/app'
5
6initializeApp()
7
8export const cleanupAnonymousUsers = onSchedule('every day 02:00', async () => {
9 const auth = getAuth()
10 const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000 // 30 days
11
12 let nextPageToken: string | undefined
13 do {
14 const listResult = await auth.listUsers(1000, nextPageToken)
15 const toDelete = listResult.users
16 .filter(user => {
17 const isAnon = user.providerData.length === 0
18 const lastSignIn = new Date(user.metadata.lastSignInTime).getTime()
19 return isAnon && lastSignIn < cutoff
20 })
21 .map(user => user.uid)
22
23 if (toDelete.length > 0) {
24 await auth.deleteUsers(toDelete)
25 console.log(`Deleted ${toDelete.length} anonymous users`)
26 }
27
28 nextPageToken = listResult.pageToken
29 } while (nextPageToken)
30})

Expected result: Anonymous accounts older than 30 days are automatically deleted on a daily schedule.

Complete working example

anonymous-auth.ts
1import {
2 getAuth,
3 signInAnonymously,
4 onAuthStateChanged,
5 EmailAuthProvider,
6 GoogleAuthProvider,
7 linkWithCredential,
8 linkWithPopup,
9 User,
10} from 'firebase/auth'
11
12const auth = getAuth()
13
14export async function signInAsGuest(): Promise<User> {
15 const { user } = await signInAnonymously(auth)
16 return user
17}
18
19export function onAuthChange(
20 callback: (user: User | null, isAnonymous: boolean) => void
21): () => void {
22 return onAuthStateChanged(auth, (user) => {
23 callback(user, user?.isAnonymous ?? false)
24 })
25}
26
27export async function upgradeToEmail(
28 email: string,
29 password: string
30): Promise<User> {
31 const user = auth.currentUser
32 if (!user?.isAnonymous) throw new Error('Not an anonymous user')
33 const credential = EmailAuthProvider.credential(email, password)
34 const { user: linked } = await linkWithCredential(user, credential)
35 return linked
36}
37
38export async function upgradeToGoogle(): Promise<User> {
39 const user = auth.currentUser
40 if (!user?.isAnonymous) throw new Error('Not an anonymous user')
41 const provider = new GoogleAuthProvider()
42 const { user: linked } = await linkWithPopup(user, provider)
43 return linked
44}
45
46export function isGuest(): boolean {
47 return auth.currentUser?.isAnonymous ?? false
48}
49
50export function getCurrentUid(): string | null {
51 return auth.currentUser?.uid ?? null
52}

Common mistakes when handling Anonymous Users in Firebase Auth

Why it's a problem: Creating a new anonymous user on every page load instead of checking for an existing session

How to avoid: Always check onAuthStateChanged first. If a user (anonymous or permanent) already exists, do not call signInAnonymously again. Only sign in anonymously when currentUser is null.

Why it's a problem: Not handling auth/email-already-in-use when linking, losing the anonymous user's data

How to avoid: Catch this error and offer to sign in with the existing account instead. Manually merge the anonymous user's data (cart, preferences) into the existing account before deleting the anonymous UID's data.

Why it's a problem: Forgetting to enable Anonymous sign-in in the Firebase Console, getting auth/operation-not-allowed

How to avoid: Go to Firebase Console > Authentication > Sign-in method and enable the Anonymous provider.

Best practices

  • Check for an existing auth session before calling signInAnonymously to avoid creating duplicate accounts
  • Always offer account linking before any permanent action like checkout or data export
  • Handle auth/email-already-in-use and auth/credential-already-in-use errors with data merge logic
  • Use security rules to differentiate between anonymous and permanent users for sensitive operations
  • Clean up abandoned anonymous accounts with a scheduled Cloud Function or Identity Platform auto-delete
  • Store anonymous user data with their UID so it persists after account linking
  • Show a non-intrusive banner prompting anonymous users to create an account after they have engaged

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to let users try my app without signing up using Firebase anonymous auth. Show me how to sign in anonymously, detect anonymous users, and then link the anonymous account to email/password or Google when they decide to create a permanent account. Include Firestore security rules that differentiate between anonymous and permanent users.

Firebase Prompt

Implement Firebase anonymous authentication with account linking. Create functions for signInAnonymously, detecting isAnonymous, upgrading to email/password with linkWithCredential, and upgrading to Google with linkWithPopup. Include error handling for credential-already-in-use and Firestore security rules.

Frequently asked questions

Do anonymous users count toward Firebase Auth billing?

On the standard Firebase Auth plan, anonymous users are free and unlimited. On Identity Platform, anonymous users count toward the 50,000 free MAU limit, then cost approximately $0.0055 per additional MAU.

What happens to anonymous user data when they link to a permanent account?

The UID stays the same after linking, so all Firestore documents, Storage files, and other data keyed by UID remain accessible. No data migration is needed.

Can I sign in anonymously multiple times and get different UIDs?

If no user is currently signed in, each call to signInAnonymously creates a new user with a new UID. If an anonymous user is already signed in, the existing session is reused.

How do I delete an anonymous user's data when they sign out?

Store the UID before signing out, then use the Admin SDK or security rules to delete their Firestore and Storage data. You can also use a Cloud Function triggered by Auth user deletion.

Are anonymous accounts automatically deleted?

Only if you upgrade to Firebase Identity Platform, which auto-deletes anonymous accounts after 30 days of inactivity. On standard Auth, anonymous accounts persist until manually deleted.

Can RapidDev help implement anonymous-to-permanent account conversion flows?

Yes, RapidDev's engineering team can build complete guest-to-user conversion flows including anonymous auth, account linking, data merging, and cleanup of abandoned accounts.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.