Compare commits

...

3 Commits

Author SHA1 Message Date
Omar Mihilmy 89f6b3c609 Split Entra app registration into commands/entra-app.md
manifest.md now documents what the params do; entra-app.md documents how to register the app (redirect URIs, per-use-case permissions table, admin consent, token version, backend validation, GCC-High/DoD, troubleshooting). Linked from every manifest.md section that needs your own app: graph_client_id, entra_scope, gateway_auth_source, sovereign clouds.
2026-06-26 17:35:42 -04:00
Omar Mihilmy a29759e365 manifest.md: list both NAA redirect URIs (brk-multihub + https fallback)
The checklist only listed the https://.../msal-redirect.html SPA fallback. Desktop Office and Outlook web use the NAA broker, which requires brk-multihub://pivot.claude.ai registered as SPA — without it sign-in fails with AADSTS50011. Both are needed: brk-multihub for NAA, https for the Excel/Word/PPT Office-Web fallback where the NAA bridge isn't injected. Also pointed the earlier 'Bring your own Entra app' prose at the checklist instead of duplicating (incompletely).
2026-06-26 17:32:58 -04:00
Omar Mihilmy b9ccfc9158 manifest: add gateway_auth_source=entra param + Entra app registration docs (incl. GCC High)
- build-manifest.mjs: recognize gateway_auth_source; require entra_scope (→ graph_client_id, entra_sso=1); warn if paired with gateway_token; drop stale 'graph_client_id is Outlook-only' note
- manifest.md: new 'Use the Entra token as your gateway credential' section with a consolidated Entra app registration checklist (SPA redirect URI, Expose an API, admin consent, v2 tokens, gateway-side validation) and a GCC High/DoD subsection (portal.azure.us, .us issuer/JWKS)
2026-06-26 17:30:35 -04:00
4 changed files with 158 additions and 13 deletions
@@ -1,7 +1,7 @@
{
"name": "claude-for-msft-365-install",
"description": "Provision direct cloud access (Vertex AI, Bedrock, or LLM gateway) for the Claude Office add-in. Generates the customized add-in manifest, walks through Azure admin consent, and writes per-user config via Microsoft Graph extension attributes.",
"version": "0.1.5",
"version": "0.1.6",
"author": {
"name": "Anthropic",
"email": "support@anthropic.com"
@@ -0,0 +1,106 @@
# Register your own Entra app
Several manifest configurations require an Entra (Azure AD) app registration in
**your** tenant rather than Anthropic's default multi-tenant app — because the
token's `aud` must match a resource you control, or because your tenant is in a
sovereign cloud where Anthropic's app doesn't exist. This page is the single
set of registration steps; the per-feature docs link here and tell you which
row of the permissions table applies.
You need this when setting any of:
| Manifest key | Why your own app |
|---|---|
| `graph_client_id` (Outlook) | Graph permissions are consented against your app, not Anthropic's |
| `entra_scope` | Access token must be audienced to *your* API resource |
| `gateway_auth_source=entra` | Gateway validates a token audienced to your API |
| `graph_cloud``global` | Anthropic's app exists only in the commercial cloud |
## Register the app
In [Entra admin center](https://entra.microsoft.com) → *App registrations*
*New registration*. Single-tenant. The simplest topology is one app acting as
both client (the add-in signs in as it) and resource (your backend validates
tokens audienced to it); split into two if your policy requires.
### 1. Redirect URIs
*Authentication**Add a platform***Single-page application** → add
**both**:
| URI | Used by |
|---|---|
| `brk-multihub://pivot.claude.ai` | [NAA broker](https://learn.microsoft.com/office/dev/add-ins/develop/enable-nested-app-authentication-in-your-add-in) — desktop Office and Outlook web. Missing this → `AADSTS50011` at sign-in. |
| `https://pivot.claude.ai/msal-redirect.html` | SPA fallback — Excel/Word/PowerPoint on Office for the web, which don't inject the NAA bridge. |
Both go under the SPA platform (not Web, not Mobile/desktop).
### 2. Permissions / API setup
What you configure here depends on what the token is for:
| Use case | Configure |
|---|---|
| **Outlook (Graph)** | *API permissions**Microsoft Graph* → Delegated → `Mail.ReadWrite`, `Calendars.Read`, `People.Read`, `User.Read`, `offline_access`. |
| **Gateway / bootstrap auth** (`entra_scope`, `gateway_auth_source=entra`) | *Expose an API* → set Application ID URI `api://<app-guid>`*Add a scope* (e.g. `access_as_user`, admin-consent enabled). Then *API permissions**My APIs* → add that scope as a delegated permission (the app to itself, in single-app topology). |
| **Bedrock WIF** (`aws_role_arn`) | No API permissions needed — the ID token alone is the web identity. |
In all cases finish with *API permissions* → **Grant admin consent for
&lt;tenant&gt;**. Without it every user sees a consent prompt (or is blocked, if
user consent is disabled).
### 3. Token version
Only if you set up *Expose an API* above: in the app *Manifest*, set
`"accessTokenAcceptedVersion": 2`. Leave it unset and Entra issues v1.0 access
tokens (`iss` without `/v2.0`, no `preferred_username`), which most JWT
middleware rejects by default.
## What your backend validates
For `entra_scope` / `gateway_auth_source=entra`, the access token the add-in
sends as `Authorization: Bearer` carries:
| Claim | Expected |
|---|---|
| `iss` | `https://login.microsoftonline.com/<tenant-id>/v2.0` |
| `aud` | your Application ID URI (`api://<app-guid>`) |
| `scp` | the scope(s) you exposed, space-separated |
| JWKS | `https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys` |
## GCC High / DoD / 21Vianet
Same steps, different portal and endpoints — apps don't replicate across
Microsoft national clouds.
- **Register at** [`portal.azure.us`](https://portal.azure.us) (US Gov) or
[`portal.azure.cn`](https://portal.azure.cn) (21Vianet), not the commercial
portal.
- **Redirect URIs** are unchanged — `brk-multihub://pivot.claude.ai` and
`https://pivot.claude.ai/msal-redirect.html`. The add-in is served from the
same domain in every cloud.
- **Manifest** — add `graph_cloud=us-gov-high` (or `us-gov-dod`, `china`) per
the [sovereign clouds](manifest.md#sovereign--national-clouds-gcc-high-dod-21vianet)
table.
- **Admin consent URL** uses the sovereign authority:
`https://login.microsoftonline.us/<tenant-id>/adminconsent?client_id=<app-id>`
- **Backend validation** — the token's `iss` and JWKS use the `.us` host:
`https://login.microsoftonline.us/<tenant-id>/v2.0` and
`https://login.microsoftonline.us/<tenant-id>/discovery/v2.0/keys`. A
validator pinned to `.com` rejects every request. The same applies to any AWS
OIDC identity provider in the chain.
## Troubleshooting
**`AADSTS50011` (redirect URI mismatch)** — one of the two URIs above is
missing, or was added under the wrong platform (must be SPA).
**`Tag: 9n156` on Outlook for Mac** — the host's auth broker can't complete.
Almost always a sovereign-cloud account hitting an app registered in commercial
(fix: register in `portal.azure.us` and set `graph_cloud`). If the app is
already in the right cloud, check Conditional Access — the deprecated *Require
approved client app* grant blocks NAA. The Correlation Id in the dialog is
resolvable in your Entra sign-in logs, not Anthropic's.
**`AADSTS65001` (consent required)** — *Grant admin consent* wasn't clicked, or
a permission was added after consent was granted (re-grant).
@@ -48,9 +48,10 @@ the add-in uses Anthropic's multi-tenant app.
The add-in auto-detects the tenant's national cloud at sign-in (from the
authority host Office reports) and resolves the matching Graph + Entra
endpoints, so most sovereign tenants need **no cloud config**. The only
required step is
bringing your own Entra app via `graph_client_id` — Anthropic's multi-tenant
app exists only in the commercial cloud. A GCC-High Outlook manifest needs
required step is bringing your own Entra app via `graph_client_id`
Anthropic's multi-tenant app exists only in the commercial cloud; see
[entra-app](entra-app.md#gcc-high--dod--21vianet) for the registration steps in
the Azure Government / 21Vianet portals. A GCC-High Outlook manifest needs
nothing beyond the usual keys:
```bash
@@ -110,10 +111,9 @@ involve Microsoft.
**Bring your own Entra app.** By default the token is requested as Anthropic's
multi-tenant app (`c2995f31-…`), so its `aud` claim is that GUID. If your
bootstrap endpoint or token-exchange service requires `aud` to match an app
registered in *your* tenant, set `graph_client_id=<your-app-guid>`. Register
the app in Entra as a single-tenant **Single-page application** with redirect
URI `https://pivot.claude.ai/msal-redirect.html`. You handle consent on your
own app — [consent](consent.md) covers the default app only.
registered in *your* tenant, set `graph_client_id=<your-app-guid>`. See
[entra-app](entra-app.md) for the registration steps (redirect URIs, API setup,
admin consent). [consent](consent.md) covers Anthropic's default app only.
**Send an access token instead of the ID token.** With `graph_client_id` alone
the add-in still sends an *ID token* to your bootstrap endpoint — `aud` is your
@@ -145,6 +145,36 @@ needs them to initialize NAA *before* it can read extension attrs or call your
bootstrap endpoint, so neither can arrive through those layers. Leave
`entra_scope` unset and the ID token is sent.
## Use the Entra token as your gateway credential
If your gateway already validates Entra JWTs (`aud` + `scp` against your own
API resource), you don't need a separate `gateway_token` or a bootstrap hop —
set `gateway_auth_source=entra` and the add-in sends the Entra access token it
acquired above directly as `Authorization: Bearer` on every gateway call, and
silently re-acquires it before expiry. The end-user experience is zero-input
SSO: open the add-in, start chatting.
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/build-manifest.mjs" office manifest.xml \
gateway_url=https://llm-gateway.your-org.example \
entra_sso=1 \
graph_client_id=<client-app-guid> \
entra_scope=api://<resource-app-guid>/access_as_user \
gateway_auth_source=entra
```
`gateway_auth_source=entra` requires `entra_scope` (and therefore
`graph_client_id` and `entra_sso=1`); the build script enforces this. It
implies `gateway_auth_header=authorization`, so you can omit that key. Don't
also set `gateway_token` — it's ignored, and the script warns.
**Entra setup:** see [entra-app](entra-app.md) — the *Gateway / bootstrap auth*
row of the permissions table, plus the
[backend validation](entra-app.md#what-your-backend-validates) section for the
`iss`/`aud`/`scp`/JWKS values your gateway should check. GCC High / DoD
deployments are covered in the
[same doc](entra-app.md#gcc-high--dod--21vianet).
## Bootstrap endpoint
`bootstrap_url` points to an HTTPS endpoint you host. At startup the add-in
@@ -46,6 +46,10 @@ const KEYS = {
gateway_token: { pattern: /./, hint: "gateway API key", secret: true },
gateway_auth_header: { pattern: /^(x-api-key|authorization)$/i, hint: "auth header scheme (default: x-api-key)" },
gateway_api_format: { pattern: /^(anthropic|bedrock|vertex)$/i, hint: "anthropic | bedrock | vertex" },
gateway_auth_source: {
pattern: /^entra$/,
hint: "'entra' to use the Entra SSO access token as the gateway Bearer (no gateway_token needed); requires entra_scope",
},
mcp_servers: { pattern: /^\[.*\]$/, hint: "JSON array of {url, label, headers?, discover?}" },
inference_headers: { pattern: /^\{.*\}$/, hint: "JSON object of extra headers to attach to every model request" },
bootstrap_url: { pattern: /^https:\/\//, hint: "HTTPS endpoint returning per-user config" },
@@ -83,7 +87,7 @@ const KEYS = {
},
};
const NEEDS_ENTRA = ["aws_role_arn", "graph_client_id", "entra_scope"];
const NEEDS_ENTRA = ["aws_role_arn", "graph_client_id", "entra_scope", "gateway_auth_source"];
async function main() {
const [host, out, ...pairs] = process.argv.slice(2);
@@ -97,10 +101,7 @@ async function main() {
console.error("error: Amazon Bedrock (aws_role_arn/aws_region) is not currently supported for Outlook");
process.exit(1);
}
if (host !== "outlook" && pairs.some((p) => p.startsWith("graph_client_id="))) {
console.warn("note: graph_client_id only applies to Outlook; it has no effect in the office manifest");
}
// graph_cloud is relevant to both manifests: in Outlook it steers Graph +
// graph_client_id and graph_cloud apply to both manifests: in Outlook it steers Graph +
// Entra sign-in; in office it steers the Entra SSO authority.
const params = new URLSearchParams();
@@ -128,6 +129,14 @@ async function main() {
if (params.has("entra_scope") && !params.has("graph_client_id")) {
throw new Error("entra_scope requires graph_client_id (the scope is requested as your own Entra app, not the default)");
}
if (params.has("gateway_auth_source") && !params.has("entra_scope")) {
throw new Error(
"gateway_auth_source=entra requires entra_scope (the gateway Bearer must be audienced to your API, not Microsoft Graph)",
);
}
if (params.has("gateway_auth_source") && params.has("gateway_token")) {
console.warn("note: gateway_auth_source=entra supersedes gateway_token — drop gateway_token from this manifest");
}
// A non-global graph_cloud needs a BYO Entra app — Anthropic's multi-tenant
// app exists only in the commercial cloud, so the default client_id against
// a sovereign authority fails with an opaque AADSTS700016 at sign-in.