React Native

This tutorial demonstrates how to add user login to an React Native with Expo application using BlitzWare.

This tutorial is based on the example app.

1) Configure BlitzWare

Get Your Application Keys

You will need some details about your application to communicate with BlitzWare. You can get these details from the Application Settings section in the BlitzWare dashboard.

You need the Client ID.

Configure Redirect URIs

A redirect URI is a URL in your application where BlitzWare redirects the user after they have authenticated. The redirect URI for your app must be added to the Redirect URIs list in your Application Settings under the Security tab. If this is not set, users will be unable to log in to the application and will get an error.

2) Install the BlitzWare React Native SDK

Run the following command within your project directory to install the BlitzWare React Native SDK:

npm
npm install blitzware-react-native-sdk
yarn
yarn add blitzware-react-native-sdk

Prerequisites

This SDK requires Expo and the following dependencies:

  • expo-auth-session - For OAuth 2.0 authentication

  • expo-secure-store - For secure token storage

If using Expo managed workflow, these are included. For bare React Native, install them separately.

Platform Setup

iOS Setup

  1. Add URL scheme to your app.json or app.config.js:

{
  "expo": {
    "scheme": "yourapp"
  }
}

Android Setup

The URL scheme in app.json automatically configures Android intent filters in Expo managed workflow.

For bare React Native, add to android/app/src/main/AndroidManifest.xml:

<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="yourapp" />
</intent-filter>

3) Implementation Guide

Follow this step-by-step guide to implement authentication in your app.

Step 1: Configure the Provider

Wrap your app with the BlitzWareAuthProvider at the root level:

import React from 'react';
import { BlitzWareAuthProvider, BlitzWareConfig } from 'blitzware-react-native-sdk';
import { Stack } from 'expo-router';

export default function RootLayout() {
  const blitzWareConfig: BlitzWareConfig = {
    clientId: "your-client-id",
    redirectUri: "yourapp://oauth", // Must match your app scheme
    responseType: "code", // OAuth 2.0 authorization code flow
  };

  return (
    <BlitzWareAuthProvider config={blitzWareConfig}>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      </Stack>
    </BlitzWareAuthProvider>
  );
}

Step 2: Basic Authentication

Create your main authentication screen:

import React from "react";
import { StyleSheet, Pressable, View, Text } from "react-native";
import { useBlitzWareAuth } from "blitzware-react-native-sdk";

export default function HomeScreen() {
  const { login, logout, isAuthenticated, user, isLoading, error } = useBlitzWareAuth();

  const handleLogin = async () => {
    try {
      await login();
    } catch (error) {
      console.error("Login error:", error);
    }
  };

  const handleLogout = async () => {
    try {
      await logout();
    } catch (error) {
      console.error("Logout error:", error);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>BlitzWare Authentication</Text>
      
      {/* Authentication Status */}
      <View style={styles.statusContainer}>
        <Text style={styles.subtitle}>Status:</Text>
        <Text>
          {isLoading ? "Loading..." : isAuthenticated ? "Authenticated" : "Not Authenticated"}
        </Text>
        
        {/* Error Display */}
        {error && (
          <View style={styles.errorContainer}>
            <Text style={styles.errorTitle}>Error:</Text>
            <Text style={styles.errorText}>{error.message}</Text>
          </View>
        )}
        
        {/* User Information */}
        {user && (
          <View style={styles.userContainer}>
            <Text style={styles.subtitle}>User Info:</Text>
            <Text>ID: {user.id}</Text>
            <Text>Email: {user.email || "N/A"}</Text>
            <Text>Username: {user.username || "N/A"}</Text>
          </View>
        )}
      </View>
      
      {/* Login/Logout Button */}
      <View style={styles.buttonContainer}>
        {!isAuthenticated ? (
          <Pressable style={styles.button} onPress={handleLogin} disabled={isLoading}>
            <Text style={styles.buttonText}>Login with BlitzWare</Text>
          </Pressable>
        ) : (
          <Pressable style={styles.button} onPress={handleLogout} disabled={isLoading}>
            <Text style={styles.buttonText}>Logout</Text>
          </Pressable>
        )}
      </View>
    </View>
  );
}

Step 3: Advanced Features - Session Validation

Add session validation for enhanced security:

import React, { useState } from "react";
import { Alert } from "react-native";
import { useValidateSession } from "blitzware-react-native-sdk";

export default function ProtectedScreen() {
  const { isAuthenticated, user } = useBlitzWareAuth();
  const validateSession = useValidateSession();
  const [sessionValid, setSessionValid] = useState<boolean | null>(null);
  const [validating, setValidating] = useState(false);

  const handleValidateSession = async () => {
    setValidating(true);
    try {
      const isValid = await validateSession();
      setSessionValid(isValid);
      Alert.alert(
        "Session Status",
        isValid ? "Session is valid!" : "Session expired. Please log in again."
      );
    } catch (error) {
      console.error("Session validation error:", error);
      Alert.alert("Error", "Failed to validate session");
    } finally {
      setValidating(false);
    }
  };

  if (!isAuthenticated) {
    return (
      <View style={styles.container}>
        <Text>Please log in to access this content</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Protected Content</Text>
      <Text>Welcome, {user?.username || user?.email}!</Text>
      
      {/* Session Status Display */}
      {sessionValid !== null && (
        <View style={[styles.statusBadge, { 
          backgroundColor: sessionValid ? '#4CAF50' : '#F44336' 
        }]}>
          <Text style={styles.statusText}>
            Session: {sessionValid ? 'Valid' : 'Invalid'}
          </Text>
        </View>
      )}
      
      <Pressable 
        style={styles.button} 
        onPress={handleValidateSession}
        disabled={validating}
      >
        <Text style={styles.buttonText}>
          {validating ? "Validating..." : "Check Session"}
        </Text>
      </Pressable>
    </View>
  );
}

Step 4: Access Token Management

Get access tokens for API calls:

import React from "react";
import { Alert } from "react-native";
import { useAccessToken } from "blitzware-react-native-sdk";

export default function ApiScreen() {
  const getAccessToken = useAccessToken();

  const makeApiCall = async () => {
    try {
      // Get the access token (automatically refreshed if needed)
      const token = await getAccessToken();
      
      if (!token) {
        Alert.alert("Error", "No access token available");
        return;
      }

      // Make authenticated API call
      const response = await fetch("https://api.yourservice.com/protected", {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
      });

      if (response.ok) {
        const data = await response.json();
        Alert.alert("Success", "API call successful!");
      } else {
        Alert.alert("Error", "API call failed");
      }
    } catch (error) {
      console.error("API call error:", error);
      Alert.alert("Error", "Failed to make API call");
    }
  };

  const showTokenInfo = async () => {
    try {
      const token = await getAccessToken();
      if (token) {
        Alert.alert(
          "Token Info", 
          `Length: ${token.length} characters\nPreview: ${token.substring(0, 20)}...`
        );
      } else {
        Alert.alert("No Token", "No access token available");
      }
    } catch (error) {
      Alert.alert("Error", "Failed to get token info");
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>API Integration</Text>
      
      <Pressable style={styles.button} onPress={makeApiCall}>
        <Text style={styles.buttonText}>Make Protected API Call</Text>
      </Pressable>
      
      <Pressable style={styles.button} onPress={showTokenInfo}>
        <Text style={styles.buttonText}>Show Token Info</Text>
      </Pressable>
    </View>
  );
}

Step 5: Role-Based Access Control

Implement role-based features:

import { Tabs } from "expo-router";
import React from "react";

import { HapticTab } from "@/components/haptic-tab";
import { IconSymbol } from "@/components/ui/icon-symbol";
import { Colors } from "@/constants/theme";
import { useColorScheme } from "@/hooks/use-color-scheme";

import { useBlitzWareAuth } from "blitzware-react-native-sdk";

export default function TabLayout() {
  const colorScheme = useColorScheme();
  const { isAuthenticated, hasRole } = useBlitzWareAuth();
  const isAdmin = hasRole("admin");

  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: Colors[colorScheme ?? "light"].tint,
        headerShown: false,
        tabBarButton: HapticTab,
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: "Home",
          tabBarIcon: ({ color }) => (
            <IconSymbol size={28} name="house.fill" color={color} />
          ),
        }}
      />
      <Tabs.Protected guard={isAuthenticated && isAdmin}>
        <Tabs.Screen
          name="admin"
          options={{
            title: "Admin",
            tabBarIcon: ({ color }) => (
              <IconSymbol size={28} name="person.fill" color={color} />
            ),
          }}
        />
      </Tabs.Protected>
      <Tabs.Screen
        name="explore"
        options={{
          title: "Explore",
          tabBarIcon: ({ color }) => (
            <IconSymbol size={28} name="paperplane.fill" color={color} />
          ),
        }}
      />
    </Tabs>
  );
}
import React from "react";
import { useHasRole } from "blitzware-react-native-sdk";

export default function Dashboard() {
  const { user } = useBlitzWareAuth();
  const isAdmin = useHasRole('admin');
  const isPremium = useHasRole('premium');

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Dashboard</Text>
      <Text>Welcome, {user?.username}!</Text>
      <Text>Roles: {user?.roles?.join(', ') || 'None'}</Text>
      
      {/* Admin-only content */}
      {isAdmin && (
        <View style={[styles.card, styles.adminCard]}>
          <Text style={styles.cardTitle}>🔧 Admin Panel</Text>
          <Text>You have administrative privileges</Text>
        </View>
      )}
      
      {/* Premium content */}
      {isPremium && (
        <View style={[styles.card, styles.premiumCard]}>
          <Text style={styles.cardTitle}>⭐ Premium Features</Text>
          <Text>Access to premium content</Text>
        </View>
      )}
      
      {/* Regular user content */}
      <View style={styles.card}>
        <Text style={styles.cardTitle}>📊 User Stats</Text>
        <Text>Standard user features available</Text>
      </View>
    </View>
  );
}

That's it!

Last updated

Was this helpful?