Issue
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] },
];
Solution
The problem was with how the URL was being retrieved. Here is the correct method according to AngularFire Documentation:
The picture.component.ts
uploadFile(event:any) {
const file = event.target.files[0];
this.filePath = `/Users/${this.userId}/profilePicture`;
const fileRef = this.storage.ref(this.filePath);
const task = this.storage.upload(this.filePath, file);
this.$taskSub = task.snapshotChanges().pipe(
finalize(() => {
//Get the download url of the photo that has been stored
//in the storage
this.downloadURL = fileRef.getDownloadURL();
this.$downloadURLSub = this.downloadURL.subscribe(result => {
this.profileUrl = result;
})
}))
.subscribe();
}
In this code, I subscribe to the task's changes within the Firebase Project and retrieve the download URL from the task observable. This URL may now be used to retrieve the image, as shown below.
The picture.component.html
<img src="{{profileUrl}}" alt="Profile Picture" class="imageContainer">
Answered By - Hamza Qureshi
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.