Azure / Azure/azure-sdk-for-rust

Key Vault challenge-based authentication fails to extract tenant ID from DSTSv2 authority URIs

Open
#3,791 0 comments 0 reactions 0 assignees View on GitHub
Client KeyVault
Dominant language
Rust
Stars
884
Forks
365
Avg merge
2d 19h
Merged PRs (30d)
109

Description

# Key Vault challenge-based authentication fails to extract tenant ID from DSTSv2 authority URIs

## Summary

The Key Vault challenge-based authentication policy incorrectly parses the tenant ID from `WWW-Authenticate` challenge responses when the authorization URI uses a DSTSv2 (Distributed Security Token Service v2) authority format. This causes authentication failures when connecting to Key Vault endpoints that return DSTSv2 challenges.

## Background

When a Key Vault client makes an unauthenticated request, the service responds with a `401 Unauthorized` containing a `WWW-Authenticate` header like:

```
Bearer authorization="https://login.microsoftonline.com/{tenantId}", resource="https://vault.azure.net"
```

The client parses the `authorization` URI to extract the tenant ID (typically the first path segment) and uses it to acquire a token.

## The Bug

The current implementation assumes the tenant ID is always the **first path segment** of the authorization URI. This is correct for standard Entra ID (AAD) authorities:

```
https://login.microsoftonline.com/{tenantId}/oauth2/authorize
^^^^^^^^^
Segments[1] = tenantId ✅
```

However, DSTSv2 authority URIs have a **different path layout** where `dstsv2` is the first path segment and the tenant ID is the **second**:

```
https://uswest2-passive-dsts.dsts.core.windows.net/dstsv2/{tenantId}
^^^^^^ ^^^^^^^^^
Seg[1] Seg[2] = tenantId
```

With the current logic, the extracted "tenant ID" is the literal string `dstsv2` instead of the actual GUID, causing token acquisition to fail or target the wrong tenant.

### Example challenge header from a DSTSv2 endpoint

```
Bearer authorization="https://uswest2-passive-dsts.dsts.core.windows.net/dstsv2/de763a21-49f7-4b08-a8e1-52c8fbc103b4", resource="https://foo.bar.core.windows.net"
```

## Expected Behavior

The tenant ID should be correctly extracted as `de763a21-49f7-4b08-a8e1-52c8fbc103b4`, regardless of whether the authority URI is a standard Entra ID endpoint or a DSTSv2 endpoint.

## Fix Description

When parsing the authorization URI to extract the tenant ID:

1. Extract the first path segment as usual.
2. If the first path segment equals `dstsv2` (case-insensitive) **and** there is a subsequent path segment, use that subsequent segment as the tenant ID instead.

### Pseudocode

```
tenantId = authorizationUri.pathSegments[1] // e.g. "dstsv2" or a GUID

if tenantId.equalsIgnoreCase("dstsv2") AND pathSegments.length > 2:
tenantId = authorizationUri.pathSegments[2]
```

## Affected Area

This bug is in the **Key Vault challenge-based authentication policy** — the component that handles the `401 → parse challenge → acquire token → retry` flow. In each language SDK, this is typically:

| Language | Approximate Location |
|----------|---------------------|
| .NET | `ChallengeBasedAuthenticationPolicy.ChallengeParameters` constructor |
| Python | `ChallengeAuthPolicy` / `HttpChallenge` class |
| Java | `KeyVaultCredentialPolicy` |
| JavaScript/TypeScript | `challengeBasedAuthenticationPolicy` / challenge parsing utilities |
| Go | `challengePolicy` / challenge parsing in `azkeys`, `azsecrets`, `azcertificates` |

## Reference Fix

The .NET fix is in [Azure/azure-sdk-for-net#56416](https://github.com/Azure/azure-sdk-for-net/pull/56416).

Key change in `ChallengeBasedAuthenticationPolicy.cs`:

```csharp
TenantId = authorizationUri.Segments[1].Trim('/');
if (TenantId.Equals("dstsv2", StringComparison.OrdinalIgnoreCase) && authorizationUri.Segments.Length > 2)
{
TenantId = authorizationUri.Segments[2].Trim('/');
}
```

## Suggested Test Case

Simulate a Key Vault challenge response with a DSTSv2 authorization URI and verify the tenant ID is correctly extracted:

1. Mock a `401` response with header:
```
WWW-Authenticate: Bearer authorization="https://uswest2-passive-dsts.dsts.core.windows.net/dstsv2/{expectedTenantGuid}", resource="https://foo.bar.core.windows.net"
```
2. Mock a `200` response for the subsequent authenticated request.
3. Capture the tenant ID passed to the credential's `getToken` call.
4. Assert the captured tenant ID equals the expected GUID, **not** the literal string `"dstsv2"`.

## Labels

`KeyVault`, `bug`, `client`

Contributor guide

Open the contributing guide

Research direction

Start with the Rust challengePolicy and challenge parsing in azkeys, azsecrets, and azcertificates, then trace the 401 challenge through token acquisition and retry. Add the suggested mocked DSTSv2 challenge test and verify that the credential's getToken call receives the expected tenant GUID rather than "dstsv2".

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
authentication
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.