You need presigned URLs to provide upload functionality without exposing the keys. Suppose you have a backend, such as a Lambda function, that has the keys that allow uploading objects.
You need an endpoint on your backend that the app can reach. Then generate a signed URL:
const url = s3.createPresignedPost({
Bucket: <bucket>,
Fields: {
key: <filename>,
},
});
On the app's side, get this signed URL from the backend (I don't know about React native, this code is written for the browser but it should be easy to modify it to other environments if needed):
const url = await (await fetch("<backend url>")).json();
Create a form with the file:
const formData = new FormData();
formData.append("Content-Type", file.type);
Object.entries(data.fields).forEach(([k, v]) => {
formData.append(k, v);
});
formData.append("file", file); // must be the last one
Then send the file:
await fetch(data.url, {
method: "POST",
body: formData,
});
There are a lot of subtle things you can set up to make sure it's secure. See this article for more info and example code.