I'm working in an Angular 10 project, I am also using firebase hosting and cloud firestore (for DB). I am using AngularFire in my project as well.
My project is already able to get documents from my firestore collection, and display them (also can edit, delete, and create them). I also set up authentication, where I use AngularFireAuth to sign in and sign out. I also have route guards to only allow users access to info after signing in.
I've discovered that Firestore also has rules, and that you should set them up to secure your collection. Currently, I basically have no rules (test mode), but I want to add a basic "only users can access anything" rule, but am running into an issue.
I think this is the issue, currently, after logging in my app will store the user in local storage. I think that I need to store this a different way so I am re-signed in from previously given creds instead of just checking if there local storage. I only get ERROR FirebaseError: Missing or insufficient permissions errors when my guard checks the local storage to ensure sign-in, if I sign-in first, I don't get the error.
So, how should I save user data so that I don't have to sign-in on every refresh, but that I can verify the auth to firebase? I know I could store the email/password to local storage and check to re sign-in, but that seems insecure to me.
I think the above is the issue, but not 100% sure.
This is my firestore rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
allow read, write: if request.auth != null
match /{document=**} {
allow read, write: if request.auth != null //should only allow users?
}
}
}
Here is my auth service (where I handle sign-in/sign-out and check if local storage has user.
export class AuthService {
constructor(private aFAuth: AngularFireAuth, public router: Router) {
//I honestly don't know if I need this
this.aFAuth.authState.subscribe((user) => {
if (user) {
localStorage.setItem('my-test-app-currentUser', JSON.stringify(user));
} else {
localStorage.setItem('my-test-app-currentUser', null);
}
});
}
async signIn(email: string, password: string) {
this.aFAuth
.signInWithEmailAndPassword(email, password).then((result) => {
localStorage.setItem('my-test-app-currentUser', JSON.stringify(result.user));
this.router.navigate(['']);
}).catch((error) => {
window.alert(error.message);
});
}
//this is the func that needs to change, if I have storage, I need to be able to sign-in with it again
isSignedIn(): boolean {
if (!localStorage.getItem('my-test-app-currentUser')) {
return false;
}
return true;
}
signOut() {
return this.aFAuth.signOut().then(() => {
localStorage.removeItem('my-test-app-currentUser');
window.alert('You have been signed-out');
});
}
}
Here my guard:
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// return true;
if (this.auth.isSignedIn()) {
return true;
} else {
this.router.navigate(["sign-in"]);
return false;
}
}
Any help would be appreciated.
Firebase already stores the user credentials in local storage, and automatically restores them when you reload the page.
Restoring them does require a check against the server though, so it happens asynchronously. For that reason, any code that depends on the user's authentication state should be inside the this.aFAuth.authState.subscribe handler, so that it runs whenever the authentication state changes.
So instead of handling the navigation when the signInWithEmailAndPassword call completes, which happens only when you actively sign the user in, the navigation should be in the auth listener, which runs both on active sign in and on a restore.
So something like:
export class AuthService {
constructor(private aFAuth: AngularFireAuth, public router: Router) {
//I honestly don't know if I need this
this.aFAuth.authState.subscribe((user) => {
if (user) {
localStorage.setItem('my-test-app-currentUser', JSON.stringify(user));
this.router.navigate(['']);
} else {
localStorage.setItem('my-test-app-currentUser', null);
}
});
}
async signIn(email: string, password: string) {
this.aFAuth
.signInWithEmailAndPassword(email, password).then((result) => {
window.alert(error.message);
});
}
...
In your canActivate you'll probably want to use the AngularFireAuthGuard. which ensures that unauthenticated users are not permitted to navigate to protected routes. I think this might replace your entire need for local storage.
Also see the AngularFire documentation on Getting started with Firebase Authentication and Route users with AngularFire guards.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With