React Native Academy · Lektion

Godkendelsesflow og beskyttede ruter

Implementér brugeroprettelse, login og bevarelse af sessionen, opsæt en root navigator, der skifter mellem auth- og app-stacks, og beskyt alle dataskærme mod adgang uden godkendelse.

Lektion 2 af 413 trin

Godkendelsesflow og beskyttede ruter er en gratis React Native Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i React Native Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. React Native Academy-kurset indeholder 4 lektioner i alt.

Oversigt over godkendelsesarkitekturen

Et godkendelsesforløb til produktion har tre tilstande, som din navigator skal håndtere: indlæser (kontrollerer, om der findes en session i lageret), ikke godkendt (viser godkendelsesskærme) og godkendt (viser appskærme). Den centrale pointe er, at din rodnavigator skifter mellem godkendelsesstakken og appstakken ud fra sessionens tilstand — brugere kan ikke få adgang til dataskærme uden en gyldig session, og godkendte brugere kan ikke ende på loginskærmen.

// Auth states:
// 'loading'        -> Show a splash/loading screen
// 'unauthenticated' -> Show auth stack (Sign In, Sign Up)
// 'authenticated'   -> Show app stack (tabs, data screens)

// The navigator reads session from AuthContext:
const { session, isLoading } = useAuth();

if (isLoading) return <SplashScreen />;

return (
  <NavigationContainer>
    {session ? <AppNavigator /> : <AuthNavigator />}
  </NavigationContainer>
);

Opsætning af AuthContext

Opret en AuthContext, der gør den aktuelle session, brugerobjektet samt funktionerne til login, tilmelding og logout tilgængelige i hele komponenttræet. Ved montering skal du kontrollere AsyncStorage eller Supabase' automatisk gendannede session for at afgøre, om brugeren allerede er logget ind fra en tidligere appstart. Det forhindrer, at godkendelsesskærmene blinker frem, før sessionen er gendannet.

// src/context/AuthContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { Session } from '@supabase/supabase-js';
import { supabase } from '../services/supabase';

interface AuthContextType {
  session: Session | null;
  isLoading: boolean;
  signOut: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType>(null!);

export function AuthProvider({ children }) {
  const [session, setSession] = useState<Session | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    supabase.auth.getSession().then(({ data }) => {
      setSession(data.session);
      setIsLoading(false);
    });

    const { data: listener } = supabase.auth.onAuthStateChange(
      (_event, session) => setSession(session)
    );
    return () => listener.subscription.unsubscribe();
  }, []);

  const signOut = async () => { await supabase.auth.signOut(); };

  return (
    <AuthContext.Provider value={{ session, isLoading, signOut }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

Indpakning af appen med AuthProvider

Indpak hele din applikation i AuthProvider ved indgangspunktet, så alle komponenter kan få adgang til sessionen. Placér den uden for NavigationContainer, eftersom navigatoren selv læser fra godkendelseskonteksten. Indpak også med QueryClientProvider, hvis du bruger React Query. Rækkefølgen er vigtig: Dataleverandører skal indpakke navigationen og ikke ligge indlejret i bestemte skærme.

// App.tsx (entry point)
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthProvider } from './src/context/AuthContext';
import { RootNavigator } from './src/navigation/RootNavigator';

const queryClient = new QueryClient();

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        <RootNavigator />
      </AuthProvider>
    </QueryClientProvider>
  );
}

Implementering af tilmeldingsskærmen

Tilmeldingsskærmen indsamler e-mail og adgangskode, validerer dem lokalt og kalder supabase.auth.signUp(). Efter en vellykket tilmelding kræver Supabase muligvis bekræftelse af e-mailen — håndtér dette ved at vise en meddelelse om at tjekke e-mailen i stedet for straks at navigere videre. Brug react-hook-form til validering, så du undgår manuel tilstandshåndtering for hver feltfejl.

// src/screens/auth/SignUpScreen.tsx
import { useForm, Controller } from 'react-hook-form';
import { supabase } from '../../services/supabase';

export function SignUpScreen({ navigation }) {
  const { control, handleSubmit, formState: { errors } } = useForm();
  const [message, setMessage] = useState('');

  const onSubmit = async ({ email, password }) => {
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) {
      setMessage(error.message);
    } else {
      setMessage('Check your email to confirm your account!');
    }
  };

  return (
    <View>
      <Controller
        control={control}
        name='email'
        rules={{ required: true, pattern: /^[^@]+@[^@]+$/ }}
        render={({ field: { onChange, value } }) => (
          <TextInput
            value={value}
            onChangeText={onChange}
            placeholder='Email'
            keyboardType='email-address'
          />
        )}
      />
      {/* Password field similar pattern */}
      <Button title='Sign Up' onPress={handleSubmit(onSubmit)} />
      {message ? <Text>{message}</Text> : null}
    </View>
  );
}

Loginskærm og sessionsvedholdenhed

Loginskærmen kalder supabase.auth.signInWithPassword(). Hvis det lykkes, gemmer Supabase sessionen i AsyncStorage (konfigureret under opsætningen af klienten), og lytteren onAuthStateChange i AuthContext udløses, så sessionstilstanden opdateres. Rodnavigatoren registrerer den nye session og navigerer automatisk til appstakken — du behøver ikke manuelt at kalde navigation.navigate efter login.

// Sign-in logic (simplified)
const signIn = async (email, password) => {
  setLoading(true);
  const { error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
  setLoading(false);

  if (error) {
    // Show error to user
    Alert.alert('Sign In Error', error.message);
    return;
  }

  // No navigation.navigate() needed!
  // AuthContext.onAuthStateChange fires automatically
  // -> session is set -> root navigator switches to AppNavigator
};

Rodnavigator med godkendelsesværn

Rodnavigatoren er portvagten, der ud fra sessionstilstanden beslutter, hvilket navigationstræ der skal vises. Den gengiver ikke selv nogen brugerflade — den overlader arbejdet til enten AuthNavigator eller AppNavigator. React Navigation gengiver automatisk igen, når navigatort træet ændres (godkendelse → app), så brugerne omdirigeres til de rigtige skærme uden eksplicitte navigationskald ved login eller logout.

// src/navigation/RootNavigator.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { useAuth } from '../context/AuthContext';
import { AuthNavigator } from './AuthNavigator';
import { AppTabNavigator } from './AppTabNavigator';
import { SplashScreen } from '../screens/SplashScreen';

const Stack = createStackNavigator();

export function RootNavigator() {
  const { session, isLoading } = useAuth();

  if (isLoading) {
    return <SplashScreen />; // Full-screen loading state
  }

  return (
    <NavigationContainer>
      {session ? (
        <AppTabNavigator />  // Authenticated: show app screens
      ) : (
        <AuthNavigator />    // Unauthenticated: show auth screens
      )}
    </NavigationContainer>
  );
}

Beskyttelse af individuelle skærme

Mønsteret med rodnavigatoren beskytter hele navigationstræer. Men nogle gange har du brug for beskyttelse i skærmen — for eksempel til en funktion, der kun er tilgængelig for premiumbrugere. Opret en useRequireAuth-hook, der kontrollerer sessionen og navigerer til login, hvis den mangler. Eller opret en wrapperkomponent kaldet ProtectedRoute. Dette mønster er nyttigt til dybe links til skærme, som kan blive åbnet før godkendelse.

// src/hooks/useRequireAuth.ts
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';
import { useAuth } from '../context/AuthContext';

export function useRequireAuth() {
  const { session, isLoading } = useAuth();
  const navigation = useNavigation();

  useEffect(() => {
    if (!isLoading && !session) {
      navigation.navigate('SignIn' as never);
    }
  }, [session, isLoading, navigation]);

  return { session, isLoading };
}

// Usage in a screen:
export function PremiumFeatureScreen() {
  const { session } = useRequireAuth();
  if (!session) return null; // Navigation already happening
  return <View>...</View>;
}

Håndtering af tokenudløb

Supabase-JWT-tokens udløber (typisk efter 1 time). Supabase-klienten, der er konfigureret med autoRefreshToken: true, håndterer dette automatisk — den fornyer tokenet i baggrunden, før det udløber. Hvis fornyelsen mislykkes (f.eks. fordi fornyelsestokenet blev tilbagekaldt), udløses onAuthStateChange med hændelsen SIGNED_OUT, og din AuthContext rydder sessionen. Navigatoren viser derefter automatisk godkendelsesskærmene og beder brugeren om at logge ind igen.

// Token expiry handling is automatic with Supabase:
// supabase client config (already set):
// {
//   auth: {
//     autoRefreshToken: true,  <- handles renewal automatically
//     persistSession: true,
//     storage: AsyncStorage,
//   }
// }

// Your AuthContext listener handles SIGNED_OUT:
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_OUT') {
    // session is null -> navigator shows auth screens
    setSession(null);
  }
  if (event === 'TOKEN_REFRESHED') {
    // New session with fresh tokens
    setSession(session);
  }
});

Forløb for nulstilling af adgangskode

Implementér Glemt adgangskode ved hjælp af Supabase' resetPasswordForEmail(). Brugeren indtaster sin e-mail, Supabase sender et nulstillingslink, og når brugeren klikker på det, åbner linket appen via et dybt link med et token. Du konfigurerer omdirigerings-URL'en i godkendelsesindstillingerne i dit Supabase-dashboard. I appen skal du lytte efter godkendelseshændelsen PASSWORD_RECOVERY og navigere til en skærm til angivelse af en ny adgangskode.

// Step 1: Send reset email
const resetPassword = async (email) => {
  const { error } = await supabase.auth.resetPasswordForEmail(email, {
    redirectTo: 'habittracker://reset-password',
  });
  if (!error) Alert.alert('Email sent', 'Check your inbox.');
};

// Step 2: Handle deep link in AuthContext
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'PASSWORD_RECOVERY') {
    // Navigate to update password screen
    navigationRef.current?.navigate('UpdatePassword');
  }
});

// Step 3: Update password screen
const updatePassword = async (newPassword) => {
  const { error } = await supabase.auth.updateUser({
    password: newPassword,
  });
  if (!error) navigation.navigate('Home');
};

Logout og oprydning af sessionen

Logout skal rydde al lokal tilstand, ikke kun Supabase-sessionen. Kald supabase.auth.signOut(), som fjerner sessionen fra AsyncStorage og udløser hændelsen SIGNED_OUT. Ryd også din React Query-cache, så den næste bruger, der logger ind på samme enhed, ikke kan se den forrige brugers data. Nulstil navigationstilstanden, hvis navigatoren indeholder skærmspecifik tilstand.

// Complete sign-out
const signOut = async () => {
  // 1. Sign out from Supabase (clears AsyncStorage session)
  await supabase.auth.signOut();

  // 2. Clear React Query cache
  queryClient.clear();

  // 3. AuthContext detects SIGNED_OUT -> session = null
  // 4. RootNavigator switches to AuthNavigator automatically
  // No navigation.navigate() needed!
};

// In a Settings screen:
<TouchableOpacity onPress={signOut}>
  <Text>Sign Out</Text>
</TouchableOpacity>

Test af godkendelsesforløbet

Test alle godkendelsesforløb, før du går videre med funktionsudviklingen: tilmelding af en ny bruger (forløbet for bekræftelse af e-mail), login af en eksisterende bruger, fejl ved forkert adgangskode, bevarelse af sessionen efter genstart af appen, logout og nulstilling af adgangskode. Automatisér det forventede standardforløb med en Maestro-test. Kontrollér, at navigatoren skifter korrekt mellem godkendelses- og app-tilstande uden at flimre — indlæsningstilstanden forhindrer dette, når AsyncStorage kontrolleres ved opstart.

# Maestro test for sign-in flow
---
appId: com.yourcompany.habittracker
---
- launchApp:
    clearState: true
- assertVisible: 'Sign In'
- tapOn: 'Email'
- inputText: 'test@example.com'
- tapOn: 'Password'
- inputText: 'TestPass123!'
- tapOn: 'Sign In'
- waitForAnimationToEnd
- assertVisible: 'My Habits'  # First app screen
- tapOn: 'Settings'
- tapOn: 'Sign Out'
- waitForAnimationToEnd
- assertVisible: 'Sign In'   # Back to auth screen

Hurtigt tjek

Test din forståelse af begreberne inden for React Native Mobile Development fra denne lektion.

Opsummering af lektionen

I denne lektion lærte du: hvordan du bygger en AuthContext, der sporer sessionstilstanden og abonnerer på Supabase-godkendelseshændelser, hvordan rodnavigatoren bruger sessionstilstanden til at skifte mellem godkendelses- og appnavigationstræer samt hvordan du implementerer forløb for tilmelding, login, logout og nulstilling af adgangskode. Du så også, hvordan du rydder React Query-cachen ved logout for at forhindre datalækage mellem brugere. Nu går vi videre med at bygge det centrale datafeed med understøttelse af offlinebrug.

Gratis at komme i gang

Lær JavaScript med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Godkendelsesflow og beskyttede ruter” gratis?

Ja — alle 3 lektioner i læringssporet React Native Academy, inklusive “Godkendelsesflow og beskyttede ruter”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. React Native Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Godkendelsesflow og beskyttede ruter”?

Implementér brugeroprettelse, login og bevarelse af sessionen, opsæt en root navigator, der skifter mellem auth- og app-stacks, og beskyt alle dataskærme mod adgang uden godkendelse. Du øver dig i React Native Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på React Native Academy?

Der kræves ingen tidligere erfaring. React Native Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Godkendelsesflow og beskyttede ruter”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne React Native Academy-lektion?

Ja. Alle React Native Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Planlægning af arkitektur og teknologistak
  2. Godkendelsesflow og beskyttede ruter
  3. Kernefunktion: Datafeed med offlineunderstøttelse
  4. Finpudsning, test og udgivelse i begge stores
← Tilbage til React Native Academy