The useSSOCallback and useSSORedirect hooks handle OAuth provider callbacks and manage the authentication flow after users return from external OAuth providers.

Import

import { useSSOCallback, useSSORedirect } from '@snipextt/wacht';

Hooks Overview

useSSOCallback

Automatically processes OAuth callback parameters and handles authentication completion.
import { useSSOCallback } from '@snipextt/wacht';

function OAuthCallbackPage() {
  const {
    loading,
    error,
    session,
    redirectUri,
    processed,
    requiresCompletion,
    requiresVerification,
    requires2FA,
    completeOAuthSignup,
    completeVerification
  } = useSSOCallback({
    onSuccess: (session, redirectUri) => {
      console.log('OAuth successful', session);
      // Handle successful authentication
    },
    onError: (error) => {
      console.error('OAuth failed', error);
    },
    onRequiresCompletion: (signupAttempt, session) => {
      console.log('Additional info needed');
    },
    autoRedirect: true
  });

  if (loading) {
    return <div>Processing authentication...</div>;
  }

  if (error) {
    return (
      <div>
        <h2>Authentication Failed</h2>
        <p>{error.message}</p>
        <a href="/signin">Try Again</a>
      </div>
    );
  }

  if (requiresCompletion) {
    return <ProfileCompletionForm onComplete={completeOAuthSignup} />;
  }

  if (requiresVerification) {
    return <VerificationForm onVerify={completeVerification} />;
  }

  if (requires2FA) {
    return <TwoFactorForm />;
  }

  return <div>Redirecting...</div>;
}

useSSORedirect

Provides manual control over SSO redirects.
import { useSSORedirect } from '@snipextt/wacht';

function SSORedirectHandler() {
  const { redirect } = useSSORedirect();

  const handleRedirect = () => {
    // Redirect to a custom URL
    redirect('https://myapp.com/dashboard');
    
    // Or redirect to default location
    redirect();
  };

  return (
    <button onClick={handleRedirect}>
      Continue to App
    </button>
  );
}

Properties

useSSOCallback

loading
boolean
Whether the OAuth callback is being processed
error
Error | null
Any error that occurred during OAuth processing
session
Session | null
The authenticated session after successful OAuth
redirectUri
string | null
The redirect URI to navigate to after authentication
processed
boolean
Whether the OAuth callback has been processed
signupAttempt
any | null
The signup attempt data if additional info is needed
signinAttempt
any | null
The signin attempt data if 2FA is required
requiresCompletion
boolean
Whether profile completion is required
requiresVerification
boolean
Whether email/phone verification is required
requires2FA
boolean
Whether two-factor authentication is required
completionLoading
boolean
Whether a completion operation is in progress
completionError
Error | null
Any error from completion operations

Options

SSOCallbackOptions

onSuccess
(session: Session, redirectUri?: string) => void
Callback when OAuth authentication is successful
onError
(error: Error) => void
Callback when OAuth authentication fails
onRequiresCompletion
(signupAttempt: any, session: Session) => void
Callback when additional profile information is needed
autoRedirect
boolean
Whether to automatically redirect after successful authentication. Default: true

Methods

completeOAuthSignup()

Completes OAuth signup with additional profile information.
data
OAuthCompletionData
required
Additional profile data required for signup
const { completeOAuthSignup } = useSSOCallback();

const success = await completeOAuthSignup({
  first_name: 'John',
  last_name: 'Doe',
  username: 'johndoe',
  phone_number: '+1234567890'
});

completeVerification()

Completes verification with the provided code.
code
string
required
The verification code
const { completeVerification } = useSSOCallback();

const success = await completeVerification('123456');

prepareVerification()

Prepares verification for a specific strategy.
strategy
string
required
The verification strategy (email_otp, phone_otp)
const { prepareVerification } = useSSOCallback();

const success = await prepareVerification('email_otp');

redirect()

Manually redirects after OAuth completion.
customRedirectUri
string
Optional custom redirect URI
const { redirect } = useSSORedirect();

// Redirect to custom URL
redirect('https://myapp.com/welcome');

// Redirect to default location
redirect();

Examples

Complete OAuth Flow with Profile Completion

function OAuthCallbackHandler() {
  const {
    loading,
    error,
    requiresCompletion,
    signupAttempt,
    completeOAuthSignup,
    completionLoading,
    completionError
  } = useSSOCallback({
    onSuccess: (session, redirectUri) => {
      // Handle successful authentication
      window.location.href = redirectUri || '/dashboard';
    },
    autoRedirect: false // Manual control over redirects
  });

  const handleProfileCompletion = async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    
    const success = await completeOAuthSignup({
      first_name: formData.get('firstName'),
      last_name: formData.get('lastName'),
      username: formData.get('username')
    });
    
    if (success) {
      window.location.href = '/dashboard';
    }
  };

  if (loading) {
    return (
      <div className="oauth-loading">
        <div className="spinner" />
        <p>Completing sign in with your provider...</p>
      </div>
    );
  }

  if (error) {
    return (
      <div className="oauth-error">
        <h2>Authentication Failed</h2>
        <p>{error.message}</p>
        <a href="/signin" className="button">
          Back to Sign In
        </a>
      </div>
    );
  }

  if (requiresCompletion && signupAttempt) {
    return (
      <div className="profile-completion">
        <h2>Complete Your Profile</h2>
        <p>Just a few more details to finish setting up your account.</p>
        
        <form onSubmit={handleProfileCompletion}>
          {signupAttempt.missing_fields?.includes('first_name') && (
            <input
              name="firstName"
              placeholder="First Name"
              required
            />
          )}
          
          {signupAttempt.missing_fields?.includes('last_name') && (
            <input
              name="lastName"
              placeholder="Last Name"
              required
            />
          )}
          
          {signupAttempt.missing_fields?.includes('username') && (
            <input
              name="username"
              placeholder="Username"
              pattern="[a-zA-Z0-9_-]+"
              required
            />
          )}
          
          {completionError && (
            <p className="error">{completionError.message}</p>
          )}
          
          <button type="submit" disabled={completionLoading}>
            {completionLoading ? 'Saving...' : 'Complete Setup'}
          </button>
        </form>
      </div>
    );
  }

  return <div>Redirecting to your dashboard...</div>;
}

OAuth with Verification Flow

function OAuthWithVerification() {
  const {
    requiresVerification,
    signupAttempt,
    completeVerification,
    prepareVerification,
    completionLoading
  } = useSSOCallback();

  const [verificationCode, setVerificationCode] = useState('');
  const [verificationSent, setVerificationSent] = useState(false);

  const handleSendVerification = async () => {
    const strategy = signupAttempt.current_step === 'verify_email' 
      ? 'email_otp' 
      : 'phone_otp';
      
    const success = await prepareVerification(strategy);
    if (success) {
      setVerificationSent(true);
    }
  };

  const handleVerification = async (e) => {
    e.preventDefault();
    const success = await completeVerification(verificationCode);
    
    if (success) {
      // Will auto-redirect or trigger onSuccess callback
    }
  };

  if (!requiresVerification) {
    return null;
  }

  return (
    <div className="verification-required">
      <h2>Verify Your {signupAttempt.current_step === 'verify_email' ? 'Email' : 'Phone'}</h2>
      
      {!verificationSent ? (
        <div>
          <p>We need to verify your contact information.</p>
          <button onClick={handleSendVerification}>
            Send Verification Code
          </button>
        </div>
      ) : (
        <form onSubmit={handleVerification}>
          <input
            type="text"
            value={verificationCode}
            onChange={(e) => setVerificationCode(e.target.value)}
            placeholder="Enter 6-digit code"
            maxLength="6"
            pattern="[0-9]{6}"
            required
          />
          <button type="submit" disabled={completionLoading}>
            {completionLoading ? 'Verifying...' : 'Verify'}
          </button>
        </form>
      )}
    </div>
  );
}

Custom Error Handling

function OAuthErrorBoundary() {
  const [retryCount, setRetryCount] = useState(0);
  
  useSSOCallback({
    onError: (error) => {
      // Log error to monitoring service
      console.error('OAuth Error:', error);
      
      // Handle specific error types
      if (error.message.includes('OAuth Error: access_denied')) {
        // User cancelled OAuth flow
        window.location.href = '/signin';
      } else if (retryCount < 3) {
        // Retry the OAuth flow
        setRetryCount(prev => prev + 1);
        window.location.reload();
      } else {
        // Show error UI after retries exhausted
      }
    },
    onSuccess: (session) => {
      // Reset retry count on success
      setRetryCount(0);
    }
  });

  return <div>Processing authentication...</div>;
}

Notes

  • The hook automatically extracts OAuth parameters from the URL
  • OAuth errors include both provider errors and application errors
  • Profile completion is typically required for OAuth signups missing required fields
  • Two-factor authentication may be required based on user security settings
  • The hook handles staging mode development sessions automatically
  • Auto-redirect can be disabled for manual control over navigation
  • Verification flows support both email and phone verification