Sichere React-Native-Wearable-App mit Supabase und RLS
React-Native-Leitfaden: sichere Wearable-App. Synchronisieren Sie Gesundheitsdaten und schützen Sie sie mit Supabase-RLS, damit Nutzer nur eigene Daten sehen.
Auf dieser Seite
Die Märkte für Wearable-Technologie und Healthtech wachsen rasant. Für Entwicklerinnen und Entwickler bietet das eine enorme Chance, bringt aber auch eine entscheidende Verantwortung mit sich: den Schutz sensibler Gesundheitsdaten der Nutzerinnen und Nutzer. Ein Datenleck in einer Gesundheits-App ist nicht nur eine Unannehmlichkeit; es ist ein schwerwiegender Eingriff in die Privatsphäre.
In diesem Tutorial erstellen wir eine vollständige, sichere Datenpipeline von einer React-Native-Anwendung zu einem Supabase-Backend. Wir simulieren Daten eines Fitness-Wearables (Herzfrequenz und Schritte) und synchronisieren sie in Echtzeit. Das Geheimnis? Wir nutzen die leistungsstarke Row Level Security (RLS) von Supabase, um Regeln auf Datenbankebene zu erstellen, die sicherstellen, dass Nutzerinnen und Nutzer ausschließlich ihre eigenen Daten lesen und schreiben können. So entsteht eine grundlegende Sicherheitsebene für unsere App.
Was wir erstellen: Eine React-Native-App mit Benutzerauthentifizierung, in der simulierte Wearable-Daten fortlaufend erzeugt und sicher in eine Supabase-Datenbank hochgeladen werden. Eine Echtzeitanzeige zeigt der Nutzerin oder dem Nutzer nur die eigenen Daten, während sie synchronisiert werden.
Voraussetzungen:
- Node.js (LTS-Version) und npm/yarn sind installiert.
- Die App Expo Go ist auf Ihrem Mobilgerät zum Testen installiert.
- Ein kostenloses Supabase-Konto.
- Grundkenntnisse in React Native und SQL.
Warum das für Entwicklerinnen und Entwickler wichtig ist: Der sichere Umgang mit Nutzerdaten ist unverhandelbar. Dieses Projekt vermittelt Ihnen ein robustes, skalierbares Muster für den Aufbau sicherer Echtzeitanwendungen, ohne die Komplexität der Verwaltung eines klassischen Backend-Servers.
Das Problem verstehen
In einem typischen Client-Server-Modell haben Sie möglicherweise einen API-Endpunkt wie /api/health-data. Ihr serverseitiger Code wäre dafür verantwortlich zu prüfen, ob die angemeldete Person berechtigt ist, auf die angeforderten Daten zuzugreifen. Das ist eine praktikable Lösung, hat aber Schwächen:
- Komplexität: Sie müssen diese Autorisierungslogik über mehrere Endpunkte hinweg schreiben und pflegen.
- Risiko von Datenlecks: Ein einzelner Fehler in Ihrem API-Code könnte versehentlich die Daten einer Person einer anderen offenlegen.
- Defense in Depth: Sich allein auf die API für die Sicherheit zu verlassen, ist riskant. Was, wenn ein API-Schlüssel kompromittiert wird?
Unser Ansatz verlagert die Sicherheit in die Datenbank selbst. Mit Supabase können Sie einfache SQL-Richtlinien schreiben, die als Türsteher für Ihre Daten fungieren. Selbst wenn eine Anfrage Ihr Frontend passiert, lehnt die Datenbank jede Aktion ab, die gegen die RLS-Richtlinie verstößt. Das ist Sicherheit auf der niedrigsten, grundlegendsten Ebene.
Voraussetzungen: Ihre Werkzeuge einrichten
1. Ein Supabase-Projekt einrichten
- Gehen Sie zu Ihrem Supabase-Dashboard und klicken Sie auf „New project“.
- Geben Sie Ihrem Projekt einen Namen und ein starkes Datenbankpasswort. Wählen Sie eine Region in Ihrer Nähe.
- Navigieren Sie nach der Erstellung des Projekts in der linken Seitenleiste zu den „API“-Einstellungen. Sie benötigen Ihre Project URL und den öffentlichen
anon-Schlüssel. Halten Sie beides bereit.
2. Ein React-Native-Projekt einrichten
Öffnen Sie Ihr Terminal und erstellen Sie ein neues Expo-Projekt:
npx create-expo-app wearable-data-sync
cd wearable-data-sync
Installieren Sie anschließend die erforderlichen Abhängigkeiten für die Verbindung mit Supabase:
npm install @supabase/supabase-js @react-native-async-storage/async-storage react-native-url-polyfill
@supabase/supabase-js: Der offizielle JavaScript-Client für Supabase.@react-native-async-storage/async-storage: Ein sicheres, asynchrones Speichersystem, das Supabase verwendet, um Benutzersitzungen in React Native beizubehalten.react-native-url-polyfill: Ein kleines Polyfill, das Supabase benötigt, um in der React-Native-Umgebung korrekt zu funktionieren.
Schritt 1: Die Supabase-Backend-Tabelle erstellen
Hier werden unsere Gesundheitsdaten gespeichert.
Was wir tun
Wir verwenden den SQL-Editor von Supabase, um eine Tabelle health_data zu erstellen. Wir definieren Spalten für die Herzfrequenz, die Schritte und – entscheidend – eine user_id, die jeden Eintrag mit einer bestimmten Person verknüpft.
Umsetzung
- Gehen Sie in Ihrem Supabase-Projekt zum „SQL Editor“.
- Klicken Sie auf „+ New query“ und führen Sie das folgende SQL aus:
-- 1. Create the health_data table
CREATE TABLE public.health_data (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
heart_rate INT,
steps INT
);
-- 2. Enable Row Level Security (RLS) on the table
ALTER TABLE public.health_data ENABLE ROW LEVEL SECURITY;
So funktioniert es
- Wir erstellen eine einfache Tabelle, um unsere Messwerte zu speichern.
- Die Spalte
user_idist ein Fremdschlüssel, der auf dieidin der integrierten Tabelleauth.usersvon Supabase verweist. Diese Verknüpfung auf Datenbankebene macht unsere Sicherheitsrichtlinien erst möglich. ALTER TABLE ... ENABLE ROW LEVEL SECURITY;ist der entscheidende Befehl. Standardmäßig wird bei aktivierter RLS und ohne Richtlinien jeder Zugriff auf die Tabelle verweigert. Dieser „secure-by-default“-Ansatz verhindert eine versehentliche Offenlegung von Daten.
Schritt 2: React Native mit Supabase verbinden
Konfigurieren wir nun unsere mobile App so, dass sie mit unserem neuen Backend kommuniziert.
Was wir tun
Wir erstellen eine Hilfsdatei, um den Supabase-Client mit den API-Schlüsseln aus unserem Projekt-Dashboard zu initialisieren.
Umsetzung
- Erstellen Sie im Stammverzeichnis Ihres React-Native-Projekts eine Datei namens
lib/supabase.js. - Fügen Sie den folgenden Code ein und ersetzen Sie die Platzhalter durch Ihre tatsächliche Supabase-URL und Ihren Anon-Schlüssel.
// lib/supabase.js
import 'react-native-url-polyfill/auto';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = 'YOUR_SUPABASE_URL';
const supabaseAnonKey = 'YOUR_SUPABASE_ANON_KEY';
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
storage: AsyncStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
},
});
So funktioniert es
Dieser Code erzeugt eine einzige, wiederverwendbare Instanz des supabase-Clients. Wir konfigurieren ihn so, dass er AsyncStorage für die Sitzungspersistenz verwendet, was bei React Native Standard ist. react-native-url-polyfill wird oben importiert, um die Kompatibilität sicherzustellen.
Schritt 3: Benutzerauthentifizierung implementieren
Nutzerinnen und Nutzer müssen sich anmelden, bevor sie Daten synchronisieren können.
Was wir tun
Wir erstellen eine einfache Benutzeroberfläche zum Anmelden und Registrieren und verwalten den Sitzungsstatus.
Umsetzung
- Erstellen Sie eine neue Datei
src/components/Auth.js. - Fügen Sie den Code für ein einfaches Authentifizierungsformular mit E-Mail und Passwort hinzu.
// src/components/Auth.js
import React, { useState } from 'react';
import { Alert, StyleSheet, View, TextInput, Button, Text } from 'react-native';
import { supabase } from '../../lib/supabase';
export default function Auth() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
async function signInWithEmail() {
setLoading(true);
const { error } = await supabase.auth.signInWithPassword({
email: email,
password: password,
});
if (error) Alert.alert(error.message);
setLoading(false);
}
async function signUpWithEmail() {
setLoading(true);
const { error } = await supabase.auth.signUp({
email: email,
password: password,
});
if (error) Alert.alert(error.message);
setLoading(false);
}
return (
<View style={styles.container}>
<Text style={styles.header}>Health Sync</Text>
<TextInput
style={styles.input}
onChangeText={(text) => setEmail(text)}
value={email}
placeholder="[email protected]"
autoCapitalize={'none'}
/>
<TextInput
style={styles.input}
onChangeText={(text) => setPassword(text)}
value={password}
secureTextEntry={true}
placeholder="Password"
autoCapitalize={'none'}
/>
<View style={styles.buttonContainer}>
<Button title="Sign in" disabled={loading} onPress={() => signInWithEmail()} />
</View>
<View style={styles.buttonContainer}>
<Button title="Sign up" disabled={loading} onPress={() => signUpWithEmail()} />
</View>
</View>
);
}
// Add some basic styling
const styles = StyleSheet.create({ /* ... */ });
- Aktualisieren Sie nun Ihre Hauptdatei
App.js, um die Sitzung zu verwalten.
// App.js
import React, { useState, useEffect } from 'react';
import { View } from 'react-native';
import { supabase } from './lib/supabase';
import Auth from './src/components/Auth';
import HealthDashboard from './src/components/HealthDashboard'; // We will create this next
export default function App() {
const [session, setSession] = useState(null);
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setSession(session);
});
supabase.auth.onAuthStateChange((_event, session) => {
setSession(session);
});
}, []);
return (
<View>
{session && session.user ? <HealthDashboard key={session.user.id} session={session} /> : <Auth />}
</View>
);
}
So funktioniert es
App.js wird zu einem Router. Die Datei prüft, ob eine aktive Sitzung besteht. Ist eine Person angemeldet, wird HealthDashboard angezeigt; andernfalls die Komponente Auth. Der Listener onAuthStateChange aktualisiert die Benutzeroberfläche automatisch, wenn sich jemand an- oder abmeldet. ✨
Schritt 4: Die sichere Synchronisierungspipeline erstellen
Das ist der Kern unserer Anwendung. Wir simulieren Wearable-Daten und synchronisieren sie sicher.
Was wir tun
Wir erstellen eine Komponente HealthDashboard, die drei Dinge erledigt:
- Sie simuliert alle paar Sekunden neue Herzfrequenz- und Schrittdaten.
- Sie fügt diese neuen Daten in unsere Tabelle
health_dataein. - Sie abonniert Echtzeitaktualisierungen dieser Tabelle, um die Daten anzuzeigen.
Umsetzung
// src/components/HealthDashboard.js
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet, FlatList, Alert } from 'react-native';
import { supabase } from '../../lib/supabase';
const HealthDashboard = ({ session }) => {
const [healthData, setHealthData] = useState([]);
const [loading, setLoading] = useState(false);
// ** 1. Fetch initial data and set up real-time subscription **
useEffect(() => {
getHealthData();
const channel = supabase
.channel('health_data_changes')
.on('postgres_changes', { event: '*', schema: 'public', table: 'health_data' }, (payload) => {
// When new data arrives, add it to the top of our list
setHealthData((currentData) => [payload.new, ...currentData]);
})
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, []);
// ** 2. Simulate wearable and sync data **
useEffect(() => {
const interval = setInterval(() => {
syncNewData();
}, 5000); // Sync new data every 5 seconds
return () => clearInterval(interval);
}, []);
const getHealthData = async () => {
setLoading(true);
const { data, error } = await supabase
.from('health_data')
.select('*')
.order('created_at', { ascending: false }); // Most recent first
if (error) {
Alert.alert('Error Fetching Data', 'Have you set up RLS policies?');
console.error('Fetch error:', error.message);
} else {
setHealthData(data);
}
setLoading(false);
};
const syncNewData = async () => {
const newHeartRate = Math.floor(Math.random() * (120 - 60 + 1)) + 60; // Random heart rate 60-120
const newSteps = Math.floor(Math.random() * 50) + 10; // Random steps 10-50
const { error } = await supabase
.from('health_data')
.insert({
user_id: session.user.id,
heart_rate: newHeartRate,
steps: newSteps,
});
if (error) {
Alert.alert('Sync Error', error.message);
}
};
return (
<View style={styles.container}>
<Text style={styles.header}>Welcome, {session.user.email}</Text>
<Text style={styles.subHeader}>Real-time Health Data:</Text>
<FlatList
data={healthData}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.dataRow}>
<Text>❤️ {item.heart_rate} bpm</Text>
<Text>👟 {item.steps} steps</Text>
<Text style={styles.timestamp}>{new Date(item.created_at).toLocaleTimeString()}</Text>
</View>
)}
/>
<Button title="Sign Out" onPress={() => supabase.auth.signOut()} />
</View>
);
};
// Add styles
const styles = StyleSheet.create({ /* ... */ });
export default HealthDashboard;
So funktioniert es
- Datensimulation:
setIntervalin einemuseEffect-Hook ruft alle 5 SekundensyncNewDataauf, wodurch Zufallsdaten erzeugt werden. - Dateneinfügung:
syncNewDataverwendetsupabase.from('health_data').insert(...), um die neuen Daten hochzuladen. Entscheidend ist, dasssession.user.identhalten ist, um die Daten der angemeldeten Person zuzuordnen. - Echtzeitabonnement: Der erste
useEffectrichtet mitsupabase.channel(...)ein Abonnement ein. Es lauscht auf alleINSERT-Ereignisse in unserer Tabellehealth_data. Wird eine Änderung erkannt, aktualisiert die Nutzlast den Zustand der Komponente, sodass die Benutzeroberfläche sofort neu gerendert wird.
Wenn Sie die App an diesem Punkt ausführen, können Sie sich registrieren, aber Sie können keine Daten abrufen oder einfügen! Es erscheint eine Fehlermeldung. Das liegt daran, dass RLS aktiviert ist, wir aber noch keine „Zulassen“-Richtlinien definiert haben. Die Datenbank blockiert alle Anfragen korrekt.
Schritt 5: Row-Level-Security-Richtlinien implementieren
Hier vergeben wir Berechtigungen und setzen unser Sicherheitsmodell durch.
Was wir tun
Wir schreiben im Supabase-Dashboard zwei einfache SQL-Richtlinien:
- Eine
SELECT-Richtlinie, die es Nutzerinnen und Nutzern erlaubt, nur ihre eigenen Daten zu lesen. - Eine
INSERT-Richtlinie, die es Nutzerinnen und Nutzern erlaubt, Datensätze nur für sich selbst zu erstellen.
Umsetzung
Gehen Sie zurück zum Bereich „Authentication“ -> „Policies“ Ihres Supabase-Dashboards. Wählen Sie die Tabelle health_data und klicken Sie auf „+ New Policy“.
1. Die SELECT-Richtlinie (Lesen)
- Klicken Sie auf „Create a new policy“.
- Wählen Sie die Vorlage „Get started quickly“ für „Enable read access to everyone“.
- Ändern Sie den Richtliniennamen in:
Users can view their own health data. - Stellen Sie sicher, dass
SELECTdie zulässige Operation ist. - Ändern Sie im Ausdruck
USINGden Werttruein:
auth.uid() = user_id
- Klicken Sie auf „Review“ und „Save policy“.
2. Die INSERT-Richtlinie (Erstellen)
- Klicken Sie erneut auf „+ New Policy“.
- Wählen Sie die Vorlage für „Enable insert access for authenticated users only“.
- Ändern Sie den Richtliniennamen in:
Users can insert their own health data. - Stellen Sie sicher, dass
INSERTdie zulässige Operation ist. - Ändern Sie im Ausdruck
WITH CHECKden Werttruein:
auth.uid() = user_id
* Click "Review" and "Save policy".
### So funktioniert es
* `auth.uid()`: Das ist eine spezielle Supabase-Funktion, die die eindeutige ID der aktuell authentifizierten Person zurückgibt, die die Anfrage stellt.
* **SELECT-Richtlinie**: Die Klausel `USING (auth.uid() = user_id)` wirkt wie ein obligatorischer `WHERE`-Filter bei jeder `SELECT`-Abfrage. Sie weist PostgreSQL an: „Gib nur Zeilen zurück, in denen die Spalte `user_id` mit der ID der anfragenden Person übereinstimmt.“
* **INSERT-Richtlinie**: Die Klausel `WITH CHECK (auth.uid() = user_id)` wirkt als Validierungsregel. Sie weist PostgreSQL an: „Prüfe vor dem Übernehmen dieser neuen Zeile, dass die eingefügte `user_id` dieselbe ist wie die ID der Person, die das Einfügen ausführt.“ Das verhindert, dass jemand böswillig Daten im Namen einer anderen Person einfügt.
Starten Sie nun Ihre React-Native-App neu. Registrieren Sie sich als neue Nutzerin oder neuer Nutzer (oder melden Sie sich an). Sie sollten das Dashboard sehen, und alle 5 Sekunden erscheinen neue Datenpunkte! Wenn Sie ein zweites Benutzerkonto erstellen und sich anmelden, sehen Sie eine vollständig getrennte, leere Datenliste. Das belegt, dass Ihre Sicherheitsrichtlinien funktionieren.
## Fazit
Wir haben erfolgreich eine sichere, vollständige Datenpipeline erstellt. Sie verfügen nun über eine React-Native-App, die Benutzerauthentifizierung beherrscht und Daten in Echtzeit mit einem Supabase-Backend synchronisiert. Vor allem haben Sie Ihre Daten mit Row Level Security abgesichert, sodass selbst bei direktem Datenbankzugriff die sensiblen Gesundheitsinformationen einer Person isoliert und geschützt bleiben.
Dieses Muster ist die Grundlage für robuste, skalierbare und sichere Anwendungen. Von hier aus können Sie das Projekt in viele Richtungen weiterentwickeln.
**Nächste Schritte für Leserinnen und Leser:**
* **Echte Wearable-Integration**: Ersetzen Sie den Simulator durch eine Bibliothek wie `react-native-health` oder `react-native-wearables`, um echte Daten aus Apple Health oder Google Fit zu beziehen.
* **Datenvisualisierung**: Nutzen Sie eine Diagrammbibliothek, um Verläufe der Gesundheitsdaten über die Zeit darzustellen.
* **Offline-Unterstützung**: Implementieren Sie eine Offline-First-Strategie mit einer lokalen Datenbank wie SQLite und einer Synchronisierungs-Engine für Fälle ohne Internetverbindung.
## Ressourcen
* **Offizielle Supabase-Dokumentation**: [supabase.com/docs](https://supabase.com/docs)
* **Supabase-RLS-Dokumentation**: [Supabase Row Level Security Docs](https://supabase.com/docs/guides/auth/row-level-security)
* **Expo-Dokumentation**: [docs.expo.dev](https://docs.expo.dev/)
* **React Native Health**: [GitHub-Repository](https://github.com/agencyenterprise/react-native-health)
---
## Kurzreferenz
| Aspekt | Wichtige Informationen |
|--------|----------------|
| **Thema** | Entwicklung |
| **Relevanz** | Patientenaufklärung und Bewusstsein |
| **Maßnahme** | Besprechen Sie dies mit Ihrer Ärztin oder Ihrem Arzt |
| **Häufigkeit** | Wie von Ihrem Behandlungsteam empfohlen |
| **Evidenz** | Basierend auf aktuellen klinischen Leitlinien |
## Häufig gestellte Fragen
**F: Woran erkenne ich, ob das auf mich zutrifft?**
A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrer Ärztin oder Ihrem Arzt, um Ihre individuellen Risikofaktoren einzuschätzen und die geeigneten nächsten Schritte festzulegen.
**F: Was sollte ich meine Ärztin oder meinen Arzt fragen?**
A: Gute Fragen sind: „Was bedeuten meine Ergebnisse?“, „Welche Möglichkeiten habe ich?“, „Welche Änderungen des Lebensstils würden helfen?“ und „Wann sollte ich zur Nachkontrolle kommen?“
**F: Gibt es Änderungen des Lebensstils, die helfen können?**
A: Die meisten Erkrankungen profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), erholsamer Schlaf (7–9 Stunden), Stressbewältigung und der Verzicht auf Tabak.
## Medizinischer Haftungsausschluss
Dieser Artikel dient ausschließlich Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie stets eine qualifizierte medizinische Fachperson, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Versorgung treffen. Die individuellen Umstände unterscheiden sich, und was für eine Person geeignet ist, muss für eine andere nicht geeignet sein.
Wenn Sie einen medizinischen Notfall erleben, rufen Sie sofort 911 (oder Ihre örtliche Notrufnummer) an.