diff --git a/website/docs/guides/microsoft-graph-app-registration.md b/website/docs/guides/microsoft-graph-app-registration.md new file mode 100644 index 0000000000..70de0498cf --- /dev/null +++ b/website/docs/guides/microsoft-graph-app-registration.md @@ -0,0 +1,180 @@ +--- +title: "Register a Microsoft Graph Application" +description: "Azure portal walkthrough for creating the app registration that powers the Teams meeting pipeline" +--- + +# Register a Microsoft Graph Application + +The Teams meeting pipeline reads meeting transcripts, recordings, and related artifacts from Microsoft Graph using **app-only** (daemon) authentication — no user sign-in, no interactive consent per meeting. That requires an Azure AD application registration with admin-consented application permissions. + +This guide walks through: + +1. Creating the app registration +2. Creating a client secret +3. Granting the Graph API permissions the pipeline needs +4. Admin-consenting those permissions +5. (Optional) Scoping the app to specific users with an Application Access Policy + +You need **tenant admin rights** (or an admin to grant consent on your behalf) to finish this. Bookmark the values you collect — they go into `~/.hermes/.env` at the end. + +## Prerequisites + +- A Microsoft 365 tenant with Teams Premium or Teams licenses that produce meeting transcripts and recordings +- Admin access to the Azure portal at [entra.microsoft.com](https://entra.microsoft.com) +- A publicly reachable HTTPS endpoint for Graph change notifications (set up later, in the webhook listener step) + +## Step 1: Create the App Registration + +1. Sign in to [entra.microsoft.com](https://entra.microsoft.com) as a tenant admin. +2. Navigate to **Identity → Applications → App registrations**. +3. Click **New registration**. +4. Fill in: + - **Name:** `Hermes Teams Meeting Pipeline` (or any name you'll recognize). + - **Supported account types:** *Accounts in this organizational directory only (Single tenant)*. + - **Redirect URI:** leave blank — app-only auth does not need one. +5. Click **Register**. + +You'll land on the app's overview page. Copy two values: + +- **Application (client) ID** → `MSGRAPH_CLIENT_ID` +- **Directory (tenant) ID** → `MSGRAPH_TENANT_ID` + +## Step 2: Create a Client Secret + +1. In the left nav, open **Certificates & secrets**. +2. Click **New client secret**. +3. **Description:** `hermes-graph-secret`. **Expires:** pick a value that matches your rotation policy (6-24 months is typical). +4. Click **Add**. +5. Copy the **Value** column immediately — it's only shown once. That value is `MSGRAPH_CLIENT_SECRET`. + +> The **Secret ID** column is not the secret. You want the **Value** column. + +## Step 3: Grant Graph API Permissions + +The pipeline uses a minimum-viable set of application permissions. Add only what you need; each one widens what the app can read tenant-wide. + +1. In the left nav, open **API permissions**. +2. Click **Add a permission** → **Microsoft Graph** → **Application permissions**. +3. Add the permissions from the table below that match what you want the pipeline to do. +4. After adding, click **Grant admin consent for ``**. The Status column should flip to a green checkmark for every permission. + +### Required for transcript-first summaries + +| Permission | What it lets the app do | +|------------|--------------------------| +| `OnlineMeetings.Read.All` | Read Teams online meeting metadata (subject, participants, join URL). | +| `OnlineMeetingTranscript.Read.All` | Read meeting transcripts generated by Teams. | + +### Required for recording fallback (when a transcript is unavailable) + +| Permission | What it lets the app do | +|------------|--------------------------| +| `OnlineMeetingRecording.Read.All` | Download Teams meeting recordings for offline STT processing. | +| `CallRecords.Read.All` | Resolve meetings from call records when only the join URL is known. | + +### Required for outbound summary delivery (Graph mode only) + +If `platforms.teams.extra.delivery_mode` is `graph`, the pipeline posts summaries into a Teams channel or chat via the Graph API. Skip these if you use `incoming_webhook` delivery mode instead. + +| Permission | What it lets the app do | +|------------|--------------------------| +| `ChannelMessage.Send` | Post messages into Teams channels on behalf of the app. | +| `Chat.ReadWrite.All` | Post messages into 1:1 and group chats (only if you set `chat_id` as the delivery target). | + +### Not recommended + +- `OnlineMeetings.ReadWrite.All` / `Chat.ReadWrite` without `.All` — broader than the pipeline needs. +- Delegated permissions — the pipeline uses app-only (client-credentials) flow; delegated permissions won't work without user sign-in. + +## Step 4: (Recommended) Scope the App with an Application Access Policy + +By default, application permissions like `OnlineMeetings.Read.All` grant the app access to **every** meeting in the tenant. For partner demos and dev tenants that's fine; for production you almost certainly want to restrict which users' meetings the app can read. + +Microsoft provides **Application Access Policies** for Teams exactly for this. The policy is a PowerShell-only surface; there's no portal UI for it. + +From an admin PowerShell with the MicrosoftTeams module installed and connected (`Connect-MicrosoftTeams`): + +```powershell +# Create a policy scoped to the Hermes app +New-CsApplicationAccessPolicy ` + -Identity "Hermes-Meeting-Pipeline-Policy" ` + -AppIds "" ` + -Description "Restrict Hermes meeting pipeline to allow-listed users" + +# Grant the policy to specific users whose meetings the pipeline may read +Grant-CsApplicationAccessPolicy ` + -PolicyName "Hermes-Meeting-Pipeline-Policy" ` + -Identity "alice@example.com" + +Grant-CsApplicationAccessPolicy ` + -PolicyName "Hermes-Meeting-Pipeline-Policy" ` + -Identity "bob@example.com" +``` + +Propagation can take up to 30 minutes after granting. Verify with: + +```powershell +Test-CsApplicationAccessPolicy -Identity "alice@example.com" -AppId "" +``` + +Without the policy, **any** user's meetings are readable — that's what the permission technically grants. Don't skip this step on a production tenant. + +## Step 5: Write the Credentials to Your Env File + +Put the three values you collected into `~/.hermes/.env`: + +```bash +MSGRAPH_TENANT_ID= +MSGRAPH_CLIENT_ID= +MSGRAPH_CLIENT_SECRET= +``` + +Set file permissions so only you can read the secret: + +```bash +chmod 600 ~/.hermes/.env +``` + +## Step 6: Verify the Token Flow + +Hermes ships a Graph auth smoke-test. From your Hermes install: + +```python +python -c " +import asyncio +from tools.microsoft_graph_auth import MicrosoftGraphTokenProvider +provider = MicrosoftGraphTokenProvider.from_env() +token = asyncio.run(provider.get_access_token()) +print('Token acquired, length:', len(token)) +print(provider.inspect_token_health()) +" +``` + +A successful run prints a long token string and a health dict showing `cached: True` and an `expires_in_seconds` value near 3600. Failures produce a `MicrosoftGraphTokenError` with the Azure error code — the most common are: + +| Azure error | Meaning | Fix | +|-------------|---------|-----| +| `AADSTS7000215: Invalid client secret` | Secret value mismatched or expired. | Generate a new secret in step 2; update `.env`. | +| `AADSTS700016: Application not found` | Wrong `MSGRAPH_CLIENT_ID` or wrong tenant. | Double-check the values from step 1 are from the same app. | +| `AADSTS90002: Tenant not found` | Typo in `MSGRAPH_TENANT_ID`. | Copy the Directory (tenant) ID from the app overview again. | +| `insufficient_claims` at call time (not token time) | Token acquires but Graph returns 401/403. | You skipped step 3 admin-consent, or added permissions but haven't re-consented. Revisit API permissions and click **Grant admin consent** again. | + +## Rotating the Client Secret + +Azure client secrets have a hard expiry. Before yours expires: + +1. Create a second client secret in step 2 without deleting the first one. +2. Update `MSGRAPH_CLIENT_SECRET` in `~/.hermes/.env` with the new value. +3. Restart the gateway so the new secret is picked up: `hermes gateway restart`. +4. Verify with the smoke test above. +5. Delete the old secret from the Azure portal. + +## Next Steps + +Once credentials verify cleanly, continue with: + +- **Webhook listener setup** — stand up the `msgraph_webhook` gateway platform that receives Graph change notifications. +- **Pipeline configuration** — configure the Teams meeting pipeline runtime and operator CLI. +- **Outbound delivery** — wire summaries back into a Teams channel or chat. + +Those pages land alongside the PRs that add the corresponding runtime. This credentials setup is a standalone prerequisite and is safe to complete in advance. diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index 61b3aebaaf..078e1ff5b7 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -406,6 +406,18 @@ For cloud sandbox backends, persistence is filesystem-oriented. `TERMINAL_LIFETI | `GATEWAY_ALLOWED_USERS` | Comma-separated user IDs allowed across all platforms | | `GATEWAY_ALLOW_ALL_USERS` | Allow all users without allowlists (`true`/`false`, default: `false`) | +### Microsoft Graph (Teams Meetings) + +App-only credentials for the Microsoft Graph REST client used by the upcoming Teams meeting summary pipeline. See [Register a Microsoft Graph application](/docs/guides/microsoft-graph-app-registration) for the Azure portal walkthrough and the exact API permissions required. + +| Variable | Description | +|----------|-------------| +| `MSGRAPH_TENANT_ID` | Azure AD tenant ID (directory GUID) for the Graph app registration. | +| `MSGRAPH_CLIENT_ID` | Application (client) ID of the Azure app registration. | +| `MSGRAPH_CLIENT_SECRET` | Client secret value for the app registration. Store in `~/.hermes/.env` with `chmod 600`; rotate periodically via the Azure portal. | +| `MSGRAPH_SCOPE` | OAuth2 scope for the client-credentials token request (default: `https://graph.microsoft.com/.default`). | +| `MSGRAPH_AUTHORITY_URL` | Microsoft identity platform authority (default: `https://login.microsoftonline.com`). Override only for national/sovereign clouds (e.g. `https://login.microsoftonline.us` for GCC High). | + ### Advanced Messaging Tuning Advanced per-platform knobs for throttling the outbound message batcher. Most users never need to touch these; defaults are set to respect each platform's rate limits without feeling sluggish. diff --git a/website/sidebars.ts b/website/sidebars.ts index 066a05223d..05dc891821 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -181,6 +181,7 @@ const sidebars: SidebarsConfig = { 'guides/migrate-from-openclaw', 'guides/aws-bedrock', 'guides/azure-foundry', + 'guides/microsoft-graph-app-registration', ], }, {