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

@lifewind-ltda/react-native-sso-client

v1.0.0

Published

React Native SSO client for LifeWind authentication

Downloads

9

Readme

LifeWind React Native SSO Client

SSO authentication for React Native / Expo applications.

Provider, hooks, and components for OAuth 2.0 authentication with LifeWind in mobile apps.

Prerequisites: Register Your App in LifeWind Core

Before installing this package, you need an OAuth client registered in LifeWind Core:

  1. Login to the LifeWind Core admin panel at https://lifewind-core.test/admin (or your production URL)
  2. Navigate to OAuth ClientsCreate Client
  3. Fill in:
    • Name: Your app name (e.g. "Atlas Mobile")
    • Redirect URI: Your app's deep link scheme (e.g. myapp://sso/callback or exp://192.168.1.10:8081/--/sso/callback for Expo dev)
    • Grant Type: Authorization Code
  4. After creation, copy the Client ID — you'll need it for your app config

Tip: For Expo development, use exp://YOUR_LOCAL_IP:8081/--/sso/callback as the redirect URI. For production builds, use your registered deep link scheme like myapp://sso/callback.

Backend Requirement

This package works together with the Laravel SSO Client package on your backend:

composer require lifewind/laravel-sso-client

The backend provides the JWT token exchange endpoints (/sso/validate, /sso/user, /sso/refresh) that this package communicates with. See the lifewind-laravel-sso-client README for backend setup.

Features

  • React Context Provider: <SSOProvider> manages all auth state with AsyncStorage persistence
  • useSSO Hook: Access authentication state and actions from any component
  • SSOButton Component: Native Pressable button with loading state
  • Expo Integration: Uses expo-web-browser for the OAuth flow
  • AsyncStorage: Persistent token storage across app restarts
  • TypeScript: Full type safety

Quick Start

1. Installation

npm install lifewind-react-native-sso-client

Install peer dependencies:

npx expo install expo-auth-session expo-web-browser @react-native-async-storage/async-storage

2. Wrap Your App with SSOProvider

// App.tsx
import { SSOProvider } from "lifewind-react-native-sso-client"
import { NavigationContainer } from "@react-navigation/native"
import AppNavigator from "./navigation/AppNavigator"

const ssoConfig = {
  baseUrl: "https://lifewind-core.your-domain.com",
  clientId: "your_oauth_client_id",
  redirectUri: "myapp://sso/callback",
  backendApiUrl: "https://your-backend-api.com",
}

export default function App() {
  return (
    <SSOProvider config={ssoConfig}>
      <NavigationContainer>
        <AppNavigator />
      </NavigationContainer>
    </SSOProvider>
  )
}

3. Add Login Button

// screens/LoginScreen.tsx
import { View, Text, StyleSheet } from "react-native"
import { SSOButton } from "lifewind-react-native-sso-client"

export default function LoginScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome</Text>
      <SSOButton
        title="Sign in with LifeWind"
        onError={(err) => console.error(err)}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: "center", padding: 24 },
  title: { fontSize: 28, fontWeight: "bold", textAlign: "center", marginBottom: 32 },
})

4. Handle the OAuth Callback

After the user authenticates in the browser, your app receives a deep link with code and state parameters. Handle this with Expo's linking or React Navigation:

// App.tsx or navigation setup
import { useEffect } from "react"
import * as Linking from "expo-linking"
import { useSSO } from "lifewind-react-native-sso-client"

function AuthHandler() {
  const { handleCallback } = useSSO()

  useEffect(() => {
    const subscription = Linking.addEventListener("url", async ({ url }) => {
      const parsed = Linking.parse(url)
      const code = parsed.queryParams?.code as string
      const state = parsed.queryParams?.state as string

      if (code && state) {
        try {
          const result = await handleCallback(code, state)
          console.log("Authenticated:", result.user)
          // Navigate to home screen
        } catch (err) {
          console.error("Auth failed:", err)
        }
      }
    })

    return () => subscription.remove()
  }, [handleCallback])

  return null
}

Components

SSOButton

Native login button using React Native Pressable with ActivityIndicator.

<SSOButton
  title="Sign In"
  loadingTitle="Signing in..."
  style={{ backgroundColor: "#2563eb", borderRadius: 12 }}
  textStyle={{ fontSize: 18 }}
  onError={(error) => Alert.alert("Error", error)}
/>

Props:

| Prop | Type | Default | Description | |------|------|---------|-------------| | title | string | "Login with LifeWind" | Button label | | loadingTitle | string | "Authenticating..." | Label during auth | | style | ViewStyle | Dark button style | Custom button styles | | textStyle | TextStyle | White text, 16px | Custom text styles | | onError | (error: string) => void | — | Called on error |

useSSO Hook

Access all authentication state and actions:

import { useSSO } from "lifewind-react-native-sso-client"

function ProfileScreen() {
  const {
    // State
    isReady,         // boolean — true after initial AsyncStorage load
    isAuthenticated, // boolean
    isLoading,       // boolean
    user,            // SSOUser | null
    token,           // string | null
    error,           // string | null

    // Actions
    login,           // () => Promise<void> — opens browser for OAuth
    logout,          // () => Promise<void> — clears state + AsyncStorage
    handleCallback,  // (code, state) => Promise<SSOResult>
    refreshToken,    // () => Promise<void>
    getCurrentUser,  // () => Promise<SSOUser>
    clearError,      // () => void
  } = useSSO()

  if (!isReady) return <ActivityIndicator />

  if (!isAuthenticated) {
    return <SSOButton onError={(e) => Alert.alert("Error", e)} />
  }

  return (
    <View>
      <Text>Welcome, {user?.name}!</Text>
      <Text>{user?.email}</Text>
      <Button title="Sign Out" onPress={logout} />
    </View>
  )
}

Note: isReady starts as false and becomes true after the initial AsyncStorage load completes. Use this to show a splash/loading screen while persisted auth state is being restored.

Configuration

SSOConfig

interface SSOConfig {
  baseUrl: string       // LifeWind Core URL (e.g. "https://lifewind-core.test")
  clientId: string      // OAuth client ID from admin panel
  redirectUri: string   // Deep link callback (e.g. "myapp://sso/callback")
  backendApiUrl: string // Your Laravel backend URL (e.g. "https://api.your-app.com")
  scopes?: string[]     // OAuth scopes (default: ["openid", "profile", "email"])
}

Deep Link Setup (Expo)

Add your deep link scheme in app.json:

{
  "expo": {
    "scheme": "myapp"
  }
}

This allows your app to handle URLs like myapp://sso/callback?code=...&state=....

For development with Expo Go, the redirect URI is:

exp://YOUR_LOCAL_IP:8081/--/sso/callback

TypeScript Types

interface SSOUser {
  id: string
  email: string
  name: string
  lifewind_uuid?: string
}

interface SSOResult {
  user: SSOUser
  token: string
  expires_in: number
}

Authentication Flow

  1. User taps login<SSOButton> or login() opens an in-app browser via expo-web-browser
  2. User authenticates — LifeWind redirects back to your deep link with a code
  3. Deep link captured — Your app receives the URL via Linking.addEventListener
  4. Token exchangehandleCallback(code, state) sends code to your backend's /sso/validate
  5. JWT returned — Backend validates with LifeWind and returns JWT + user data
  6. State persisted — Token and user stored in AsyncStorage and React context
  7. ReadyuseSSO() returns isAuthenticated: true, data persists across restarts

Advanced Usage

Navigation Guard

import { useSSO } from "lifewind-react-native-sso-client"

function AppNavigator() {
  const { isReady, isAuthenticated } = useSSO()

  if (!isReady) {
    return <SplashScreen />
  }

  return (
    <Stack.Navigator>
      {isAuthenticated ? (
        <>
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Profile" component={ProfileScreen} />
        </>
      ) : (
        <Stack.Screen name="Login" component={LoginScreen} />
      )}
    </Stack.Navigator>
  )
}

API Requests with Token

import { useSSO } from "lifewind-react-native-sso-client"

function useApi() {
  const { token } = useSSO()

  const fetchWithAuth = async (url: string, options: RequestInit = {}) => {
    const response = await fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: "application/json",
        "Content-Type": "application/json",
        ...options.headers,
      },
    })

    if (!response.ok) throw new Error(`API Error: ${response.statusText}`)
    return response.json()
  }

  return { fetchWithAuth }
}

Refresh Token on App Foreground

import { useEffect } from "react"
import { AppState } from "react-native"
import { useSSO } from "lifewind-react-native-sso-client"

function TokenRefresher() {
  const { token, refreshToken } = useSSO()

  useEffect(() => {
    const subscription = AppState.addEventListener("change", (state) => {
      if (state === "active" && token) {
        refreshToken().catch(() => {
          console.log("Token refresh failed")
        })
      }
    })

    return () => subscription.remove()
  }, [token, refreshToken])

  return null
}

Troubleshooting

"Invalid state parameter" error:

  • State is stored in AsyncStorage — ensure the callback is handled in the same app session
  • If testing with Expo Go, make sure the redirect URI matches exp://YOUR_IP:PORT/--/sso/callback

Browser doesn't close after auth:

  • Call WebBrowser.dismissBrowser() after handling the callback
  • This is handled automatically by expo-web-browser in most cases

Deep link not being received:

  • Verify your scheme is set in app.json
  • For Expo Go, use the exp:// scheme
  • For standalone builds, use your custom scheme (e.g. myapp://)

Token not persisting across restarts:

  • Token is stored in AsyncStorage under auth_token
  • User data is stored under user_data
  • Check that @react-native-async-storage/async-storage is properly installed

"useSSO must be used within SSOProvider" error:

  • Make sure <SSOProvider> wraps your entire app (above NavigationContainer)

Peer Dependencies

| Package | Version | |---------|---------| | react | ^18.0.0 \|\| ^19.0.0 | | react-native | * | | expo-auth-session | >=5.0.0 | | expo-web-browser | >=13.0.0 | | @react-native-async-storage/async-storage | >=1.19.0 |

License

MIT License. See LICENSE for details.