a # Outlook / Microsoft 365 Connector — Setup Guide

## Prerequisites

- Python 3.13+
- Application Interfaces repo cloned
- Outlook extra installed: `uv pip install -e ".[outlook]"`

## 1. Register an Azure AD Application

1. Go to [Azure Portal → App registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade).
2. Click **New registration**.
3. Configure:
   - **Name**: `appif-outlook` (or any descriptive name)
   - **Supported account types**: Choose based on your needs:
     - *Personal Microsoft accounts only* — for personal Outlook.com
     - *Accounts in any organizational directory and personal* — for both org and personal
   - **Redirect URI**: Select **Mobile and desktop applications** → `http://localhost`
4. Click **Register** and note the **Application (client) ID**.

## 2. Configure API Permissions

1. In your app registration, go to **API permissions**.
2. Click **Add a permission** → **Microsoft Graph** → **Delegated permissions**.
3. Add these permissions:
   - `Mail.ReadWrite` — Read and write mail
   - `Mail.Send` — Send mail
   - `User.Read` — Read user profile (for email address)
4. Click **Grant admin consent** if you have admin access (optional for personal accounts).

## 3. (Optional) Create a Client Secret

Only needed for confidential-client flow (server-side applications):

1. Go to **Certificates & secrets** → **New client secret**.
2. Set a description and expiry.
3. Copy the secret **Value** (not the ID).

For desktop/CLI usage, the public-client flow (no secret) is recommended.

## 4. Set Environment Variables

Copy the template and fill in your values:

```bash
cp .env.example ~/.env
```

Required:
```
APPIF_OUTLOOK_CLIENT_ID=<your-application-client-id>
```

Optional:
```
APPIF_OUTLOOK_CLIENT_SECRET=<secret>          # Only for confidential-client
APPIF_OUTLOOK_TENANT_ID=common                # Or your org tenant ID
APPIF_OUTLOOK_ACCOUNT=default                 # Logical account label
APPIF_OUTLOOK_CREDENTIALS_DIR=~/.config/appif/outlook
APPIF_OUTLOOK_POLL_INTERVAL_SECONDS=30
APPIF_OUTLOOK_FOLDER_FILTER=Inbox
```

## 5. Run the Consent Flow

```bash
python scripts/outlook_consent.py
```

This opens a browser for Microsoft login and consent. After authorization,
the MSAL token cache is saved to `~/.config/appif/outlook/default.json`.

### Named accounts

```bash
python scripts/outlook_consent.py --account work
python scripts/outlook_consent.py --account personal
```

Each account gets its own credential file.

### Custom tenant

```bash
python scripts/outlook_consent.py --tenant <your-tenant-id>
```

## 6. Verify

After consent, the connector can be used:

```python
from appif.adapters.outlook import OutlookConnector

connector = OutlookConnector(client_id="<your-client-id>")
connector.connect()
print(connector.get_status())       # ConnectorStatus.CONNECTED
print(connector.list_accounts())    # [Account(...)]
connector.disconnect()
```

## Credential Storage

Credentials are stored as MSAL serialized token caches:

```
~/.config/appif/outlook/
├── default.json     # Default account
├── work.json        # Named account "work"
└── personal.json    # Named account "personal"
```

These files contain OAuth refresh tokens. The connector automatically
refreshes access tokens using `acquire_token_silent`. If the refresh
token expires, re-run the consent script.

## Troubleshooting

| Symptom | Cause | Fix |
|---------|-------|-----|
| `NotAuthorized: No cached credentials` | Consent not run | Run `python scripts/outlook_consent.py` |
| `NotAuthorized: Token refresh failed` | Refresh token expired | Re-run consent script |
| `AADSTS65001: consent required` | Missing API permissions | Add permissions in Azure Portal |
| `AADSTS7000218: request body must contain client_assertion` | App requires secret | Pass `--client-secret` or set env var |
| `AADSTS50011: reply URL mismatch` | Wrong redirect URI | Add `http://localhost` as Mobile & Desktop redirect |