npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@personql/react-native

v1.0.6

Published

React Native SDK for PersonQL authentication and multi-tenant support

Readme

@personql/react-native

Official PersonQL SDK for React Native applications. Provides authentication, multi-tenant organization management, secure storage, and biometric authentication for iOS and Android.

Features

  • 🔐 Secure Authentication - Email/password, OAuth, and biometric authentication
  • 🏢 Multi-Tenant Organizations - Complete organization management with RBAC
  • 🔒 Secure Storage - Native Keychain (iOS) and Keystore (Android) integration
  • 👆 Biometric Auth - Face ID, Touch ID, and Fingerprint support
  • 🎨 Pre-built UI - Ready-to-use SignInScreen component
  • 📱 Cross-Platform - Works on iOS and Android
  • 🪝 React Hooks - Modern hooks-based API
  • 💪 TypeScript - Full type safety

Installation

npm install @personql/react-native
# or
yarn add @personql/react-native
# or
pnpm add @personql/react-native

Peer Dependencies

Install required peer dependencies:

npm install react react-native @react-native-async-storage/async-storage react-native-keychain react-native-biometrics

iOS Setup

cd ios && pod install

Add to your Info.plist:

<key>NSFaceIDUsageDescription</key>
<string>Authenticate to sign in to PersonQL</string>

Android Setup

No additional setup required for basic functionality.

Quick Start

1. Setup Provider

Wrap your app with PersonQLProvider:

import { PersonQLProvider } from '@personql/react-native';

function App() {
  return (
    <PersonQLProvider apiUrl="https://gateway.personql.com">
      <AppContent />
    </PersonQLProvider>
  );
}

2. Use the Pre-built SignInScreen

import { SignInScreen } from '@personql/react-native';
import { useState } from 'react';

function AuthFlow() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  if (!isAuthenticated) {
    return (
      <SignInScreen
        apiUrl="https://gateway.personql.com"
        onSignInSuccess={() => setIsAuthenticated(true)}
        enableBiometric
      />
    );
  }

  return <Dashboard />;
}

3. Or Build Custom Auth UI

import { useAuth } from '@personql/react-native';
import { View, TextInput, Button, Text } from 'react-native';
import { useState } from 'react';

function CustomSignIn() {
  const {
    signIn,
    signInWithBiometric,
    loading,
    error,
    biometricAvailable,
  } = useAuth({
    apiUrl: 'https://gateway.personql.com',
    enableBiometric: true,
  });

  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSignIn = async () => {
    try {
      await signIn({ email, password });
      // Navigate to app
    } catch (error) {
      console.error('Sign in failed:', error);
    }
  };

  return (
    <View>
      <TextInput
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        autoCapitalize="none"
      />
      <TextInput
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
      />
      {error && <Text>{error.message}</Text>}
      <Button title="Sign In" onPress={handleSignIn} disabled={loading} />

      {biometricAvailable && (
        <Button
          title="Sign In with Biometric"
          onPress={signInWithBiometric}
          disabled={loading}
        />
      )}
    </View>
  );
}

API Reference

PersonQLProvider

Context provider that initializes PersonQL client.

<PersonQLProvider
  apiUrl="https://gateway.personql.com"
  clientId="optional-client-id"
>
  {children}
</PersonQLProvider>

Props:

  • apiUrl (required): Your PersonQL API endpoint
  • clientId (optional): Client identifier for tracking
  • children: React components

useAuth Hook

Manages authentication state and operations.

const {
  // State
  isAuthenticated,
  user,
  loading,
  error,
  biometricAvailable,

  // Auth methods
  signIn,
  signUp,
  signOut,
  signInWithBiometric,

  // Token methods
  refreshToken,
  isTokenExpired,

  // Biometric methods
  enableBiometricAuth,
  disableBiometricAuth,

  // Utility
  clearError,
} = useAuth({
  apiUrl: 'https://gateway.personql.com',
  enableBiometric: true,
  biometricPrompt: 'Authenticate to continue',
  onAuthStateChange: (isAuth) => console.log('Auth changed:', isAuth),
});

Configuration:

  • apiUrl (required): Your PersonQL API endpoint
  • enableBiometric (optional): Enable biometric authentication
  • biometricPrompt (optional): Custom biometric prompt message
  • onAuthStateChange (optional): Callback when auth state changes

Methods:

signIn(credentials)

Sign in with email and password.

await signIn({
  email: '[email protected]',
  password: 'password123',
});

signUp(credentials)

Create a new account.

await signUp({
  email: '[email protected]',
  password: 'password123',
  firstName: 'John',
  lastName: 'Doe',
});

signOut()

Sign out the current user.

await signOut();

signInWithBiometric()

Authenticate using biometrics (Face ID, Touch ID, Fingerprint).

await signInWithBiometric();

refreshToken()

Manually refresh the access token.

await refreshToken();

isTokenExpired()

Check if the current token is expired.

const expired = await isTokenExpired();

useOrganization Hook

Manages multi-tenant organizations and permissions.

const {
  // State
  currentOrganization,
  organizations,
  loading,
  switching,
  error,
  currentRole,
  members,
  roles,

  // Methods
  fetchOrganizations,
  switchOrganization,
  createOrganization,
  updateOrganization,
  hasPermission,
  loadMembers,
  loadRoles,
  refreshOrganizations,
  clearError,
} = useOrganization();

Methods:

fetchOrganizations()

Load all organizations for the current user.

await fetchOrganizations();

switchOrganization(organizationId)

Switch to a different organization.

await switchOrganization('org_123');

createOrganization(data)

Create a new organization.

const org = await createOrganization({
  name: 'My Company',
  slug: 'my-company',
});

updateOrganization(organizationId, updates)

Update organization details.

const updated = await updateOrganization('org_123', {
  name: 'New Name',
  primary_color: '#007AFF',
});

hasPermission(permission)

Check if current user has a specific permission.

if (hasPermission('users:write')) {
  // User can create/edit users
}

if (hasPermission('billing:*')) {
  // User has all billing permissions
}

Permission Format:

  • resource:action - e.g., users:read, billing:write
  • resource:* - All actions for a resource
  • * - All permissions (superuser)

loadMembers(organizationId?)

Load members of an organization.

await loadMembers(); // Current org
await loadMembers('org_123'); // Specific org

loadRoles(organizationId?)

Load roles for an organization.

await loadRoles(); // Current org
await loadRoles('org_123'); // Specific org

SignInScreen Component

Pre-built sign-in screen with email/password and biometric authentication.

<SignInScreen
  apiUrl="https://gateway.personql.com"
  onSignInSuccess={() => navigation.navigate('Dashboard')}
  onSignUpPress={() => navigation.navigate('SignUp')}
  onForgotPasswordPress={() => navigation.navigate('ForgotPassword')}
  Logo={MyLogoComponent}
  primaryColor="#007AFF"
  enableBiometric
  biometricPrompt="Sign in to continue"
/>

Props:

  • apiUrl (required): Your PersonQL API endpoint
  • onSignInSuccess: Callback when sign in succeeds
  • onSignUpPress: Callback when user taps "Sign Up"
  • onForgotPasswordPress: Callback when user taps "Forgot Password"
  • Logo: Custom logo component
  • primaryColor: Theme color for buttons and links
  • enableBiometric: Enable biometric authentication
  • biometricPrompt: Custom biometric prompt message

SecureStorage

Low-level secure storage using native Keychain/Keystore.

import { SecureStorage } from '@personql/react-native';

const storage = new SecureStorage({
  service: 'com.myapp', // Optional
});

// Store
await storage.setItem('key', 'value');

// Retrieve
const value = await storage.getItem('key');

// Remove
await storage.removeItem('key');

// Clear all
await storage.clear();

// Check availability
const available = await storage.isAvailable();

TokenManager

Manages authentication tokens securely.

import { TokenManager } from '@personql/react-native';

const tokenManager = new TokenManager();

// Save tokens
await tokenManager.saveTokens(
  'access_token',
  'refresh_token',
  Date.now() + 3600000 // Expiry timestamp
);

// Get tokens
const accessToken = await tokenManager.getAccessToken();
const refreshToken = await tokenManager.getRefreshToken();

// Check expiry
const expired = await tokenManager.isTokenExpired();

// Clear tokens
await tokenManager.clearTokens();

// Organization management
await tokenManager.saveCurrentOrganization('org_123');
const orgId = await tokenManager.getCurrentOrganization();
await tokenManager.clearCurrentOrganization();

BiometricAuth

Biometric authentication wrapper.

import { BiometricAuth } from '@personql/react-native';

const biometric = new BiometricAuth();

// Check availability
const capabilities = await biometric.checkAvailability();
if (capabilities.available) {
  console.log('Biometry type:', capabilities.biometryType);
  // 'FaceID', 'TouchID', or 'Biometrics'
}

// Authenticate
const success = await biometric.authenticate('Sign in to continue');

// Get biometry type name
const typeName = await biometric.getBiometryTypeName();
// "Face ID", "Touch ID", or "Biometric Authentication"

// Advanced: Create keys for signing
const keys = await biometric.createKeys();
if (keys) {
  console.log('Public key:', keys.publicKey);
}

// Advanced: Create signature
const signature = await biometric.createSignature('data_to_sign');

// Delete keys
await biometric.deleteKeys();

Multi-Tenant Examples

Organization Switcher

import { useOrganization } from '@personql/react-native';
import { View, Text, TouchableOpacity, FlatList } from 'react-native';

function OrganizationSwitcher() {
  const {
    currentOrganization,
    organizations,
    switchOrganization,
    switching,
  } = useOrganization();

  return (
    <FlatList
      data={organizations}
      keyExtractor={(org) => org.id}
      renderItem={({ item }) => (
        <TouchableOpacity
          onPress={() => switchOrganization(item.id)}
          disabled={switching || item.id === currentOrganization?.id}
        >
          <Text>{item.name}</Text>
          {item.id === currentOrganization?.id && <Text>✓ Active</Text>}
        </TouchableOpacity>
      )}
    />
  );
}

Permission-Based Rendering

import { useOrganization } from '@personql/react-native';
import { View, Button } from 'react-native';

function UserManagement() {
  const { hasPermission } = useOrganization();

  return (
    <View>
      {hasPermission('users:read') && (
        <Button title="View Users" onPress={viewUsers} />
      )}

      {hasPermission('users:write') && (
        <Button title="Add User" onPress={addUser} />
      )}

      {hasPermission('users:delete') && (
        <Button title="Delete User" onPress={deleteUser} />
      )}
    </View>
  );
}

Create Organization

import { useOrganization } from '@personql/react-native';
import { View, TextInput, Button } from 'react-native';
import { useState } from 'react';

function CreateOrganization() {
  const { createOrganization } = useOrganization();
  const [name, setName] = useState('');
  const [slug, setSlug] = useState('');

  const handleCreate = async () => {
    try {
      const org = await createOrganization({ name, slug });
      console.log('Created:', org);
      // Navigation or success handling
    } catch (error) {
      console.error('Failed to create organization:', error);
    }
  };

  return (
    <View>
      <TextInput
        placeholder="Organization Name"
        value={name}
        onChangeText={setName}
      />
      <TextInput
        placeholder="Slug (URL-friendly)"
        value={slug}
        onChangeText={setSlug}
        autoCapitalize="none"
      />
      <Button title="Create" onPress={handleCreate} />
    </View>
  );
}

Types

User

interface User {
  id: string;
  email: string;
  firstName?: string;
  lastName?: string;
  avatar?: string;
  createdAt: string;
  updatedAt: string;
}

Organization

interface Organization {
  id: string;
  name: string;
  slug: string;
  domain?: string;
  logo_url?: string;
  primary_color?: string;
  plan_type: 'free' | 'starter' | 'pro' | 'enterprise';
  max_users: number;
  max_api_calls: number;
  settings?: Record<string, any>;
  status: 'active' | 'suspended' | 'deleted';
  created_at: string;
  updated_at: string;
}

Role

interface Role {
  id: string;
  organization_id: string;
  name: string;
  description?: string;
  permissions: string[];
  is_system: boolean;
  created_at: string;
  updated_at: string;
}

OrganizationMember

interface OrganizationMember {
  id: string;
  organization_id: string;
  user_id: string;
  role_id: string;
  status: 'pending' | 'active' | 'suspended';
  created_at: string;
  updated_at: string;
}

Security Best Practices

  1. Never log sensitive data - Avoid logging tokens, passwords, or biometric data
  2. Use HTTPS - Always use HTTPS endpoints in production
  3. Token expiry - Tokens expire automatically; use refreshToken() as needed
  4. Biometric fallback - Always provide password fallback for biometric auth
  5. Keychain access - Data is encrypted at rest using native APIs
  6. Permission checking - Always check permissions before showing/allowing actions

Troubleshooting

iOS Biometric Not Working

Make sure you've added NSFaceIDUsageDescription to Info.plist:

<key>NSFaceIDUsageDescription</key>
<string>Authenticate to sign in</string>

Android Keystore Issues

If you encounter keystore errors, try clearing app data or reinstalling.

Token Refresh Fails

Ensure your refresh token hasn't expired. Tokens have a limited lifetime.

Organization Not Switching

Make sure the user has access to the organization and it's in active status.

Support

License

MIT License - see LICENSE file for details