Skip to content

Fix /token (GET) response field: tokenType → token_type (fixes newer Azure SDK clients) - #125

Merged
Gérald Barré (geraldbarre-workleap) merged 1 commit into
workleap:mainfrom
JoeBengalen:fix-token_type
Sep 23, 2026
Merged

Gérald Barré (geraldbarre-workleap) merged 1 commit into
workleap:mainfrom
JoeBengalen:fix-token_type

Conversation

@JoeBengalen

Copy link
Copy Markdown
Contributor

Summary

The GET /token endpoint (the one consumed via IDENTITY_ENDPOINT + IMDS_ENDPOINT, emulating the Azure Arc managed-identity source) returns the token-type field as:

["tokenType"] = "Bearer",

This field name doesn't match what recent versions of the Azure SDK for .NET actually parse. As a result, any consumer using a moderately recent Azure.Identity/Azure.Core version gets a silently malformed Authorization header — missing the Bearer scheme prefix entirely — and every downstream request fails with a 401/RequestFailedException.

The POST /token endpoint already uses the correct field name (token_type) — only the GET endpoint has the bug.

Why this breaks

  • Microsoft.Identity.Client (MSAL), which recent Azure.Identity/Azure.Core versions use internally to handle managed-identity token acquisition (including the Arc flow this proxy emulates), deserializes the token type from a JSON property explicitly named token_type:

    // Microsoft.Identity.Client.ManagedIdentity.ManagedIdentityResponse
    [JsonProperty("token_type")]
    public string TokenType { get; set; }

    Since the proxy sends tokenType (camelCase) instead, this property is never populated and defaults to null/empty.

  • Starting in Azure.Core 1.59.0, BearerTokenAuthenticationPolicy stopped hardcoding the Authorization header scheme as "Bearer " and instead builds it from the token's actual type:

    // Azure.Core, BearerTokenAuthenticationPolicy.AccessTokenCache
    targetTcs.SetResult(new AuthHeaderValueInfo(token.TokenType + " " + token.Token, ...));

    (This change shipped alongside experimental mTLS Proof-of-Possession support, where the token type is no longer always "Bearer".)

    Combined with the empty TokenType above, the resulting header becomes a single leading space followed by the raw token — no Bearer scheme at all — which every Azure resource correctly rejects with a 401.

Older SDK versions never hit this because managed-identity token acquisition was implemented by hand in Azure.Identity and always force-set TokenType = "Bearer" regardless of what the JSON response said, masking the field-name mismatch for years.

Minimal reproduction

// dotnet add package Azure.Data.AppConfiguration --version 1.10.0   (pulls Azure.Core 1.59.0)
// dotnet add package Azure.Identity --version 1.21.0

Environment.SetEnvironmentVariable("IDENTITY_ENDPOINT", "http://127.0.0.1:8080/token");
Environment.SetEnvironmentVariable("IMDS_ENDPOINT", "dummy_required_value");

var credential = new ManagedIdentityCredential();
var options = new Azure.Data.AppConfiguration.ConfigurationClientOptions();
options.AddPolicy(new HeaderDumpPolicy(), Azure.Core.Pipeline.HttpPipelinePosition.PerRetry);

var client = new Azure.Data.AppConfiguration.ConfigurationClient(
    new Uri("https://<any-appconfig-instance>.azconfig.io"), credential, options);

await client.GetConfigurationSettingsAsync(new Azure.Data.AppConfiguration.SettingSelector())
    .AsPages().GetAsyncEnumerator().MoveNextAsync();

class HeaderDumpPolicy : Azure.Core.Pipeline.HttpPipelineSynchronousPolicy
{
    public override void OnSendingRequest(Azure.Core.HttpMessage message)
    {
        message.Request.Headers.TryGetValue("Authorization", out var v);
        Console.WriteLine($"Authorization: [{v?.Substring(0, Math.Min(20, v.Length))}]");
    }
}

Run this against the container as currently published: the printed header is [ eyJ...] (leading space, no scheme) and the call fails with a 401. With the field name corrected and the image rebuilt, the header becomes [Bearer eyJ...] and the call succeeds.

I verified this locally end-to-end: same package versions, same real Azure AD credentials via az, the only variable changed was this field name — unpatched image fails every request, patched image succeeds.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The focused change has no unresolved blocking issues.

Review effort: Lite
Findings: None

What changed in this PR

Updates the GET /token response to use the Azure SDK-compatible token_type field.

Changes:

  • Renames tokenType to token_type.
  • Preserves existing token metadata and serialization.
File Summary
Program.cs Corrects the GET token response field name.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@geraldbarre-workleap
Gérald Barré (geraldbarre-workleap) merged commit 2bbe1db into workleap:main Sep 23, 2026
2 of 3 checks passed
@JoeBengalen

Copy link
Copy Markdown
Contributor Author

New image works for me. Thank you for the fast merge and release :)

@JoeBengalen
Martijn Wennink (JoeBengalen) deleted the fix-token_type branch September 24, 2026 07:21
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.

3 participants