Hermes-agent
This commit is contained in:
@@ -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 `<your tenant>`**. 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 "<MSGRAPH_CLIENT_ID>" `
|
||||
-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 "<MSGRAPH_CLIENT_ID>"
|
||||
```
|
||||
|
||||
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=<directory-tenant-id>
|
||||
MSGRAPH_CLIENT_ID=<application-client-id>
|
||||
MSGRAPH_CLIENT_SECRET=<client-secret-value>
|
||||
```
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user