> ## Documentation Index
> Fetch the complete documentation index at: https://docs.doshi.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Considerations

> Essential security guidelines for webview authentication

## Overview

Security is paramount when implementing webview authentication. This guide covers critical security considerations for both postMessage and query parameter methods.

## IP Whitelisting

<Warning>
  The Doshi API requires IP whitelisting. Contact [hello@doshi.app](mailto:hello@doshi.app) to whitelist your server IP addresses.
</Warning>

To get your server's IP address:

```bash theme={null}
# Run this command on your server
curl ifconfig.me
```

Send this IP address to [hello@doshi.app](mailto:hello@doshi.app) along with your organization name.

## API Key Security

<Warning>
  **Never expose your API key in client-side code**. Always call the Doshi API from your backend server.
</Warning>

### Secure API Key Usage

```typescript theme={null}
// ❌ NEVER do this - API key exposed in frontend
const response = await fetch('https://api.doshi.app/client/auth/token', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',  // Exposed to users!
    'Access-type': 'client'
  }
});

// ✅ Correct - Call your backend which securely stores the API key
const response = await fetch('https://your-backend.com/api/generate-doshi-token', {
  method: 'POST',
  body: JSON.stringify({ userId: 'user_123' })
});
```

### Backend Implementation Example

```typescript theme={null}
// Your backend server
app.post('/api/generate-doshi-token', async (req, res) => {
  // Verify the user is authenticated in your system
  if (!req.session.userId) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  // Call Doshi API with your API key (stored securely in environment variables)
  const response = await fetch('https://api.doshi.app/client/auth/token', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.DOSHI_API_KEY}`,
      'Acess-Type': 'client',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      email: req.user.email,
      partnerUserId: req.user.id
    })
  });

  const data = await response.json();
  res.json({ token: data.token });
});
```

## Origin Verification

<Warning>
  Always verify the origin of messages in production environments
</Warning>

<Note>
  If you load the iframe from sandbox, the origin you verify here — and the
  `targetOrigin` you pass to `postMessage` — must be
  `https://sandbox.embed.doshi.app`. A check hardcoded to the production host
  will silently reject sandbox messages. Allow both hosts to support either
  environment. See [Environments](/webview/environments).
</Note>

### For postMessage

```javascript theme={null}
window.addEventListener("message", (event) => {
  // ✅ Verify origin in production
  if (event.origin !== 'https://embed.v2.doshi.app') {
    console.warn('Rejected message from unauthorized origin:', event.origin);
    return;
  }
  
  // Process message
});
```

### Specify Target Origins

```javascript theme={null}
// ❌ Never use in production
webview.contentWindow.postMessage(data, "*");

// ✅ Always specify exact origin in production
webview.contentWindow.postMessage(data, "https://embed.v2.doshi.app");
```

## Token Security

### Token Lifecycle

The custom token received from the API:

* Is single-use for initial authentication
* Has a short expiration time
* Cannot be reused after the user session is established

```typescript theme={null}
// After getting the token, use it immediately
const { token } = await getTokenFromBackend();

// Pass to iframe right away
passTokenToIframe(token);

// Don't store the token for long periods
// Don't reuse the token for multiple sessions
```

### Session Tokens

Once authenticated:

* **ID Token TTL**: 1 hour
* **Refresh Token TTL**: 12 hours
* The iframe manages token refresh automatically
* Sessions are isolated per iframe instance

## Cross-Origin Communication

### postMessage API Security

<AccordionGroup>
  <Accordion title="Message Validation">
    Always validate the structure and content of messages:

    ```javascript theme={null}
        try {
          const data = typeof event.data === "string" 
            ? JSON.parse(event.data) 
            : event.data;
          
          // Validate message structure
          if (!data.type || typeof data.type !== 'string') {
            throw new Error('Invalid message structure');
          }
          
          // Validate expected message types
          const validTypes = ['PING', 'AUTH', 'AUTH_SUCCESS', 'AUTH_ERROR'];
          if (!validTypes.includes(data.type)) {
            throw new Error(`Unexpected message type: ${data.type}`);
          }
          
          // Process valid message
        } catch (error) {
          console.error("Error processing message:", error);
          return;
        }
    ```
  </Accordion>

  <Accordion title="Error Handling">
    Implement comprehensive error handling:

    ```javascript theme={null}
        const handleMessage = (event) => {
          try {
            // Verify origin
            if (event.origin !== 'https://embed.v2.doshi.app') return;
            
            // Parse and validate
            const data = JSON.parse(event.data);
            
            // Process message
          } catch (error) {
            console.error('Message handling failed:', error);
            // Log to monitoring service
            logError(error);
          }
        };
    ```
  </Accordion>

  <Accordion title="HTTPS Enforcement">
    Always use HTTPS for secure communication:

    ```javascript theme={null}
        // ✅ Secure HTTPS URL
        const webviewUrl = "https://embed.v2.doshi.app";

        // ❌ Never use HTTP in production
        const insecureUrl = "http://embed.v2.doshi.app";
    ```
  </Accordion>
</AccordionGroup>

## Query Parameter Security

<Warning>
  Query parameters are visible in URLs and browser history. The custom token is short-lived but still sensitive.
</Warning>

### Best Practices

1. **Clear Parameters After Use**

```javascript theme={null}
// After iframe loads, clear sensitive params
window.addEventListener('load', () => {
  if (window.history.replaceState) {
    const url = new URL(window.location.href);
    url.search = ''; // Clear all query params
    window.history.replaceState({}, document.title, url.toString());
  }
});
```

2. **Use Short-Lived Tokens**

The custom token from the API is already short-lived, but you can add additional validation:

```typescript theme={null}
// Backend - add timestamp to token generation
const tokenData = {
  email: user.email,
  timestamp: Date.now()
};

// Frontend - check token age before using
const isTokenFresh = (timestamp: number) => {
  const maxAge = 5 * 60 * 1000; // 5 minutes
  return Date.now() - timestamp < maxAge;
};
```

3. **Monitor Token Usage**

```typescript theme={null}
// Log token usage for security monitoring
app.post('/api/generate-doshi-token', async (req, res) => {
  const token = await generateDoshiToken(req.user);
  
  // Log token generation
  await logSecurityEvent({
    event: 'token_generated',
    userId: req.user.id,
    timestamp: new Date(),
    ip: req.ip
  });
  
  res.json({ token });
});
```

4. **Respect URL Length Limits**

<Note>
  URLs should generally stay under 2000 characters for maximum browser compatibility
</Note>

```javascript theme={null}
function buildWebviewUrl(baseUrl, params) {
  const urlParams = new URLSearchParams(params);
  const fullUrl = `${baseUrl}?${urlParams.toString()}`;
  
  // Check URL length
  if (fullUrl.length > 2000) {
    console.warn('URL exceeds recommended length. Consider using postMessage.');
  }
  
  return fullUrl;
}
```

## 2FA Security

When implementing 2FA:

```typescript theme={null}
// Only pass 2FA parameters when explicitly enabled
const authData = {
  token: customToken,
  email: user.email,
  // Only include these if 2FA is actually enabled
  ...(is2FaEnabled && {
    is2FaEnabled: true,
    dob: user.dob,
    organizationId: org.id,
    partnerUserId: user.id,
    firstName: user.firstName,
    lastName: user.lastName
  })
};
```

### Don't Send Unnecessary Data

```typescript theme={null}
// ❌ Don't send sensitive data if not needed
const authData = {
  token: customToken,
  ssn: user.ssn,  // Never send unless absolutely required
  password: user.password  // Never send passwords
};

// ✅ Only send required fields
const authData = {
  token: customToken,
  email: user.email,
  segment: 'premium'
};
```

## Security Checklist

<Check>
  Store API key securely in backend environment variables
</Check>

<Check>
  Never expose API key in client-side code
</Check>

<Check>
  Call Doshi API only from your backend server
</Check>

<Check>
  Use HTTPS for all communication
</Check>

<Check>
  Verify message origins in production
</Check>

<Check>
  Never use "\*" wildcard for target origins in production
</Check>

<Check>
  Validate all incoming messages and parameters
</Check>

<Check>
  Use short-lived tokens
</Check>

<Check>
  Clear sensitive data from URLs after reading
</Check>

<Check>
  Implement comprehensive error handling
</Check>

<Check>
  Log security events for monitoring
</Check>

<Check>
  Test across different browsers and environments
</Check>

## Common Security Pitfalls

| Pitfall                            | Risk                              | Solution                        |
| ---------------------------------- | --------------------------------- | ------------------------------- |
| API key in frontend                | Anyone can steal your key         | Call API from backend only      |
| Using `"*"` for postMessage target | Any site can receive messages     | Specify exact origin            |
| Sensitive tokens in URLs           | Exposed in logs/history           | Use postMessage or clear params |
| No origin verification             | Malicious sites can send messages | Always check `event.origin`     |
| Missing input validation           | XSS vulnerabilities               | Validate all inputs             |
| Long-lived tokens                  | Increased attack window           | Use short expiration times      |
| Storing tokens in localStorage     | XSS attacks can steal tokens      | Let iframe manage sessions      |

## Environment-Specific Configuration

```typescript theme={null}
// config.ts
const config = {
  development: {
    doshiApiUrl: 'https://sandbox.api.doshi.app',
    doshiEmbedUrl: 'https://staging-embed.doshi.app',
    strictOriginCheck: false  // For local testing
  },
  production: {
    doshiApiUrl: 'https://api.doshi.app',
    doshiEmbedUrl: 'https://embed.v2.doshi.app',
    strictOriginCheck: true
  }
};

// Use environment-specific config
const env = process.env.NODE_ENV || 'development';
export default config[env];
```

## Monitoring and Logging

```typescript theme={null}
// Log security-relevant events
const logSecurityEvent = async (event: SecurityEvent) => {
  await logger.log({
    level: 'security',
    event: event.type,
    userId: event.userId,
    ip: event.ip,
    timestamp: new Date(),
    metadata: event.metadata
  });
};

// Monitor for suspicious activity
app.post('/api/generate-doshi-token', async (req, res) => {
  // Rate limiting
  const recentTokens = await getRecentTokensForUser(req.user.id);
  if (recentTokens.length > 10) {
    await logSecurityEvent({
      type: 'excessive_token_generation',
      userId: req.user.id,
      ip: req.ip
    });
    return res.status(429).json({ error: 'Too many requests' });
  }
  
  // Generate token...
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="postMessage Implementation" icon="message" href="/webview/postmessage">
    Implement secure real-time authentication
  </Card>

  <Card title="Query Parameters" icon="link" href="/webview/query-parameters">
    Use URL-based authentication safely
  </Card>
</CardGroup>
