Search code examples
angularfirebase-authenticationprogressive-web-appsangularfire

Offline Authentication


In an Angular PWA I'm using firebase security rules to restrict access to nearly all of my data to authenticated users. In an auth-guard I check if the user is authenticated and if not, the user is signed in anonymously (signInAnonymously()) so I ensure that I have a user-id that I can use to flag objects that the user creates. This flag is necessary so I know if the user is the creator and if he is allowed to delete his own comment.

This all works fine - also when I lose connectivity. But if I close the PWA, enable flight-mode and then open the PWA (without connectivity) - signInAnonymously returns an error (because of course the user cannot be signed in on the server). I on the other hand expect to either get the user from LocalStorage / IndexedDb returned or that after opening the app the onAuthStateChanged-Event fires with the locally stored user.

None of this happens (although I see, that the user is in fact stored in indexeddb by Firebase). Docs: Authentication State Persistence also state, that

For a web application, the default behavior is to persist a user's session even after the user closes the browser.

What am I missing? What should I do differently to achieve my goal of:

The PWA should behave like a native app. If I have no connectivity, as I use enableIndexedDbPersistence() for Firestore, the app should load whichever data is in cache and as soon as connectivity is restored, the app should seamlessly use online data.

Edit: This is the code of my AuthService:

@Injectable()
export class AuthService {
    private authState: User = null;

    private _isAuthenticated = new ReplaySubject<boolean>();
    public isAuthenticated$ = this._isAuthenticated.asObservable();

    public get userId() {
        if (this.authState)
            return this.authState.uid;

        throw "No User-Id!";
    }

    constructor(private fAuth: Auth) {
        onAuthStateChanged(this.fAuth, async (user) => {
            AdminConsoleDialogComponent.addLogLine(`onAuthStateChanged: ${user?.uid}`);

            this.authState = user;
            try {
                if (!this.authState) {
                    this.authState = (await signInAnonymously(this.fAuth)).user;
                }
            } catch (e) {
                AdminConsoleDialogComponent.addLogLine(`onAuthStateChanged Error: ${e}`);
                console.error(e);
            } finally {
                this._isAuthenticated.next(this.authState != null);
            }
        });
    }
}

Output as follows (from addLogLine-Calls):

  • Open the PWA with connectivity:

onAuthStateChanged: tszjaBTtltVo9XV5B4kHAHnNNt22

  • Close/Kill the PWA, go into Flight-Mode and reopen PWA without connectivity:

onAuthStateChanged: undefined

onAuthStateChanged Error: FirebaseError: Firebase: Error (auth/internal-error).

  • Close/Kill the PWA, go out of Flight-Mode and reopen PWA with connectivity:

onAuthStateChanged: undefined

onAuthStateChanged: D54ENbliWVhyYc9izXjFbtzXhUG2

I just realized that I even get a new anonymous user.


Solution

  • I cannot pinpoint it exactly but after deleting node_modules/package-lock.json (and with this updating @angular/* packages from 12.2.5 to 15.2.7) it seems to work fine. There must have been a bugfix recently.