Mastering Product Safety: Building Image-recognition Based User Security Solutions in S3
A Step-by-Step Guide for React Native Expo Apps to S3 Integration
“What is user safety worth?”
To me, this seems like one of those questions so obvious it’s not worth answering. But to answer it anyway — I would say that user safety is of the utmost importance. That in the context of developing a new product, it is worth avoiding short-cuts, and spending a lot of time in analytical thought, to make sure your product does not create new edges of risk for users.
To me, when it comes to user safety, there's no room for half-assing it. Of course, not everyone sees it that way. Take Facebook and Google, for example— sure they might be shelling out plenty of cash to beef up their safety and security measures now, but they’re doing so expressly in order to try and repair user safety flaws their previous negligence has allowed. Turns out there is such a thing as a billion-dollar afterthought.
From a business standpoint, investing in safety isn't just smart—it's essential. When it comes to physical safety, well, you can't put a price on human life. Think about it: safety breeds trust, and trust breeds loyalty. This kind of good karma is the cornerstone of major brands like Apple.
Yes, we’re talking about product karma here, people. The spiritual health of your application. It’s a real thing, and bearing it mind maps onto all kinds of fantastic second- and third-degree usage and retention outcomes.
Right now, my team is building a KYC feature for the MVP of our social platform. As a product leader, I anticipated our biggest challenge would be crafting a seamless user experience for KYC—making a security procedure users would actually believe in, and be patient with, so that we could keep them safe without the dreaded post- (or pre-)verification drop-off. Then there was the entirely separate matter of navigating the murky, stormy waters of global regulation on user security and data privacy.
And regulation in this space is something else — the kind of thing that can make you a prisoner of your own success. Because, let's be real, as an early-stage company, we don't exactly have the luxury of setting up data centres for processing user PII data in every country in which we operate (our waitlist is massive now!). Each geography comes with its own set of rules, and staying compliant while maintaining usability is paramount, unless you fancy a day in court.
And all this comes before we get to the technical hurdles—the stuff that keeps me up at night. You see, on my team, I’m also tech lead and the most active full-stack engineer, as well as being the product lead. We’ve already dealt with so many technical challenges, from limited data for training to the looming threat of deepfakes. I thought these would be big challenges. Keeping users safe should be a no-brainer by comparison, right?
Well, if you want to offload the safety burden onto users, yeah. That’s a move straight out of the FAANG playbook. Some of our previous team members were all about that kind of corner-cutting.
They don’t work with us anymore.
Call me old-fashioned, but I believe in putting users first — product karma, people. So, despite the setbacks I knew would come, I rolled up my sleeves and dove headfirst into building that KYC feature.
It’s been a rollercoaster ride of failures—from wonky architectures to tangled dependencies and everything in between.
But we’re all about failure here at NLUS. I'm not one to back down from a challenge. Armed at first with a solid frontend understanding and a sprinkle of backend knowledge from the books, I embarked on my journey. AWS felt like a whole new universe, constantly shape-shifting since 2019! What once took me a mere 2 minutes now felt like a marathon of trial and error, with outdated tutorials leading me down rabbit holes.
I stumbled, I fumbled, I tried, and I failed. But through persistence and grit, after a week-long saga of ups and downs, I emerged victorious. And you know what? It wasn't just about building a feature—it was about upholding our promise to our users: safety first, always.
Always remember this: you're the guardian of your users' digital wellbeing, the protector of their online sanctuary. Dealing with regulations, navigating complex tech landscapes, and staying one step ahead of the bad actors — it’s on you. When you make safety your top priority, you're not just building a product—you're building trust. Trust is the currency of the digital age.
I embarked on a mission to craft the ultimate KYC experience—a seamless blend of security and user-friendliness. To make it happen, I needed users to upload images captured on their mobile devices to an S3 bucket. Simple, right? …Wrong. My struggles with this step led me to write this tutorial. Do I know it will ensure 100% user safety? I can’t promise it but I gave my everything to ensure it.
Are you looking to integrate image capturing and uploading functionality into your React Native app using Expo? If you are here you must have tried preSignedURL, lambda function or even an express server and failed at some point like me. I have an easy solution, so follow along.
In this step-by-step tutorial, we'll guide you through the process of capturing images with the device's camera and uploading them to Amazon S3 using the react-native-aws3
library.
Prerequisites:
Basic knowledge of React Native and JavaScript.
An Amazon Web Services (AWS) account with access to S3.
Now let’s create your shining app -
Setup Environment and Dependencies: First, make sure you have a React Native project set up with Expo. If not, you can create one using
expo init
. Then, install the required dependencies:
expo install expo-camera
npm install react-native-aws3
Request Camera Permissions: In your component file (e.g.,
App.js
), import necessary libraries and hooks:
import React, { useState, useEffect, useRef } from 'react';
import { View, Button, Text, Alert, StyleSheet, Dimensions } from 'react-native';
import { Camera } from 'expo-camera';
import { RNS3 } from 'react-native-aws3';
Then, initialize state variables and request camera permissions using the
useEffect
hook:
export default function App() {
const [selectedImage, setSelectedImage] = useState(null);
const [hasCameraPermission, setHasCameraPermission] = useState(null);
const cameraRef = useRef(null);
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasCameraPermission(status === 'granted');
})();
}, []);
Capture Image: Implement a function to capture images using the device's camera:
const takePicture = async () => {
if (cameraRef.current) {
const photo = await cameraRef.current.takePictureAsync();
console.log('Image captured:', photo.uri);
setSelectedImage(photo.uri);
}
}
Upload Image to S3: Create a function to upload the captured image to Amazon S3:
const uploadImage = async () => {
if (!selectedImage) {
Alert.alert('No image selected');
return;
}
// Configure AWS S3 options
const options = {
bucket: 'your_bucket',
region: 'your_region',
accessKey: 'your_accessKey',
secretKey: 'your_secretKey',
successActionStatus: 201,
};
// Prepare file object for upload
const fileName = selectedImage.split('/').pop();
const file = {
uri: selectedImage,
name: fileName,
type: 'image/jpeg',
};
// Upload image to S3
try {
const response = await RNS3.put(file, options);
console.log('Upload response:', response);
if (response.status === 201) {
Alert.alert('Success', 'Image uploaded successfully');
} else {
throw new Error('Failed to upload image to S3');
}
} catch (error) {
console.error('Error uploading image:', error);
Alert.alert('Error', 'Failed to upload image to S3');
}
};