Firebase Storage "Permission denied" error - reactjs

When I try to read my Firebase Storage data I'm getting the following error:
Uncaught (in promise) FirebaseError: Firebase Storage: An unknown error occurred, please check the error payload for server response. (storage/unknown)
{
"error": {
"code": 400,
"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."
}
}
But my rules are set to public:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
What am I missing? Here's my code:
export const getAll = async () => {
let list: Photo[] = [];
const imagesFolder = ref(storage, "images");
const photoList = await listAll(imagesFolder);
for(let i in photoList.items) {
let photoUrl = await getDownloadURL(photoList.items[i]);
list.push({
name: photoList.items[i].name,
url: photoUrl
});
}
return list;
}

Following the steps in this post fixed my issue:
This is due to a missing permission:
firebase-storage#system.gserviceaccount.com
Go to https://console.cloud.google.com
Select your project in the top blue bar
Scroll down the left menu and select "Cloud Storage"
Select all your buckets then click "ADD PRINCIPAL" on the right
Add "firebase-storage#system.gserviceaccount.com" and "Storage
Admin" as a role
Save it
https://newbedev.com/firebase-storage-security-rules-400-error-issue-permission-denied-could-not-access-bucket-xxxxx-appspot-com

Before you setting for storage admin, make sure you have activate your google cloud platform. Because it's can be the first cause, if you use firebase storage and you never use google cloud platform before

Related

Error while deleting S3 objects - likely an issue with credentials but could not locate the problem

So I have been following other Q&A on stackoverflow and AWS SDK docs but I still couldn't delete S3 files with the following code, it simply gives the following error Error TypeError: Cannot read properties of undefined (reading 'byteLength')
My code (s3Client.js):
import { S3Client } from "#aws-sdk/client-s3";
const REGION = `${process.env.S3_UPLOAD_REGION}`;
const creds = {
accessKeyId: process.env.S3_UPLOAD_KEY,
secretAccessKey: process.env.S3_UPLOAD_SECRET
};
// Create an Amazon S3 service client object.
const s3Client = new S3Client({
region: REGION,
credentials: creds
});
export { s3Client };
My nextJS component:
import { DeleteObjectCommand } from "#aws-sdk/client-s3";
import { s3Client } from "../lib/s3Client.js"
const bucketParams = { Bucket: "my bucket name...", Key: "my key..." };
const run = async () => {
try {
const data = await s3Client.send(new DeleteObjectCommand(bucketParams));
console.log("Success. Object deleted.", data);
return data; // For unit tests.
} catch (err) {
console.log("Error", err);
}
};
I understand from others that this seems like a credential issue but I still couldn't wrap my head around where the problem is.
Edit:
Solution to this problem -
Check the .env, depending on whether you are using React or NextJS, you will need to have either "REACT_PUBLIC" or "NEXT_PUBLIC" in order for the environment objects to be exposed via process.env.
Check your permission in your S3 bucket and set "AllowedOrigins" to include your localhost. I had mine set as "AllowedOrigins": "*" but it wasn't enough. So I have included http://localhost:3000 and it worked.
So it looks like that you're using keys credentials for this. To debug your problem the 1st thing you should do is to check the credentials outside code and SDK and make sure they're fine.
To do this, setup the credentials on CLI by setting environment variables.
export AWS_ACCESS_KEY_ID=<Your Key here>
export AWS_SECRET_ACCESS_KEY=<SECRET HERE>
export AWS_DEFAULT_REGION=<AWS REGION>
Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
After this run this command to verify credentials are setup correctly aws sts get-caller-identity
If they show you the account and user details. Then delete the file using CLI. If that works then it is confirmed that issue is not with the credentials and with code. If not, it will point you in the right direction where the issue is.

How to pass uid as a parameter in firebase? React+Firebase

I am using react and have logged in user using firebase auth and storing the uid app context.
Security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if
request.auth!=null;
}
}
}
I want to keep this security rules.
I want to make request with uid. How do i do that?
This way gives error.
await db.collection('users').doc(uuid).set({name:name});
Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
How do i pass uid as parameter to each request while writing to the database?
It's not clear how you initialize uuid right now, but it should be:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
var uuid = user.uid;
await db.collection('users').doc(uuid).set({name:name});
} else {
// User is signed out
// ...
}
});
This snippet comes pretty directly from the Firebase documentation on getting the current user.

Getting Error while uploading file to S3 with Federated Identity using aws-amplify in React

While the user with Facebook federated Identity trying to upload Image, I'm getting an error: AWSS3Provider - error uploading Error: "Request failed with status code 403"
Status Code: 403 Forbidden
Noticed that URL in request, while user authenticated with Federated Identity (Facebook), looks:
Request URL: https://my-gallery-api-dev-photorepos3bucket-XXXX.s3.us-east-2.amazonaws.com/private/undefined/1587639369473-image.jpg?x-id=PutObject
The folder where the uploaded image will be placed is 'undefined' instead of being a valid user identity like for users authenticated with from AWS UserPool, see:
Request URL: https://my-gallery-api-dev-photorepos3bucket-XXXX.s3.us-east-2.amazonaws.com/private/us-east-2%3Aa2991437-264a-4652-a239-XXXXXXXXXXXX/1587636945392-image.jpg?x-id=PutObject
For Authentication and upload I'am using React aws dependency "aws-amplify": "^3.0.8"
Facebook Authentication (Facebook Button):
async handleResponse(data) {
console.log("FB Response data:", data);
const { userID, accessToken: token, expiresIn } = data;
const expires_at = expiresIn * 1000 + new Date().getTime();
const user = { userID };
this.setState({ isLoading: true });
console.log("User:", user);
try {
const response = await Auth.federatedSignIn(
"facebook",
{ token, expires_at },
user
);
this.setState({ isLoading: false });
console.log("federatedSignIn Response:", response);
this.props.onLogin(response);
} catch (e) {
this.setState({ isLoading: false })
console.log("federatedSignIn Exception:", e);
alert(e.message);
this.handleError(e);
}
}
Uploading:
import { Storage } from "aws-amplify";
export async function s3Upload(file) {
const filename = `${Date.now()}-${file.name}`;
const stored = await Storage.vault.put(filename, file, {
contentType: file.type
});
return stored.key;
}
const attachment = this.file
? await s3Upload(this.file)
: null;
I'm understand that rejection by S3 with 403, because of the IAM role, I have for authenticated users:
# IAM role used for authenticated users
CognitoAuthRole:
Type: AWS::IAM::Role
Properties:
Path: /
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Federated: 'cognito-identity.amazonaws.com'
Action:
- 'sts:AssumeRoleWithWebIdentity'
Condition:
StringEquals:
'cognito-identity.amazonaws.com:aud':
Ref: CognitoIdentityPool
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': authenticated
Policies:
- PolicyName: 'CognitoAuthorizedPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'mobileanalytics:PutEvents'
- 'cognito-sync:*'
- 'cognito-identity:*'
Resource: '*'
# Allow users to invoke our API
- Effect: 'Allow'
Action:
- 'execute-api:Invoke'
Resource:
Fn::Join:
- ''
-
- 'arn:aws:execute-api:'
- Ref: AWS::Region
- ':'
- Ref: AWS::AccountId
- ':'
- Ref: ApiGatewayRestApi
- '/*'
# Allow users to upload attachments to their
# folder inside our S3 bucket
- Effect: 'Allow'
Action:
- 's3:*'
Resource:
- Fn::Join:
- ''
-
- Fn::GetAtt: [PhotoRepoS3Bucket, Arn]
- '/private/**${cognito-identity.amazonaws.com:sub}/***'
- Fn::Join:
- ''
-
- Fn::GetAtt: [PhotoRepoS3Bucket, Arn]
- '/private/**${cognito-identity.amazonaws.com:sub}**'
It works fine for users registered in AWS User Pool (Email, Password), but for federated users, there is no record in AWS User Pool only in Federated Identities, so there will be no cognito-identity.amazonaws.com:sub found for those users and directory 'undefined' not falling in role allowance for user identified with Federated Identity.
Please advise:
1. Where/how to fix this 'undefined' in URL?
2. Also, I would like, probably, to replace thouse Id's in upload URL to genereted user Id's from user database I'm going to add in near future. How to fix IAM Role to use custom Id's?
I stumbled with the same problem when doing Serverless Stack tutorial
This error arises when you do the Extra Credit > React > Facebook Login with Cognito using AWS Amplify, as you have notice uploading a file fails if you're authenticated with Facebook.
The error comes up when sending a PUT to:
https://<bucket>.s3.amazonaws.com/private/<identity-id>/<file>
...the <identity-id> is undefined so the PUT fails.
You can track down the source of this undefinition if you log what you get when running the login commands. For example, when you login using your email and password, if you do:
await Auth.signIn(fields.email, fields.password);
const currCreds = await Auth.currentCredentials();
console.log('currCreds', currCreds);
...you can see that identityId is set correctly.
On the other hand when you login with Facebook through Auth.federatedSignIn if you log the response you don't get identityId. Note: In the case you've previously logged in using email and password, it will remain the same, so this misconfiguration will also make uploading fail.
The workaround I've used is adding a simple lambda which returns the identityId for the logged in user, so once the user logs in with facebook, we ask for it and we can send the PUT to the correct url using AWS.S3().putObject
In the case you want to try this out, take into account that you should host your React app in https as Facebook doesn't allow http domains. You can set this adding HTTPS=true to your React .env file.
You can check my repos as example:
API
Frontend

How can I provide my react-native app with google sign in?

I`ve tried to register my app as Web application, generate the user id and implement it in my code but get an error when I press my button for log in with google:
[Unhandled promise rejection: Error: Please provide the appropriate client ID.
enter image description here
If you're using expo, you have to configure the google sign-in like this. This is my configuration. You have to create androidClientId and iosClientId from your account and use it here.
Disclaimer: This is a standalone function only for signingin/signingup a Google user and fetching details. To configure it with firebase you have to add other functions too.
Also, make sure that you're importing this package. I faced a similar problem when I used another package.
import * as Google from 'expo-google-app-auth'
Additionally, are you using the latest version of expo SDK?
async signInWithGoogleAsync() {
try {
const result = await Google.logInAsync({
androidClientId:
'your-id',
iosClientId:
'your-id',
scopes: ['profile', 'email'],
permissions: ['public_profile', 'email', 'gender', 'location']
})
if (result.type === 'success') {
/*put your logic here, I set some states and navigate to home screen
after successful signin.*/
const googleUser = result.user
this.setState({
email: googleUser.email,
name: googleUser.name,
})
this.navigateToLoadingScreen()
return result.accessToken
} else {
return { cancelled: true }
}
} catch (e) {
return { error: true }
}
}

Error when using Google Application Default Credentials on App Engine

I am trying to make a Node.js app (running Express on App Engine) authenticate with Google API (Server-to-Server) using the Google Application Default Credentials. The app is supposed to use the credentials to talk with Google Analytics, which I have set up by turning on the Analytics API in the Google Developers Console. This is the code I have implemented:
var google = require('googleapis')
var analytics = google.analytics('v3')
app.post('/getAnalyticsData', (req, res) => {
google.auth.getApplicationDefault(function(err, authClient) {
if (err) {
/* Handle error */
}
if (authClient) {
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
authClient = authClient.createScoped(['https://www.googleapis.com/auth/analytics.readonly'])
}
analytics.data.ga.get({
'auth': authClient,
'ids': 'ga:VIEW_ID',
'metrics': 'ga:pageviews,ga:sessions',
'start-date': '2017-01-01',
'end-date': '2017-03-09'
}, function(err, response) {
if (err) {
console.log("Analytics error: ", err)
}
if (response) {
console.log("YAY! Analytics response: ", response)
/* Do something with the response */
}
})
}
})
})
But I am getting this error: A Forbidden error was returned while attempting to retrieve an access token for the Compute Engine built-in service account. This may be because the Compute Engine instance does not have the correct permission scopes specified. Insufficient Permission.
Any idea how to solve this and succeed with the authentication?
I had the same error when trying to use google-auth-library to connect to datastore and was unable to set the correct permissions for the default service account. I found an example in their samples folder that created an auth client using a key file. You can create your own service account with the right permissions and generate a key file on the service account admin page in the cloud console. Hope this helps.
const {auth} = require('google-auth-library');
async function getDnsInfo() {
const client = await auth.getClient({
keyFile: 'path/to/keyFile.json,
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({url});
console.log('DNS Info:');
console.log(res.data);
}

Resources