Skip to content

Latest commit

 

History

History
347 lines (277 loc) · 11.3 KB

EXAMPLES-WEB.md

File metadata and controls

347 lines (277 loc) · 11.3 KB

React Native Auth0 for Web: Examples

This guide provides usage examples specifically for developers targeting React Native Web. The web platform uses the underlying @auth0/auth0-spa-js library, and its features are aligned with browser security best practices.

1. The Hooks-Based Approach (Recommended)

This is the simplest and recommended way to integrate Auth0. The Auth0Provider handles all the complexity of the redirect flow automatically.

Step 1: Wrap Your App in the Auth0Provider

In your main application entry point (e.g., App.tsx), wrap your application with the provider.

// src/App.tsx
import React from 'react';
import { Auth0Provider } from 'react-native-auth0';
import config from './auth0-configuration';
import MainComponent from './MainComponent'; // Your main app UI

const App = () => (
  <Auth0Provider domain={config.domain} clientId={config.clientId}>
    <MainComponent />
  </Auth0Provider>
);

export default App;

Step 2: Use the useAuth0 Hook in Your Components

The Auth0Provider will automatically handle the redirect callback when your app loads. The useAuth0 hook will then provide the authentication state.

// src/MainComponent.tsx
import React from 'react';
import { useAuth0 } from 'react-native-auth0';
import { View, Button, Text, ActivityIndicator } from 'react-native';

const MainComponent = () => {
  const { authorize, clearSession, user, isLoading, error } = useAuth0();

  const onLogin = async () => {
    try {
      // This triggers a redirect to the Auth0 Universal Login page.
      await authorize({ scope: 'openid profile email' });
    } catch (e) {
      console.error('Login error:', e);
    }
  };

  const onLogout = async () => {
    try {
      await clearSession();
    } catch (e) {
      console.error('Logout error:', e);
    }
  };

  if (isLoading) {
    return (
      <View>
        <ActivityIndicator size="large" />
      </View>
    );
  }

  return (
    <View>
      {error && <Text>Error: {error.message}</Text>}
      {user ? (
        <>
          <Text>Welcome, {user.name}!</Text>
          <Button title="Log Out" onPress={onLogout} />
        </>
      ) : (
        <Button title="Log In" onPress={onLogin} />
      )}
    </View>
  );
};

2. The Class-Based / Manual Approach

If you are not using React Hooks or need more fine-grained control, you can instantiate the Auth0 class and handle the redirect callback manually.

Step 1: Instantiate the Auth0 Client

Create a singleton instance of the client.

// src/api/auth0.ts
import Auth0 from 'react-native-auth0';
import config from '../auth0-configuration';

const auth0 = new Auth0({
  domain: config.domain,
  clientId: config.clientId,
});

export default auth0;

Step 2: Handle the Redirect Callback

In your application's root component or entry point, you need to add logic to process the result from Auth0 after the user is redirected back.

// src/App.tsx
import React, { useEffect, useState } from 'react';
import { View, Button, Text } from 'react-native';
import auth0 from './api/auth0'; // Import your singleton
import type { User } from 'react-native-auth0';

const App = () => {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const checkSession = async () => {
      // Check if the URL contains redirect parameters
      if (window.location.search.includes('code=') && window.location.search.includes('state=')) {
        try {
          // Process the redirect
          await auth0.webAuth.handleRedirectCallback();
        } catch (e) {
          console.error(e);
        }
        // Clean the URL
        window.history.replaceState({}, document.title, '/');
      }

      // After handling a potential redirect, check for an existing session
      try {
        const credentials = await auth0.credentialsManager.getCredentials();
        // Assuming you have a way to decode the idToken to get the user
        // const decodedUser = jwt_decode(credentials.idToken);
        // setUser(decodedUser);
      } catch (e) {
        // No credentials, user is not logged in
        setUser(null);
      }
      setIsLoading(false);
    };

    checkSession();
  }, []);

  const onLogin = async () => {
    await auth0.webAuth.authorize({ scope: 'openid profile email' });
  };

  const onLogout = async () => {
    await auth0.webAuth.clearSession();
  };

  // ... Render UI based on isLoading and user state ...
};

3. MFA Flexible Factors Grant (Web)

The MFA Flexible Factors Grant is fully supported on the web platform. It uses the @auth0/auth0-spa-js MFA API under the hood.

Using MFA with Hooks

import React, { useState } from 'react';
import { View, Button, TextInput, Text } from 'react-native';
import { useAuth0, MfaError, MfaErrorCodes } from 'react-native-auth0';

function MfaScreen({ mfaToken }: { mfaToken: string }) {
  const { mfa } = useAuth0();
  const [otp, setOtp] = useState('');

  const listAuthenticators = async () => {
    try {
      const authenticators = await mfa.getAuthenticators({ mfaToken });
      console.log('Authenticators:', authenticators);
    } catch (error) {
      if (error instanceof MfaError) {
        console.error('MFA error:', error.type, error.message);
      }
    }
  };

  const enrollTotp = async () => {
    try {
      const challenge = await mfa.enroll({ mfaToken, factorType: 'otp' });
      if (challenge.type === 'totp') {
        console.log('Scan QR:', challenge.barcodeUri);
        console.log('Secret:', challenge.secret);
      }
    } catch (error) {
      if (error instanceof MfaError) {
        console.error('Enrollment error:', error.type);
      }
    }
  };

  const verifyOtp = async () => {
    try {
      const credentials = await mfa.verify({ mfaToken, otp });
      console.log('Authenticated!');
    } catch (error) {
      if (error instanceof MfaError) {
        switch (error.type) {
          case MfaErrorCodes.INVALID_OTP:
            console.log('Incorrect code');
            break;
          case MfaErrorCodes.TOO_MANY_ATTEMPTS:
            console.log('Too many attempts');
            break;
          case MfaErrorCodes.EXPIRED_MFA_TOKEN:
            console.log('Session expired');
            break;
        }
      }
    }
  };

  return (
    <View>
      <Button title="List Authenticators" onPress={listAuthenticators} />
      <Button title="Enroll TOTP" onPress={enrollTotp} />
      <TextInput placeholder="Enter OTP" value={otp} onChangeText={setOtp} />
      <Button title="Verify" onPress={verifyOtp} />
    </View>
  );
}

Using MFA with Auth0 Class

import Auth0, { MfaError, MfaErrorCodes } from 'react-native-auth0';

const auth0 = new Auth0({
  domain: 'YOUR_AUTH0_DOMAIN',
  clientId: 'YOUR_AUTH0_CLIENT_ID',
});

// List authenticators
const authenticators = await auth0.mfa.getAuthenticators({
  mfaToken: 'mfa_token',
});

// Enroll TOTP
const challenge = await auth0.mfa.enroll({
  mfaToken: 'mfa_token',
  factorType: 'otp',
});

// Enroll SMS
const smsChallenge = await auth0.mfa.enroll({
  mfaToken: 'mfa_token',
  factorType: 'sms',
  phoneNumber: '+12025550135',
});

// Enroll push notification (Auth0 Guardian) — web returns barcodeUri + oobCode
const pushChallenge = await auth0.mfa.enroll({
  mfaToken: 'mfa_token',
  factorType: 'push',
});

// Challenge an authenticator
const challengeResult = await auth0.mfa.challenge({
  mfaToken: 'mfa_token',
  authenticatorId: 'sms|dev_123',
});

// Verify OTP
const credentials = await auth0.mfa.verify({
  mfaToken: 'mfa_token',
  otp: '123456',
});

4. Passkeys (Web)

Passkeys are supported on web via @auth0/auth0-spa-js. The flow is the same three steps as native (challenge → WebAuthn ceremony → exchange), but step 2 uses the browser's built-in WebAuthn API (navigator.credentials.create()/.get()) instead of a native module — the app calls it directly, the SDK does not perform this step for you. getTokenByPasskey's authResponse accepts the raw PublicKeyCredential returned by navigator.credentials directly on web — no manual serialization needed (unlike native, which takes a JSON string; see Signup with Passkey (Web) in EXAMPLES.md for the full signup example).

Secure context requirement: navigator.credentials.create()/.get() only work over HTTPS (or localhost for local development). Apps served over plain HTTP will fail at the WebAuthn ceremony with a SecurityError.

Because navigator.credentials.create()/.get() require a user gesture, call passkeySignupChallenge / passkeyLoginChallenge from within a click handler (not, for example, from a useEffect).

import { useAuth0, PasskeyError } from 'react-native-auth0';

function PasskeyLoginButton() {
  const { passkeyLoginChallenge, getTokenByPasskey } = useAuth0();

  const handleLogin = async () => {
    try {
      const challenge = await passkeyLoginChallenge({
        realm: 'Username-Password-Authentication',
      });

      // App calls navigator.credentials directly (not wrapped by SDK)
      const credential = (await navigator.credentials.get({
        publicKey:
          challenge.authParamsPublicKey as PublicKeyCredentialRequestOptions,
      })) as PublicKeyCredential | null;

      if (!credential) {
        // User cancelled or no credential available
        throw new Error('No passkey credential returned');
      }

      const credentials = await getTokenByPasskey({
        authSession: challenge.authSession,
        authResponse: credential,
        realm: 'Username-Password-Authentication',
      });

      console.log('Signed in with passkey');
    } catch (error) {
      if (error instanceof PasskeyError) {
        console.error('Passkey login failed:', error.type, error.message);

        // Handle MFA required scenario
        const mfaPayload = error.getMfaRequiredPayload();
        if (mfaPayload) {
          console.log('MFA required. Token:', mfaPayload.mfaToken);
          console.log('Available factors:', mfaPayload.mfaRequirements);
          // Continue with mfa.challenge() and mfa.verify()
        }
      }
    }
  };

  return <button onClick={handleLogin}>Sign In with Passkey</button>;
}

Web Platform Notes

The web platform supports direct authentication grants including auth.passwordRealm(), auth.createUser(), auth.resetPassword(), and the MFA Flexible Factors Grant. These methods make direct HTTP calls to the Auth0 API.

Token refresh is handled automatically by credentialsManager.getCredentials() on the web. The auth.refreshToken() method is not available.

My Account API (Web)

The My Account API is supported on the web platform. The auth0.myAccount client works the same way as on native, so the examples in EXAMPLES.md apply. Passkey enrollment on the web uses the browser's WebAuthn APIs instead of a native passkey module.

When DPoP is enabled (the default), a My Account call on the web only succeeds when the supplied access token was issued by the same client instance, because the DPoP proof is signed with that client's keypair. When the client is not configured with DPoP, plain bearer tokens are used and any valid access token works.