# expo-ii-integration

This library enables Expo applications (both web and native) to authenticate with Internet Identity through a web application bridge. It provides a seamless integration between Expo applications and Internet Identity authentication, with different authentication flows for web and native platforms.

## Security Implementation Details

This library implements the secure integration pattern described in the [Internet Computer documentation](https://internetcomputer.org/docs/building-apps/security/iam#integrating-internet-identity-on-mobile-devices) to address several critical security concerns when integrating Internet Identity with mobile applications.

### Security Overview

When integrating Internet Identity with mobile applications, there are several critical security concerns that must be addressed:

1. **Public Key Verification**: The most critical security measure is ensuring that the delegation chain's public key matches your app's public key. This prevents delegation theft and ensures that only your legitimate app can use the delegation chain.

2. **Delegation Theft**: Malicious applications could intercept the delegation chain issued by Internet Identity, potentially allowing an attacker to impersonate the user.

3. **Cross-App Delegation Misuse**: A delegation issued for one application could potentially be misused by another application.

4. **Phishing Attacks and Deep Link Security**: Users might be tricked into authenticating through a malicious application, leading to delegation theft.

5. **Insecure Communication Channels**: Delegation chains could leak through insecure communication channels.

6. **Platform-Specific Security**: Different platforms require different security approaches.

expo-ii-integration addresses these concerns through a comprehensive security model that includes:

- **Public Key Verification**: Critical security check ensuring the delegation chain's public key matches your app's public key
- **Secure Delegation Chain**: Using an intermediate session key that attackers cannot control
- **App Links/Universal Links**: Binding domain names to specific mobile apps
- **Secure Storage**: Using platform-specific secure storage for keys and delegation chains
- **Origin Validation**: Implementing proper origin validation for web messaging

### Security Concerns Addressed

#### 1. Public Key Verification (Primary Security Measure)

**Problem:** Without proper public key verification, a malicious application could trick your app into using a delegation chain with a mismatching public key. While the IC would reject such a message, the delegation chain would already have leaked to boundary and potentially replica nodes where an attacker could steal it.

**Solution:** expo-ii-integration implements strict public key verification:

```typescript
// Critical security check: Verify that the delegation chain's public key
// matches your app's public key
if (
  !arrayBufferEquals(delegationChain.publicKey, appKey.getPublicKey().toDer())
) {
  throw new Error('Delegation public key does not match app key');
}
```

This verification is crucial because:

1. **Preventing Delegation Leakage**: Without this verification, a malicious application could trick your app into using a delegation chain with a mismatching public key.

2. **Ensuring Key Consistency**: The verification ensures that the public key in the delegation chain matches your app's public key, maintaining the integrity of the authentication process.

3. **Protecting Against Man-in-the-Middle Attacks**: By verifying the delegation chain before using it, the app can detect if the delegation has been tampered with during transmission.

#### 2. Protection Against Delegation Theft

**Problem:** Without proper safeguards, a malicious application could intercept the delegation chain issued by Internet Identity, potentially allowing an attacker to impersonate the user.

**Solution:** expo-ii-integration implements a secure delegation chain approach:

- Uses an intermediate session key generated and stored by the web app proxy
- Creates a delegation chain with two delegations:
  1. From the II canister key to the intermediate key (generated by II)
  2. From the intermediate key to the mobile app public key (signed by the proxy)
- This approach ensures that the delegation chain is properly secured. By using an intermediate session key that the attacker cannot control, even if the delegation issued by II is intercepted, the attacker cannot use it because they don't have access to the intermediate session private key. This prevents the delegation from being misused even if it falls into the wrong hands.

#### 3. Cross-App Delegation Misuse Prevention

**Problem:** A malicious application could intercept the postMessage communication between your app and Internet Identity, potentially stealing the delegation chain.

**Solution:** expo-ii-integration implements strict origin validation for postMessage communication:

```typescript
// In the web app proxy
window.addEventListener('message', (event) => {
  // Critical security check: Verify that the message origin is from your legitimate app
  if (event.origin !== expectedOrigin) {
    console.error('Rejected message from unexpected origin:', event.origin);
    return;
  }

  // Process the message only if it's from a trusted origin
  // ...
});
```

This origin validation is crucial because:

1. **Preventing Message Interception**: Without this check, any website could send messages to your proxy and potentially intercept the delegation chain.

#### 4. Protection Against Phishing Attacks and Deep Link Security

**Problem:** Users might be tricked into authenticating through a malicious application, leading to delegation theft.

**Solution:** expo-ii-integration implements platform-specific security measures:

1. **Platform-Specific Deep Link Security**:

   - **Android**: Uses App Links to bind your domain to your app, ensuring only your legitimate app can handle the authentication callback
   - **iOS**: Uses Universal Links to bind your domain to your app, ensuring only your legitimate app can handle the authentication callback

   When using `easDeepLinkType: "modern"`, the library ensures that the authentication callback can only return to your legitimate app:

   ```typescript
   // In [buildDeepLink.ts](src/utils/buildDeepLink.ts)
   switch (deepLinkType) {
     case 'modern':
       if (frontendCanisterURL.startsWith('https://')) {
         return frontendCanisterURL;
       }
       throw new Error(`Frontend URL is not HTTPS: ${frontendCanisterURL}`);
     // ...
   }
   ```

   This ensures that:

   - The deep link is bound to your specific frontend canister URL
   - Only your legitimate app can receive the authentication callback
   - The URL must be HTTPS for additional security

2. **Secure Delegation Chain**:

   - Implements a secure delegation chain with an intermediate session key that the attacker cannot access
   - Returns delegation chains using URI fragments that are not included in server requests
   - Prevents leakage through URL parameters or paths

3. **Secure Storage**:
   - Uses secure storage mechanisms to protect the intermediate session key
   - Ensures the critical security element is properly protected

#### 5. Secure Communication Channels

**Problem:** Delegation chains could leak through insecure communication channels.

**Solution:** expo-ii-integration:

- Uses secure storage mechanisms for both keys and delegation chains
- Implements proper validation before using delegation chains in IC messages
- Ensures that delegation chains are not exposed in URL parameters or paths

#### 6. Platform-Specific Security Considerations

**Problem:** Different platforms require different security approaches.

**Solution:** expo-ii-integration:

- Provides platform-specific implementations for web and native environments
- Uses appropriate secure storage mechanisms for each platform
- Implements proper origin validation for web messaging
- Handles authentication flows differently based on the platform

### Implementation Details

The library implements these security measures through:

1. **Secure Key Management:**

   - `AppKeyStorage` for securely storing the application's Ed25519KeyIdentity
   - Platform-specific secure storage implementations

2. **Delegation Chain Handling:**

   - `DelegationStorage` for securely storing and validating delegation chains
   - Automatic validation of delegation chains before use
   - Proper cleanup of expired delegations

3. **Authentication Flow Security:**

   - Platform-specific authentication flows that maintain security
   - Proper origin validation for web messaging
   - Secure communication between the proxy and mobile app

4. **Type Safety and Validation:**
   - Type-safe interfaces to prevent misuse
   - Runtime validations to ensure security requirements are met
   - Proper error handling for security-related issues

## EAS Deep Link Type and App Links/Universal Links

A critical security feature of expo-ii-integration is its support for App Links (Android) and Universal Links (iOS) through the `easDeepLinkType` parameter.

### Configuration

When building your Expo app with EAS Build, you should set the `EXPO_PUBLIC_EAS_DEEP_LINK_TYPE` environment variable to `"modern"`:

```json
{
  "preview": {
    "distribution": "internal",
    "env": {
      "EXPO_PUBLIC_EAS_BUILD_PROFILE": "preview",
      "EXPO_PUBLIC_EAS_DEEP_LINK_TYPE": "modern"
    }
  },
  "production": {
    "autoIncrement": true,
    "env": {
      "EXPO_PUBLIC_EAS_BUILD_PROFILE": "production",
      "EXPO_PUBLIC_EAS_DEEP_LINK_TYPE": "modern"
    }
  }
}
```

### Security Benefits

Setting `easDeepLinkType` to `"modern"` is crucial for security because:

1. **Domain Binding**: It forces the authentication flow to use App Links (Android) or Universal Links (iOS), which bind your domain to your specific mobile app.

2. **Preventing Delegation Theft**: This binding prevents malicious applications from intercepting the delegation chain, as only your legitimate app can receive the authentication callback.

3. **Phishing Protection**: Even if a user is tricked into authenticating through a malicious app, the delegation cannot be stolen because the authentication callback will only be handled by your legitimate app.

### Implementation Details

The library uses this setting in several ways:

1. In [`useIIIntegration.ts`](src/hooks/useIIIntegration.ts), the `easDeepLinkType` parameter is passed to determine the authentication flow.

2. In [`getDeepLinkType.ts`](src/utils/getDeepLinkType.ts), this parameter is used to set the deep link type to `"modern"`.

3. When `"modern"` is set, the proxy web app uses App Links/Universal Links to ensure the authentication callback returns to your legitimate app.

4. This is implemented in the [`buildDeepLink.ts`](src/utils/buildDeepLink.ts) utility, which generates the appropriate deep link based on the type.

This approach is a key security measure that prevents delegation theft and ensures that even if the delegation chain is intercepted, it cannot be used by malicious applications.

### Delegation Chain Verification

A critical security measure implemented in expo-ii-integration is the verification of the delegation chain before using it in IC messages. This verification focuses specifically on comparing the public key in the delegation chain with your app's public key:

```typescript
// In [useIIIntegration.ts](src/hooks/useIIIntegration.ts)
const setupIdentityFromDelegation = async (delegation: string) => {
  console.log('Processing delegation');
  const delegationChain = DelegationChain.fromJSON(delegation);
  await delegationStorage.save(delegationChain);
  const appKey = await appKeyStorage.retrieve();

  // Critical security check: Verify that the delegation chain's public key
  // matches your app's public key
  if (
    !arrayBufferEquals(delegationChain.publicKey, appKey.getPublicKey().toDer())
  ) {
    throw new Error('Delegation public key does not match app key');
  }

  const id = DelegationIdentity.fromDelegation(appKey, delegationChain);
  setIdentity(id);
  console.log('identity set from delegation');
};
```

This public key comparison is crucial for security because:

1. **Preventing Delegation Leakage**: Without this verification, a malicious application could trick your app into using a delegation chain with a mismatching public key. While the IC would reject such a message, the delegation chain would already have leaked to boundary and potentially replica nodes where an attacker could steal it.

2. **Ensuring Key Consistency**: The verification ensures that the public key in the delegation chain matches your app's public key, maintaining the integrity of the authentication process.

3. **Protecting Against Man-in-the-Middle Attacks**: By verifying the delegation chain before using it, the app can detect if the delegation has been tampered with during transmission.

This verification is implemented using the `arrayBufferEquals` utility function, which performs a secure comparison of the public keys to ensure they match exactly. This is a critical security check that prevents delegation theft and ensures that only your legitimate app can use the delegation chain.

## Features

- Seamless Internet Identity authentication in Expo apps
- Platform-specific authentication flows:
  - Web (PC/Smartphone): Modal-based authentication using iframe messaging
  - Native (iOS/Android): Browser-based authentication using Expo WebBrowser
- Secure key and delegation chain management
- Platform-agnostic secure storage handling
- Type-safe React hooks and context
- Path tracking utilities for authentication flow:
  - `pathWhenLogin`: Tracks the path when authentication was initiated
  - `clearPathWhenLogin()`: Utility to clear the saved path
- Error handling and state management

## Installation

```bash
npm install expo-ii-integration
```

### Dependencies

This package has the following peer dependencies that you need to install:

```json
{
  "@dfinity/agent": "^0.20.2",
  "@dfinity/identity": "^0.20.2",
  "expo-linking": "~7.0",
  "expo-router": "~4.0",
  "expo-web-browser": "~14.0",
  "react": "~18.3"
}
```

## Usage

### Basic Setup

1. Wrap your app with the `IIIntegrationProvider`:

```tsx
import { IIIntegrationProvider, useIIIntegration } from 'expo-ii-integration';
import { AppKeyStorage } from 'expo-ii-integration/storage';
import { DelegationStorage } from 'expo-ii-integration/storage';

function App() {
  const appKeyStorage = new AppKeyStorage(storage);
  const delegationStorage = new DelegationStorage(storage);

  const iiIntegration = useIIIntegration({
    localIPAddress: '192.168.0.210',
    dfxNetwork: 'local',
    easDeepLinkType: 'legacy',
    deepLink: 'your-deep-link',
    frontendCanisterId: 'your-frontend-canister-id',
    iiIntegrationCanisterId: 'YOUR_II_INTEGRATION_CANISTER_ID',
    platform: 'web',
    appKeyStorage,
    delegationStorage,
  });

  return (
    <IIIntegrationProvider value={iiIntegration}>
      {/* Your app components */}
    </IIIntegrationProvider>
  );
}
```

### Using the Authentication Context

```tsx
import { useIIIntegrationContext } from 'expo-ii-integration';

function AuthButton() {
  const {
    login,
    logout,
    isAuthenticated,
    isReady,
    identity,
    pathWhenLogin,
    clearPathWhenLogin,
    authError,
  } = useIIIntegrationContext();

  if (!isReady) return null;

  return (
    <Button
      onPress={isAuthenticated ? logout : login}
      title={isAuthenticated ? 'Logout' : 'Login with Internet Identity'}
    />
  );
}
```

### Path Restoration Example

```tsx
import React from 'react';
import FontAwesome from '@expo/vector-icons/FontAwesome';
import { Href, Redirect, Tabs, usePathname } from 'expo-router';
import { LogIn } from '@/components/LogIn';
import { LogOut } from '@/components/LogOut';
import { View, ActivityIndicator } from 'react-native';
import { useIIIntegrationContext } from 'expo-ii-integration';

function TabBarIcon(props: {
  name: React.ComponentProps<typeof FontAwesome>['name'];
  color: string;
}) {
  return <FontAwesome size={28} style={{ marginBottom: -3 }} {...props} />;
}

export default function TabLayout() {
  const { identity, pathWhenLogin, clearPathWhenLogin } =
    useIIIntegrationContext();
  const pathname = usePathname();

  if (identity && pathWhenLogin) {
    clearPathWhenLogin();

    if (pathWhenLogin !== pathname) {
      console.log('redirecting to', pathWhenLogin);
      // Show loading indicator while redirecting
      return (
        <View
          style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
        >
          <ActivityIndicator size="large" color="#007AFF" />
          <Redirect href={pathWhenLogin as Href} />
        </View>
      );
    }
  }

  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#007AFF',
        headerRight: () => (identity ? <LogOut /> : <LogIn />),
        headerStyle: {
          height: 110,
        },
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: 'Identity',
          tabBarIcon: ({ color }) => <TabBarIcon name="user" color={color} />,
        }}
      />
      <Tabs.Screen
        name="two"
        options={{
          title: 'Encryption',
          tabBarIcon: ({ color }) => <TabBarIcon name="lock" color={color} />,
        }}
      />
    </Tabs>
  );
}
```

This example shows how to:

- Integrate path restoration with tab navigation
- Show authentication UI components in the header
- Compare the current path with the saved path to avoid unnecessary redirects
- Show a loading indicator during redirection
- Use the `Redirect` component from expo-router for navigation
- Clear the saved path after initiating the redirect

## API Reference

### useIIIntegration Hook

The main hook for Internet Identity integration.

#### Parameters

```typescript
type UseIIAuthParams = {
  localIPAddress: string; // Local IP address for development
  dfxNetwork: string; // dfx network (e.g., 'local', 'ic')
  easDeepLinkType?: string; // EAS deep link type ('legacy' or 'modern')
  deepLink: string; // Deep link to determine the type
  frontendCanisterId: string; // Frontend canister ID
  iiIntegrationCanisterId: string; // II Integration canister ID
  platform: string; // Platform identifier (e.g., 'ios', 'android', 'web')
  appKeyStorage: Ed25519KeyIdentityValueStorageWrapper; // Storage for app's key identity
  delegationStorage: DelegationChainValueStorageWrapper; // Storage for delegation chain
};
```

#### Returns

```typescript
interface IIIntegrationContextType {
  identity: DelegationIdentity | undefined; // Current user identity
  isReady: boolean; // Authentication state is initialized
  isAuthenticated: boolean; // User is authenticated
  login: () => Promise<void>; // Trigger login flow
  logout: () => Promise<void>; // Clear authentication
  pathWhenLogin: string | undefined; // Path when login was initiated
  clearPathWhenLogin: () => void; // Clear the saved path
  authError: unknown | undefined; // Authentication errors
}
```

### Storage Classes

#### Ed25519KeyIdentityValueStorageWrapper

A storage wrapper for Ed25519KeyIdentity that handles serialization and deserialization.

```typescript
class Ed25519KeyIdentityValueStorageWrapper
  implements StorageWrapper<Ed25519KeyIdentity>
{
  constructor(storage: Storage, key: string);

  find(): Promise<Ed25519KeyIdentity | undefined>;
  retrieve(): Promise<Ed25519KeyIdentity>;
  save(value: Ed25519KeyIdentity): Promise<void>;
  remove(): Promise<void>;
}
```

#### DelegationChainValueStorageWrapper

A storage wrapper for DelegationChain that includes automatic validation.

```typescript
class DelegationChainValueStorageWrapper
  implements StorageWrapper<DelegationChain>
{
  constructor(storage: Storage, key: string);

  find(): Promise<DelegationChain | undefined>;
  retrieve(): Promise<DelegationChain>;
  save(value: DelegationChain): Promise<void>;
  remove(): Promise<void>;
}
```

#### AppKeyStorage

A specialized wrapper for storing the application's Ed25519KeyIdentity.

```typescript
class AppKeyStorage extends Ed25519KeyIdentityValueStorageWrapper {
  constructor(storage: Storage);
}
```

#### DelegationStorage

A specialized wrapper for storing the application's DelegationChain.

```typescript
class DelegationStorage extends DelegationChainValueStorageWrapper {
  constructor(storage: Storage);
}
```

### API Methods

#### login()

Initiates the authentication flow:

- On web: Opens a modal with Internet Identity using iframe messaging
- On native: Opens the system browser using Expo WebBrowser
- Automatically saves the current path before initiating login
- Handles platform-specific authentication flows

#### logout()

Clears the current authentication state:

- Removes the delegation chain from storage
- Clears the identity state
- Maintains the app key for future authentication

#### clearPathWhenLogin()

Clears the saved path that was stored during login:

- Used after restoring the user to their original location
- Prevents unnecessary redirects
- Helps maintain a smooth navigation flow

## Platform-Specific Behavior

### Web

- Uses modal-based authentication with iframe messaging
- Handles authentication flow within the same window
- Maintains application state during authentication

### Native (iOS/Android)

- Opens authentication in the system browser
- Uses URL scheme for authentication callback
- Provides utilities to restore application state after authentication

## Security Features

- Secure storage of Ed25519 key pairs
- Delegation chain validation and automatic cleanup
- Origin validation for web messaging
- Platform-specific secure storage implementations
- Type-safe interfaces and runtime validations

## Contributing

Contributions are welcome! Please read our contributing guidelines for details.

## License

MIT License
