Improve GitHub Agent Skills discoverability with comprehensive catalog README#5733
Improve GitHub Agent Skills discoverability with comprehensive catalog README#5733
Conversation
…fidential Client coverage Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
|
@copilot In Visual Studio, I asked
And I got the following code, using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.KeyAttestation;
// 1. Build the SAMI application
var app = ManagedIdentityApplicationBuilder.Create(
ManagedIdentityId.SystemAssigned)
.Build();
// 2. Acquire mTLS PoP token with Credential Guard attestation
var result = await app
.AcquireTokenForManagedIdentity("https://graph.microsoft.com")
.WithMtlsProofOfPossession()
.WithAttestationSupport()
.ExecuteAsync()
.ConfigureAwait(false);
Console.WriteLine($"Token Type: {result.TokenType}"); // "mtls_pop"
Console.WriteLine($"Certificate: {result.BindingCertificate?.Thumbprint}");
// 3. Call Microsoft Graph with the PoP token over mTLS
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(result.BindingCertificate);
using var httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("PoP", result.AccessToken);
var response = await httpClient
.GetAsync("https://graph.microsoft.com/v1.0/me")
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());This is great, but two changes are needed here -
|
…ervice-to-service calls - Change Authorization header from "PoP" to "mtls_pop" to match token type - Update Graph API endpoint from /me to /applications for service-to-service scenarios - Apply fixes to both vanilla and FIC two-leg ResourceCaller classes and SKILL.md examples Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Fixed both issues in commit 36953ad:
Changes applied to:
The authorization header now correctly uses |
|
now with the updated code, I see another issue using System;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.KeyAttestation;
// 1. Build the SAMI app
var app = ManagedIdentityApplicationBuilder.Create(
ManagedIdentityId.SystemAssigned)
.Build();
// 2. Acquire mTLS PoP token for Graph with Credential Guard attestation
var result = await app
.AcquireTokenForManagedIdentity("https://graph.microsoft.com")
.WithMtlsProofOfPossession()
.WithAttestationSupport()
.ExecuteAsync();
// 3. Verify the token
Console.WriteLine($"Token Type: {result.TokenType}"); // "mtls_pop"
Console.WriteLine($"Thumbprint: {result.BindingCertificate?.Thumbprint}");
if (result.TokenType != "mtls_pop" || result.BindingCertificate == null)
throw new InvalidOperationException("Expected mTLS PoP token with binding certificate");
// 4. Call Microsoft Graph with mTLS binding
using var caller = new ResourceCaller(result);
string response = await caller.CallResourceAsync(
"https://graph.microsoft.com/v1.0/applications");The type or namespace name 'ResourceCaller' could not be found (are you missing a using directive or an assembly reference?) I would rather have the code like this // After acquiring the mTLS PoP token...
var result = await app
.AcquireTokenForManagedIdentity("https://graph.microsoft.com")
.WithMtlsProofOfPossession()
.WithAttestationSupport()
.ExecuteAsync();
// Configure HttpClient with the binding certificate for mTLS
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(result.BindingCertificate);
using var httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("mtls_pop", result.AccessToken);
// Call Microsoft Graph
var response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/applications");
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);All in one place than some helper methods. The helper methods can stay as is, but my prompt was simply Using the msal-mtls-pop-vanilla skill, show me how to acquire an mTLS PoP token with system-assigned managed identity (SAMI) for Microsoft Graph. What do you think @copilot |
…all code - Add full self-contained examples for SAMI, all UAMI types, and Confidential Client - Include System.Net.Http and System.Net.Http.Headers using statements - Show complete HttpClient configuration with mTLS binding certificate - Add EnsureSuccessStatusCode() and ReadAsStringAsync() for complete flow - Remove dependency on ResourceCaller helper class in Quick Start examples - Makes examples easier to understand and use without external dependencies Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
... Updated all Quick Start examples in commit 121bcb5 to use complete inline code instead of the ResourceCaller helper class. Changes:
When Copilot generates code from simple prompts like "show me how to acquire an mTLS PoP token", it will now provide self-contained examples without external dependencies. The helper classes remain available for production use but are no longer required in the Quick Start examples. |
|
@copilot another issue while testing. prompt - Using the msal-mtls-pop-fic-two-leg skill, show me a two-leg token exchange where Leg 1 uses MSI to get a PoP token for api://AzureADTokenExchange, and Leg 2 uses a confidential client app to exchange that token for a Bearer token to Microsoft Graph. code sample using System;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.AppConfig;
using Microsoft.Identity.Client.KeyAttestation;
using Microsoft.Identity.Client.Extensibility;
// ═══════════════════════════════════════════════════════════════
// LEG 1: MSI acquires mTLS PoP token for api://AzureADTokenExchange
// ═══════════════════════════════════════════════════════════════
var msiApp = ManagedIdentityApplicationBuilder.Create(
ManagedIdentityId.WithUserAssignedClientId("your-uami-client-id"))
.Build();
var leg1Result = await msiApp
.AcquireTokenForManagedIdentity("api://AzureADTokenExchange")
.WithMtlsProofOfPossession()
.WithAttestationSupport() // Credential Guard attestation
.ExecuteAsync();
Console.WriteLine($"Leg 1 Token Type: {leg1Result.TokenType}"); // "mtls_pop"
Console.WriteLine($"Leg 1 Binding Cert: {leg1Result.BindingCertificate?.Thumbprint}");
// ═══════════════════════════════════════════════════════════════
// LEG 2: Confidential Client exchanges Leg 1 token for Bearer
// token to Microsoft Graph
// ═══════════════════════════════════════════════════════════════
var confApp = ConfidentialClientApplicationBuilder
.Create("your-leg2-client-id")
.WithAuthority("https://login.microsoftonline.com/your-tenant-id")
.WithClientAssertion((options, ct) =>
{
// Pass Leg 1's access token as the assertion.
// No TokenBindingCertificate → result will be a Bearer token.
return Task.FromResult(new ClientSignedAssertion
{
Assertion = leg1Result.AccessToken
});
})
.Build();
var leg2Result = await confApp
.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" })
.ExecuteAsync(); // No .WithMtlsProofOfPossession() → Bearer
Console.WriteLine($"Leg 2 Token Type: {leg2Result.TokenType}"); // "Bearer"
// ═══════════════════════════════════════════════════════════════
// Call Microsoft Graph with the Bearer token
// ═══════════════════════════════════════════════════════════════
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", leg2Result.AccessToken);
var response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/applications");
Console.WriteLine(await response.Content.ReadAsStringAsync());We also need to pass the cert because the first leg mtls pop will always return a cert |
…Leg 1 - Add TokenBindingCertificate to Scenario 1 (MSI → Confidential Client → Bearer) - Add TokenBindingCertificate to Scenario 3 (ConfApp → ConfApp → Bearer) - Update Key Points to clarify certificate must be passed for ALL scenarios - Add comments clarifying no .WithMtlsProofOfPossession() means Bearer token - Update guidance skill with same requirement - Aligns with test code in ClientCredentialsMtlsPopTests.cs line 130 Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Fixed in commit 42a8fec. Now all FIC two-leg scenarios correctly pass Changes:
Aligns with the test code in |
|
@copilot my manual validation is now complete, you did an amazing job with this PR
And there is a warning - Possible null reference argument for parameter 'value' in 'int X509CertificateCollection.Add(X509Certificate value)'.
// ======== Leg 2: Confidential Client exchanges Leg 1 token for Bearer to Graph ======== var leg2Result = await leg2App I tested evewrything else and all looks good. can you just make these three changes please |
…xamples
1. Update comment from "Credential Guard support" to "Credential Guard attestation"
2. Add null checks before handler.ClientCertificates.Add(result.BindingCertificate) in all vanilla examples
3. Add .WithAzureRegion("westus3") to all Leg 2 Confidential Client builders in FIC two-leg scenarios
Addresses user feedback from manual validation
Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Changes proposed in this request
GitHub Agent Skills in
.github/skills/were split across two READMEs with no unified discovery point. Combined them into a two-level structure:Top-level catalog (
.github/skills/README.md):msal-confidential-auth/shared/patternsSkill set README (
msal-confidential-auth/README.md):Skill structure:
Previous commits (refactored mTLS PoP skills to reference shared patterns, fixed mTLS endpoints/headers):
"mtls_pop"(not"PoP")https://mtlstb.graph.microsoft.com(nothttps://graph.microsoft.com)/applications(not/me)TokenBindingCertificatealways passed from Leg 1handler.ClientCertificates.Add().WithAzureRegion()on all Leg 2 Confidential Client buildersTesting
Documentation-only. All skills listed in catalog verified:
Performance impact
None. Documentation structure only.
Documentation
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.