# TOTP Authentication

Time-based One-Time Password (TOTP) authentication using dynamic codes compatible with authenticator apps.

## Configuration

### Basic Configuration

```bash
--plugin core/auth/totp \
--auth-totp "JBSWY3DPEHPK3PXP:admin:6" \
--auth-totp "HXDMVJECJJWSRB3H:service:8"
```

**Environment Variable:**

```bash
DM_AUTH_TOTP="JBSWY3DPEHPK3PXP:admin:6,HXDMVJECJJWSRB3H:service:8"
```

**Syntax:** `secret:name[:digits]`

- **secret**: Base32-encoded secret (e.g., generated by authenticator apps)
- **name**: Descriptive user/service name
- **digits**: Number of digits in TOTP code (optional, 6-10, default: 6)

### Advanced Configuration

```bash
--auth-totp-window 1    # Time window tolerance (±30s with step=30)
--auth-totp-step 30     # Time step in seconds (default: 30)
```

**Environment Variables:**

```bash
DM_AUTH_TOTP_WINDOW=1   # Default: 1
DM_AUTH_TOTP_STEP=30    # Default: 30
```

- **window**: Number of time steps to check before/after current time (compensates for clock drift)
- **step**: Time interval in seconds for code generation (typically 30)

## Generating Secrets

Use an authenticator app or generate Base32 secrets:

```bash
# Using Node.js crypto (example)
node -e "console.log(require('crypto').randomBytes(20).toString('base32'))"
```

## Usage

### HTTP Requests

Include the current TOTP code in the `Authorization` header:

```bash
# Get current TOTP code from your authenticator app
curl -H "Authorization: Bearer 123456" \
  http://localhost:8081/api/v1/ldap/users
```

### Browser Client

The TOTP client library is available as an npm module export:

```bash
npm install ldap-rest
```

```typescript
import {
  TotpAuthClient,
  generateTotp,
  getRemainingSeconds,
  isValidBase32,
} from 'ldap-rest/browser-shared-utils-totp';

const client = new TotpAuthClient({
  secret: 'JBSWY3DPEHPK3PXP',
  digits: 6,
  step: 30,
});

// Automatic authentication with current TOTP code
const response = await client.get('/api/v1/ldap/users');
const users = await response.json();

// Or get current code manually
const code = await client.getCode();
console.log(`Current TOTP code: ${code}`);
```

**Live Demo:** See [examples/web/totp-client.html](../../../../examples/web/totp-client.html) for an interactive demonstration.

## How It Works

1. Extracts TOTP code from `Authorization: Bearer <code>` header
2. Generates expected TOTP codes for current time window
3. Validates code against all configured users' secrets
4. Supports multiple time windows (±window × step seconds) for clock drift tolerance
5. Sets `req.user` to the user's name on successful validation
6. Returns 401 Unauthorized if code is missing, invalid, or expired

## Multi-User Example

```bash
# Configure multiple users with different code lengths
--plugin core/auth/totp \
--auth-totp "JBSWY3DPEHPK3PXP:admin:6" \
--auth-totp "HXDMVJECJJWSRB3H:api-service:8" \
--auth-totp "IXDMVJECJJWSRB2A:monitoring:10"
```

**In logs:**

```
INFO: TOTP authentication successful for user: admin
INFO: TOTP authentication successful for user: api-service
```

## Use Cases

- **Enhanced Security**: Dynamic codes that expire every 30 seconds
- **No Shared Secrets**: Each user/service has unique TOTP secret
- **Compatible**: Works with Google Authenticator, Authy, 1Password, etc.
- **API Access**: Suitable for automated scripts with TOTP generation
- **Multi-Factor**: Can be combined with other authentication methods

## Security Considerations

- **Secret Storage**: Store Base32 secrets securely (encrypted configuration, environment variables)
- **HTTPS Required**: Always use HTTPS to protect TOTP codes in transit
- **Clock Synchronization**: Ensure server and client clocks are synchronized (NTP recommended)
- **Window Size**: Larger windows are more tolerant but slightly less secure
- **Secret Rotation**: Rotate TOTP secrets periodically for better security
- **Rate Limiting**: Consider adding rate limiting to prevent brute force attacks

## Browser Client Features

The TOTP browser library provides these functions and classes:

```typescript
import {
  generateTotp,
  getRemainingSeconds,
  isValidBase32,
  TotpAuthClient
} from 'ldap-rest/browser-shared-utils-totp';

// Generate TOTP code
const code = await generateTotp({
  secret: 'JBSWY3DPEHPK3PXP',
  digits: 6,
  step: 30
});

// Check validity time
const remaining = getRemainingSeconds(30);
console.log(`Code expires in ${remaining} seconds`);

// Validate secret format
if (isValidBase32('JBSWY3DPEHPK3PXP')) {
  console.log('Valid Base32 secret');
}

// HTTP client with automatic TOTP authentication
const client = new TotpAuthClient({
  secret: 'JBSWY3DPEHPK3PXP',
  digits: 6,
  step: 30
});

// All HTTP methods automatically include TOTP in Authorization header
await client.post('/api/v1/ldap/users', { uid: 'user1', ... });
await client.put('/api/v1/ldap/users/user1', { mail: 'new@example.com' });
await client.delete('/api/v1/ldap/users/user1');
```

**Module Export:** `ldap-rest/browser-shared-utils-totp`

**Live Demo:** Run the server and open [http://localhost:8081/static/examples/web/totp-client.html](http://localhost:8081/static/examples/web/totp-client.html)

## Troubleshooting

**Problem:** 401 Unauthorized despite correct code

**Solutions:**

1. Verify secret is correctly configured (Base32 format)
2. Check system clock synchronization (use NTP)
3. Increase `--auth-totp-window` if experiencing timing issues
4. Ensure code is used immediately (codes expire every 30s)
5. Verify `Authorization: Bearer {code}` format

**Problem:** Codes don't match authenticator app

**Solutions:**

1. Verify secret matches exactly (case-sensitive Base32)
2. Check `--auth-totp-step` matches app configuration (usually 30)
3. Ensure both server and device clocks are synchronized
4. Confirm correct number of digits configured

**Problem:** Invalid Base32 secret warning

**Solutions:**

1. Secret must contain only A-Z and 2-7 characters
2. Use proper Base32 encoding (not Base64)
3. Remove padding `=` characters if present (optional)
