# Authentication Flow

Overview of how authentication works in OrchardGo using Auth0.

## Authentication Flow Overview

OrchardGo uses Auth0 for authentication with the following flow:

1. **App Launch** - Restore persisted session data
2. **Root Navigator** - Determine navigation structure
3. **Splash Screen** - Initialize and navigate to appropriate screen
4. **Session Initialization** - Set up auth state and user data
5. **Navigation** - Route to appropriate screen based on auth state

## Flow Diagram

```
App.tsx
  ↓
Restore reactive vars (session data)
  ↓
RootNavigator
  ↓
RootSwitchNavigator
  ↓
SplashScreen
  ↓
navigateToFirstScreen()
  ↓
initializeSession()
  ↓
Navigate to:
  - LoginScreen (not authenticated)
  - WalkthroughScreen (first launch)
  - MainApp (authenticated)
```

## Key Components

### 1. App.tsx

Restores persisted session data on app launch:

```typescript
// Restore reactive variables
await restoreReactiveVars();

// Session data is stored in reactive vars
const session = sessionVar();
```

Location: `App.tsx`

### 2. RootNavigator

Sets up the navigation structure.

Location: `src/navigation/RootNavigator.js`

### 3. RootSwitchNavigator

Switches between authenticated and unauthenticated screens.

Location: `src/navigation/RootSwitchNavigator.js`

### 4. SplashScreen

Shows splash screen and calls `navigateToFirstScreen` on mount:

```typescript
useEffect(() => {
  navigateToFirstScreen();
}, []);
```

Location: `src/screens/SplashScreen/SplashScreen.js`

### 5. navigateToFirstScreen

Orchestrates the navigation based on auth state:

```typescript
const navigateToFirstScreen = async () => {
  // Initialize session
  await initializeSession();

  // Navigate based on auth state
  if (!isAuthenticated) {
    navigation.navigate('Login');
  } else if (shouldShowWalkthrough) {
    navigation.navigate('Walkthrough');
  } else {
    navigation.navigate('Main');
  }

  // Show new features overlay if needed
  if (shouldShowNewFeatures) {
    showNewFeaturesOverlay();
  }
};
```

Location: `src/hooks/auth/utils/navigateToFirstScreen.js`

### 6. initializeSession

Core authentication setup:

```typescript
const initializeSession = async () => {
  // Load stored credentials
  const credentials = await SecureStore.getItemAsync('credentials');

  if (credentials) {
    // Validate token
    const isValid = await validateToken(credentials.accessToken);

    if (isValid) {
      // Set session
      sessionVar({ ...credentials, isAuthenticated: true });

      // Fetch user profile
      await fetchUserProfile();
    } else {
      // Refresh token
      const newCredentials = await refreshToken(credentials.refreshToken);
      sessionVar({ ...newCredentials, isAuthenticated: true });
    }
  } else {
    // No credentials - user needs to log in
    sessionVar({ isAuthenticated: false });
  }
};
```

Location: `src/hooks/auth/utils/initializeSession.js`

## Auth0 Integration

### Configuration

Auth0 is configured per brand in `brands/[brand]/config.json`:

```json
{
  "auth0": {
    "domain": "auth.theorchard.com",
    "clientId": "your-client-id",
    "audience": "your-audience"
  }
}
```

### Login Flow

```typescript
import Auth0 from 'react-native-auth0';

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

const login = async () => {
  try {
    const credentials = await auth0.webAuth.authorize({
      scope: 'openid profile email offline_access',
      audience: config.auth0.audience,
    });

    // Store credentials securely
    await SecureStore.setItemAsync('credentials', JSON.stringify(credentials));

    // Update session
    sessionVar({ ...credentials, isAuthenticated: true });

    // Navigate to main app
    navigation.navigate('Main');
  } catch (error) {
    console.error('Login error:', error);
  }
};
```

### Logout Flow

```typescript
const logout = async () => {
  // Clear credentials
  await SecureStore.deleteItemAsync('credentials');

  // Clear session
  sessionVar({ isAuthenticated: false });

  // Clear Apollo cache
  await apolloClient.clearStore();

  // Navigate to login
  navigation.navigate('Login');
};
```

### Token Refresh

```typescript
const refreshToken = async (refreshToken: string) => {
  try {
    const credentials = await auth0.auth.refreshToken({
      refreshToken,
    });

    // Update stored credentials
    await SecureStore.setItemAsync('credentials', JSON.stringify(credentials));

    return credentials;
  } catch (error) {
    console.error('Token refresh error:', error);
    throw error;
  }
};
```

## Session Management

### Reactive Variables

Session state is stored in Apollo reactive variables:

```typescript
import { makeVar } from '@apollo/client';

export const sessionVar = makeVar({
  isAuthenticated: false,
  accessToken: null,
  refreshToken: null,
  expiresAt: null,
  user: null,
});

// Read session
const session = sessionVar();

// Update session
sessionVar({ ...session, user: newUserData });

// Subscribe to changes
useReactiveVar(sessionVar);
```

### Secure Storage

Sensitive data is stored using `expo-secure-store`:

```typescript
import * as SecureStore from 'expo-secure-store';

// Store
await SecureStore.setItemAsync('credentials', JSON.stringify(credentials));

// Retrieve
const credentials = await SecureStore.getItemAsync('credentials');

// Delete
await SecureStore.deleteItemAsync('credentials');
```

## Protected Routes

Routes are protected using navigation guards:

```typescript
const ProtectedRoute = ({ children }) => {
  const session = useReactiveVar(sessionVar);

  if (!session.isAuthenticated) {
    return <LoginScreen />;
  }

  return children;
};
```

## API Authentication

### GraphQL Requests

Access token is automatically included in GraphQL requests:

```typescript
const authLink = setContext((_, { headers }) => {
  const session = sessionVar();

  return {
    headers: {
      ...headers,
      authorization: session.accessToken
        ? `Bearer ${session.accessToken}`
        : '',
    },
  };
});
```

### REST Requests

For REST API calls:

```typescript
const fetchData = async () => {
  const session = sessionVar();

  const response = await fetch(API_URL, {
    headers: {
      Authorization: `Bearer ${session.accessToken}`,
      'Content-Type': 'application/json',
    },
  });

  return response.json();
};
```

## Error Handling

### Token Expiration

Handle 401 errors by refreshing token:

```typescript
const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
  if (graphQLErrors) {
    for (const error of graphQLErrors) {
      if (error.extensions?.code === 'UNAUTHENTICATED') {
        // Refresh token
        return fromPromise(
          refreshToken(sessionVar().refreshToken)
            .then((credentials) => {
              sessionVar({ ...sessionVar(), ...credentials });

              // Retry request with new token
              const oldHeaders = operation.getContext().headers;
              operation.setContext({
                headers: {
                  ...oldHeaders,
                  authorization: `Bearer ${credentials.accessToken}`,
                },
              });

              return forward(operation);
            })
            .catch(() => {
              // Refresh failed - logout
              logout();
              return;
            })
        );
      }
    }
  }
});
```

### Network Errors

Handle network errors gracefully:

```typescript
if (networkError) {
  console.error('Network error:', networkError);
  // Show error message to user
  showNetworkErrorMessage();
}
```

## Testing Authentication

### Mock Auth State

```typescript
import { sessionVar } from '@/state/session';

// Mock authenticated state
sessionVar({
  isAuthenticated: true,
  accessToken: 'mock-token',
  user: { id: '123', name: 'Test User' },
});

// Test component
render(<ProtectedComponent />);
```

### Mock Auth0

```typescript
jest.mock('react-native-auth0', () => ({
  __esModule: true,
  default: jest.fn().mockImplementation(() => ({
    webAuth: {
      authorize: jest.fn().mockResolvedValue({
        accessToken: 'mock-token',
        refreshToken: 'mock-refresh-token',
      }),
    },
  })),
}));
```

## Debugging Authentication

### View Session State

```typescript
import { sessionVar } from '@/state/session';

// In dev tools or console
console.log('Session:', sessionVar());
```

### View Stored Credentials

```typescript
import * as SecureStore from 'expo-secure-store';

// In development only!
const credentials = await SecureStore.getItemAsync('credentials');
console.log('Stored credentials:', JSON.parse(credentials));
```

## Security Best Practices

1. **Never log tokens in production**
2. **Use HTTPS for all API calls**
3. **Store tokens in secure storage only**
4. **Implement token refresh before expiration**
5. **Clear session on logout**
6. **Validate tokens on app resume**
7. **Use short-lived access tokens**
8. **Implement proper error handling**

## Next Steps

- [Apollo Client](./apollo-client.md)
- [API Integration](../architecture/api-integration.md)
- [Testing Strategies](../development/testing.md)
