Skip to content

Add auth troubleshooting guide; remove unsupported license-key gate; link Adaptive Card Validator#247

Open
brandonpollett wants to merge 4 commits into
mainfrom
personal/brpoll/auth-improvements
Open

Add auth troubleshooting guide; remove unsupported license-key gate; link Adaptive Card Validator#247
brandonpollett wants to merge 4 commits into
mainfrom
personal/brpoll/auth-improvements

Conversation

@brandonpollett

Copy link
Copy Markdown
Collaborator

Summary

Adds a partner-facing authentication troubleshooting guide, removes the license-key gate from the sample extension (the platform does not actually support license-key auth), and links the new Adaptive Card Validator from the spots partners are most likely to land.

Authentication troubleshooting guide

  • New doc/Troubleshooting-Authentication.md — symptom -> cause matrix, deep dives covering:
    • 401 InvalidAudience / InvalidIssuer / InvalidApplicationId
    • Extension deployed but receives no traffic (incl. WAF / firewall / network ACL dropping requests)
    • AADSTS500011 — Runtime service principal missing in tenant
    • Customer/tenant identity model (it's in the payload, not the JWT)
    • Application ID URI / identifierUris mistakes — top source of InvalidAudience after partners think everything else is correct
  • FAQ, pre-flight checklist, diagnostic-bundle template for support tickets
  • Linked from doc/Authentication.md, doc/AuthenticationDesign.md, and physician/QUICKSTART.md

License-key cleanup

The platform doesn't support license-key auth — its presence in the sample was misleading partners. Fully removed:

  • Deleted LicenseKeyMiddleware.cs, LicenseKeyOptions.cs, and the now-empty Middleware/ folder
  • Removed license-key middleware from UseFullSecurity, the Swagger security definition, and the DI registration
  • Removed LicenseKey sections from appsettings.json and appsettings.Development.json
  • Updated comments / XML docs / response-code descriptions
  • Rewrote doc/Authentication.md as JWT-only (single gate); updated doc/AuthenticationDesign.md overview and .github/copilot-instructions.md

Adaptive Card Validator

Added discoverable references to https://cardvalidator.copilot.dragon.com/ in:

  • physician/QUICKSTART.md — next to the existing Adaptive Card Specification link
  • physician/.../pythonSampleExtension/README.md — next to the adaptive_card_payload example response
  • physician/src/models/.../AdaptiveCardPayload.cs<remarks> XML doc (IntelliSense discovery)

Verification

  • dotnet build of SampleExtension.Web -> Build succeeded. 0 Warning(s). 0 Error(s).
  • Grep confirms zero remaining references to LicenseKey / license key / X-License-Key / ValidKeys anywhere in the repo.

…d Adaptive Card Validator references

Adds a partner-facing authentication troubleshooting guide and removes the
license-key gate from the sample extension (the platform does not support
license-key auth, so its inclusion was confusing partners).

Authentication troubleshooting guide
- New doc/Troubleshooting-Authentication.md: symptom -> cause matrix, deep
  dives (InvalidAudience/InvalidIssuer/InvalidApplicationId, no-traffic /
  WAF / network ACLs, AADSTS500011, customer identity model,
  Application ID URI / identifierUris mistakes), FAQ, pre-flight checklist,
  and diagnostic bundle to attach to support tickets.
- Linked from doc/Authentication.md, doc/AuthenticationDesign.md, and
  physician/QUICKSTART.md so partners hitting auth errors land on it from
  any of the obvious entry points.

License-key cleanup (sample extension)
- Deleted LicenseKeyMiddleware.cs and LicenseKeyOptions.cs (and the now-empty
  Middleware/ folder).
- Removed the LicenseKey middleware from UseFullSecurity, the LicenseKey
  Swagger security definition, and the LicenseKeyOptions DI registration.
- Removed the LicenseKey sections from appsettings.json and
  appsettings.Development.json.
- Updated KnownRoutes/ProcessController/Program.cs comments and XML docs.
- Rewrote doc/Authentication.md as JWT-only (single-gate); updated
  doc/AuthenticationDesign.md overview and .github/copilot-instructions.md.

Adaptive Card Validator references
- Added a tip linking to https://cardvalidator.copilot.dragon.com/ from
  physician/QUICKSTART.md, the Python sample README, and the
  AdaptiveCardPayload.cs XML doc (for IntelliSense discovery).

Build verified: dotnet build of SampleExtension.Web -> 0 warnings, 0 errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 12, 2026 00:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Dragon Copilot Extension samples and docs to reflect the platform’s actual security model (JWT/Entra-only), adds a partner-facing authentication troubleshooting guide, and improves discoverability of the Adaptive Card Validator.

Changes:

  • Added a new authentication troubleshooting guide and linked it from existing auth docs and the Physician quickstart.
  • Removed the sample extension’s license-key middleware/options and related configuration/docs (JWT-only).
  • Added references to the Adaptive Card Validator in docs and in AdaptiveCardPayload XML remarks.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Program.cs Updates pipeline comment to reflect JWT-only security.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Middleware/LicenseKeyMiddleware.cs Removes unsupported license-key middleware.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Extensions/WebApplicationExtensions.cs Removes license-key middleware and Swagger security scheme wiring.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Extensions/ServiceCollectionExtensions.cs Removes DI options wiring for license-key configuration.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Controllers/ProcessController.cs Updates 403 response documentation to claims-based authorization.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Configuration/LicenseKeyOptions.cs Removes unsupported license-key options type.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/Configuration/KnownRoutes.cs Updates public-route comment to match JWT-only model.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/appsettings.json Removes LicenseKey configuration block.
physician/src/samples/DragonCopilot/Workflow/SampleExtension.Web/appsettings.Development.json Removes LicenseKey configuration block.
physician/src/samples/DragonCopilot/Workflow/pythonSampleExtension/README.md Adds Adaptive Card Validator link for debugging card output.
physician/src/models/Dragon.Copilot.Physician.Models/AdaptiveCardPayload.cs Adds XML remarks linking to Adaptive Card Validator.
physician/QUICKSTART.md Links auth troubleshooting guide and adds Adaptive Card Validator tip.
doc/Troubleshooting-Authentication.md Adds new partner-facing auth troubleshooting guide.
doc/AuthenticationDesign.md Updates overview to JWT-only and links troubleshooting guide.
doc/Authentication.md Rewrites as JWT-only and links troubleshooting guide.
.github/copilot-instructions.md Updates repo guidance to JWT-only authentication.

Comment on lines +52 to +55
**Checks:**
1. `iss` must be exactly `https://login.microsoftonline.com/{your-tenant-id}/` — note the trailing slash and v2.0 endpoint format.
2. Confirm the tenant ID configured in your JWT validator matches the `auth.tenantId` in your `extension.yaml` manifest. The two values **must** match — the `dragon-copilot` CLI prompts for the same value to keep them aligned.
3. Do not use the `common` or `organizations` authority for a single-tenant extension. Pin the validator to your tenant GUID.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed in 8282803: §2.2 now reads https://login.microsoftonline.com/{tenant}/v2.0 (the /v2.0 suffix is the v2 issuer that pairs with
equestedAccessTokenVersion: 2). The matrix row and pre-flight checklist were updated to match.

Comment thread doc/Troubleshooting-Authentication.md Outdated
Comment on lines +124 to +125
**Mental model:** the platform does not "send" an audience. It acquires a token using `identifierUri` **as the scope**, and the resulting `aud` claim is derived from it. If `identifierUri` does not line up with the actual endpoint host, the token your extension receives will have an `aud` that cannot match a sensible validation rule.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — this was the root issue across all three of your comments. §2.7's mental-model paragraph is rewritten in 8282803: identifierUri is the scope the Runtime requests, and with v2 tokens aud is the Client ID GUID, not the URI. A scope mismatch now correctly attributes to "token acquisition fails → no traffic" (pointing at §2.4), not InvalidAudience.

Comment on lines +171 to +180
```text
1. Read the endpoint host from extension.yaml (tools[].endpoint).
2. Read the identifierUris array from your Entra app registration manifest.
3. Confirm the array contains exactly: api://{your-tenant-id}/{that-host}.
4. Decode a real token at https://jwt.ms and confirm aud equals that string.
```

**Validation rule for your extension code:**
Reject the request if `aud` does not equal `api://{your-tenant-id}/{Host header of the request}`. This prevents accidental cross-environment token replay.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Rewritten in 8282803: the "Validation rule for your extension code" block (which recommended deriving expected aud from the HTTP Host header) is removed. The quick-validation procedure now says to confirm aud == Client ID GUID, azp == Runtime client ID, and iss == .../{tenant}/v2.0 — matching AuthenticationDesign.md and the sample's validator.

brandonpollett and others added 3 commits June 11, 2026 20:29
The validator preview pane silently failed (Invalid Card) on the sample's
output even though structural validation reported Valid JSON. Two issues:

1. AdaptiveCardPayload.Version was hard-coded to "1.3". The Dragon
   Copilot card spec does not require �ersion; the validator's reference
   sample omits it entirely. Made Version nullable and tagged it with
   JsonIgnore(Condition = WhenWritingNull) so it is omitted by default.
   Partners can still set a specific version if they need one.

2. VisualizationResource.References was being emitted as [] (empty
   array). The validator treats an empty 
eferences array as a render-
   blocker (silent fail) and a missing 
eferences field as a hard error
   (Missing property "references"). Populated the sample with a Web
   reference pointing at the published adaptive-card-spec page so partners
   see a working reference end-to-end.

Also tagged the nullable string fields on VisualizationReference
(SectionId, Text, Title, Url) with JsonIgnore(WhenWritingNull) so a
Web-typed reference no longer serializes sectionId: null / 	ext: null
(which the validator flags as "Property X is not allowed").

XML doc updates:
- AdaptiveCardPayload: validator link now also notes the aka.ms shortlink
  and links to the official adaptive-card-spec.
- AdaptiveCardPayload.Version: documents that the field is optional and
  should normally be omitted.
- VisualizationResource.References: warns that an empty array causes the
  validator preview to fail silently and links to the spec.

Verified end-to-end against https://cardvalidator.copilot.dragon.com/:
status Valid JSON, zero warnings, full preview renders (title, all five
action buttons, references footer).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…alidator

Mirrors the C# fix in the previous commit. Without these changes the
validator preview pane silently failed (`Invalid Card`) on the python
sample's output even though structural validation reported `Valid JSON`.

Changes in `app/service.py`:
- Removed the hardcoded `"version": "1.6"` from the active adaptive
  card payload (and from the two commented-out alternate cards). The
  Dragon Copilot card spec does not require `version` and the validator's
  reference sample omits it.
- Replaced `references=[]` with a `Web`-typed reference pointing at
  the published adaptive-card-spec page. The validator treats an empty
  `references` array as a render-blocker (silent fail).
- Added inline comments at both sites explaining why `version` is
  omitted and why `references` must be non-empty.
- Applied the same fix to the two commented-out example cards so any
  future re-enablement starts from a working baseline.

Test update in `app/tests/test_adaptive_card.py`:
- Replaced `assert ac.get("version") == "1.6"` with
  `assert "version" not in ac` to lock in the new behavior.

Verified end-to-end against https://cardvalidator.copilot.dragon.com/:
status `Valid JSON`, zero warnings, preview renders both action buttons
and the references footer. All 5 pytest tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three related fixes to doc/Troubleshooting-Authentication.md, all
stemming from the same root mistake: §2.7 had framed the JWT `aud`
claim as being derived from `identifierUri`, which is only true for
v1 tokens. With `requestedAccessTokenVersion: 2` (which this repo
explicitly recommends), `aud` is the extension's Client ID GUID and
`identifierUri` only appears as the *scope* the Runtime requests when
acquiring a token.

Changes:
- §2.2 / matrix / pre-flight checklist: corrected the expected v2.0
  issuer to `https://login.microsoftonline.com/{tenant-id}/v2.0`
  (added the missing `/v2.0` suffix that pairs with v2 tokens).
- §2.7: rewrote the "Mental model" paragraph to state that
  `identifierUri` is the scope, and that a mismatch causes token
  acquisition to fail (symptom: no traffic — point at §2.4) rather
  than `InvalidAudience` on the extension side.
- §2.7 "Quick validation procedure" now tells partners to verify
  `aud == Client ID GUID`, `azp == Runtime client ID`, and
  `iss == .../{tenant-id}/v2.0` — matching the sample extension's
  validator and the guidance in AuthenticationDesign.md.
- §2.7: removed the "Validation rule for your extension code" block
  that recommended deriving expected `aud` from the HTTP Host header
  (which contradicted the v2-token guidance and would have encouraged
  unsafe host-header-trusting validation logic).
- Matrix: split the old "InvalidAudience even though Client ID looks
  correct" row into two rows that correctly attribute (a) `aud`
  containing the App ID URI -> v1 tokens (points at §2.1) and (b)
  no-traffic / token-acquisition failure -> malformed `identifierUris`
  (points at §2.7).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants