I am developing a web app using Angular and Firebase through angularfire. I have a service that handles the authentication process for the users called HandleAuthService. I would like to allow users to upload a profile picture of themselves. I am storing the profile picture within the Firebase storage under a unique file path. The file path where their image is stored is under: UID/displayPicture/.
When the user uploads a picture, it appears in the Firebase storage under their unique path. However, when I attempt to download the picture so that it may be displayed, I am running into a 403 error where it claims that the user does not have permission to read or write.
The Error
ERROR FirebaseError: Firebase Storage: User does not have permission to access 'USER_ID/displayPicture.jpg'. (storage/unauthorized)
{
"error": {
"code": 403,
"message": "Permission denied. Please enable Firebase Storage for your bucket by visiting
the Storage tab in the Firebase Console and ensure
that you have sufficient permission to
properly provision resources."
}
}
Here are my Storage Rules (I have not changed them):
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
I tried removing the if request.auth !=null;
but it still produced the same error.
Here are snippets of the HandleAuthService code:
constructor(private fireAuth: AngularFireAuth, private ngZone: NgZone, private router: Router) {
//I create an observable for other components to subscribe to in order to get
//key information such as the uid
this.currentUser$ = this.fireAuth.authState.pipe(
take(1),
map(user => {
if (user) {
return {uid: user.uid, email:user.email, displayName:user.displayName}
} else {
return null;
}
})
);
}
//to login at the very beginning, the users may do so through google
googleLogin(provider:any) {
return this.fireAuth.signInWithPopup(provider)
.then((result)=> {
this.ngZone.run(() => {
this.router.navigate(['data']);
});
this.setUser(result.user);
})
.catch((error) => {
console.log(error);
})
}
Then, in each component, I subscribe to this.currentUser$
in order to access the user's authState data. This has been working for all other components.
In the component where I display the profile picture, picture.component.ts:
constructor(private handleAuth:HandleAuthService, private storage: AngularFireStorage, private crowboxService:CrowboxdbService) { }
//in this function I try to get the profile picture from the Firebase Storage
ngOnInit(): void {
//Subscribe to the user auth state observable and wait
//to get the UID to proceed - then get the user's profile picture to be displayed
this.handleAuth.currentUser$
.subscribe(user => {
this.userId = user.uid;
this.getProfilePicture();
});
}
//the function to upload the profile picture - invoked by the click of a button
uploadFile(event:any) {
const file = event.target.files[0];
this.filePath = `${this.userId}/displayPicture`;
const task = this.storage.upload(this.filePath, file);
}
//the function to get the profile picture
getProfilePicture() {
const ref = this.storage.ref(`${this.userId}/displayPicture.jpg`);
this.profileUrl = ref.getDownloadURL();
}
The HTML view of the picture.component.ts:
<div id="imageContainer">
IMAGE GOES HERE
<img alt="Profile Picture" [src]="profileUrl | async" />
<div>
<input type="file" (change)="uploadFile($event)">
</div>
</div>
If the user is able to upload the picture, why can I not retrieve the image? How can I ensure that the user is logged in?
I would also like to add that I have set up route guards as well:
const routes: Routes = [
{ path:'', redirectTo:'/home', pathMatch:'full' },
{ path:'home', component:HomeComponent},
{ path:'data', component:DataComponent, canActivate: [AngularFireAuthGuard] },
{ path:'troubleshoot', component:TroubleshootComponent, canActivate: [AngularFireAuthGuard] },
{ path:'profile', component:ProfileComponent, canActivate: [AngularFireAuthGuard] },
];