Unauthorized (401) error when calling the admin API with version 2 Azure AAD tokens. #688

Closed
opened 2025-12-29 15:30:52 +01:00 by adam · 16 comments
Owner

Originally created by @AchoArnold on GitHub (Apr 29, 2025).

Originally assigned to: @StefH on GitHub.

Describe the bug

The issuwer from https://login.microsoftonline.com/{tennantID}/.well-known/openid-configuration is given as https://sts.windows.net/{tennantID}/ but when you decode a v2 AAD token using jwt.ms, the issuer is set as https://login.microsoftonline.com/{tennantID}/v2.0 And the recommendation is that instead of comparing the entire string, you should only compare that the tennants match. The identity model even has a helper method for validating the issuwer 36fb5f5556/tools/CrossPlatformValidator/CrossPlatformValidation/CrossPlatformValidation/RequestValidator.cs (L42)

Expected behavior:

I can make wiremock admin API requests with with AAD version 2 tokens

Test to reproduce

  1. Configure Wiremock server with AAD
  2. Generate an AAD token with version 2 https://learn.microsoft.com/en-us/entra/identity-platform/msal-acquire-cache-tokens
  3. Send a request to the server for the wiremock admin requests e.g /__admin/requests and you'll get 401 instead of 200

Provide additional information if any.

Originally created by @AchoArnold on GitHub (Apr 29, 2025). Originally assigned to: @StefH on GitHub. ### Describe the bug - The application throws `401` When it is configured to use version 2 of the AAD token. This happens because of the validation code here https://github.com/WireMock-Net/WireMock.Net/blob/e7310fbc7b2930be099e65b5673c7846576496f7/src/WireMock.Net/Authentication/AzureADAuthenticationMatcher.cs#L61 The `issuwer` from `https://login.microsoftonline.com/{tennantID}/.well-known/openid-configuration` is given as `https://sts.windows.net/{tennantID}/` but when you decode a v2 AAD token using jwt.ms, the issuer is set as ` https://login.microsoftonline.com/{tennantID}/v2.0` And the recommendation is that instead of comparing the entire string, you should only compare that the tennants match. The identity model even has a helper method for validating the issuwer https://github.com/AzureAD/microsoft-identity-web/blob/36fb5f555638787823a89e89c67f17d6a10006ed/tools/CrossPlatformValidator/CrossPlatformValidation/CrossPlatformValidation/RequestValidator.cs#L42 ### Expected behavior: I can make wiremock admin API requests with with AAD version 2 tokens ### Test to reproduce 1. Configure Wiremock server with AAD 2. Generate an AAD token with version 2 https://learn.microsoft.com/en-us/entra/identity-platform/msal-acquire-cache-tokens 3. Send a request to the server for the wiremock admin requests e.g `/__admin/requests` and you'll get 401 instead of 200 ### Other related info Provide additional information if any.
adam added the bug label 2025-12-29 15:30:52 +01:00
adam closed this issue 2025-12-29 15:30:52 +01:00
Author
Owner

@StefH commented on GitHub (Apr 30, 2025):

@AchoArnold

I did update some code, but was not able to test it. (https://github.com/WireMock-Net/WireMock.Net/pull/1288)
Can you test latest preview 1.8.0-ci-19960
(https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions)

@StefH commented on GitHub (Apr 30, 2025): @AchoArnold I did update some code, but was not able to test it. (https://github.com/WireMock-Net/WireMock.Net/pull/1288) Can you test latest preview `1.8.0-ci-19960` (https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions)
Author
Owner

@StefH commented on GitHub (May 2, 2025):

@AchoArnold

I did update some code, but was not able to test it. (https://github.com/WireMock-Net/WireMock.Net/pull/1288)
Can you test latest preview 1.8.0-ci-19960
(https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions)

@StefH commented on GitHub (May 2, 2025): @AchoArnold I did update some code, but was not able to test it. (https://github.com/WireMock-Net/WireMock.Net/pull/1288) Can you test latest preview `1.8.0-ci-19960` (https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions)
Author
Owner

@AchoArnold commented on GitHub (May 2, 2025):

Hello @StefH

Thanks for the fast response, I'll be able to test it out on Monday next week.

@AchoArnold commented on GitHub (May 2, 2025): Hello @StefH Thanks for the fast response, I'll be able to test it out on Monday next week.
Author
Owner

@AchoArnold commented on GitHub (May 2, 2025):

Hello @StefH

I installed the nuget source but I can't see this speicifc CI version

Image

@AchoArnold commented on GitHub (May 2, 2025): Hello @StefH I installed the nuget source but I can't see this speicifc CI version ![Image](https://github.com/user-attachments/assets/a2478a75-3040-4b00-8a2c-f47767ca0b8d)
Author
Owner

@AchoArnold commented on GitHub (May 2, 2025):

@StefH I downloaded the nupkg from here https://dev.azure.com/stef/WireMock.Net/_build/results?buildId=9960&view=artifacts&pathAsName=false&type=publishedArtifacts and ran the specific version.

I can confirm that the bug is now fixed I'm authenticating with 200

Image

@AchoArnold commented on GitHub (May 2, 2025): @StefH I downloaded the nupkg from here https://dev.azure.com/stef/WireMock.Net/_build/results?buildId=9960&view=artifacts&pathAsName=false&type=publishedArtifacts and ran the specific version. I can confirm that the bug is now fixed I'm authenticating with 200 ![Image](https://github.com/user-attachments/assets/07421008-13bf-4f3a-9d8f-c00a2580d2e4)
Author
Owner

@StefH commented on GitHub (May 2, 2025):

That's good news.

Would you be so kind to share the postman request or collection (without any traceable credentials to your system). I cannot find my own test anymore.
And can you also share the C# code for this? (I also cannot find any example code anymore..)

@StefH commented on GitHub (May 2, 2025): That's good news. Would you be so kind to share the postman request or collection (without any traceable credentials to your system). I cannot find my own test anymore. And can you also share the C# code for this? (I also cannot find any example code anymore..)
Author
Owner

@AchoArnold commented on GitHub (May 2, 2025):

Hello @StefH , I don't have any postman but I can send the instructions here.

To get v2.0 AAD token you need to modify the Manifest of your AAD app registration by following the instructions here https://docs.azure.cn/en-us/entra/identity-platform/scenario-protected-web-api-app-registration#accepted-token-version

You can then get the token using this CURL command

# replace AadClientId, AadApplicationURI, AadClientSecret, AadTenantId with the AAD details from the azure portal.
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id={AadClientId}&scope={AadApplication Uri}/.default&client_secret={AadClientSecret}&grant_type=client_credentials' 'https://login.microsoftonline.com/{AadTenantId}/oauth2/v2.0/token'

Once you obtain the token you can verify that it is version 2 using this website https://jwt.ms, I start the wiremock server. I'm using the WireMock.Net.StandAlone package.

using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.Settings;

var settings = new WireMockServerSettings
{
    AllowPartialMapping=true,
    Logger = new WireMockConsoleLogger(),
    UseSSL = true,
    AdminAzureADTenant = "AadTennatId",
    AdminAzureADAudience = "AadAudience",
    StartAdminInterface=true
};

StandAloneApp.Start(settings);

Console.WriteLine("Press any key to stop the server");
Console.ReadKey();

And then I make a GET request to {WiremockServerURL}/__admin/requests with the Bearer AadToken set in the Authorization header and 200 for success 401 for authentication errors.

@AchoArnold commented on GitHub (May 2, 2025): Hello @StefH , I don't have any postman but I can send the instructions here. To get v2.0 AAD token you need to modify the `Manifest` of your AAD app registration by following the instructions here https://docs.azure.cn/en-us/entra/identity-platform/scenario-protected-web-api-app-registration#accepted-token-version You can then get the token using this CURL command ```bash # replace AadClientId, AadApplicationURI, AadClientSecret, AadTenantId with the AAD details from the azure portal. curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id={AadClientId}&scope={AadApplication Uri}/.default&client_secret={AadClientSecret}&grant_type=client_credentials' 'https://login.microsoftonline.com/{AadTenantId}/oauth2/v2.0/token' ``` Once you obtain the token you can verify that it is version 2 using this website https://jwt.ms, I start the wiremock server. I'm using the `WireMock.Net.StandAlone` package. ```c# using WireMock.Logging; using WireMock.Net.StandAlone; using WireMock.Settings; var settings = new WireMockServerSettings { AllowPartialMapping=true, Logger = new WireMockConsoleLogger(), UseSSL = true, AdminAzureADTenant = "AadTennatId", AdminAzureADAudience = "AadAudience", StartAdminInterface=true }; StandAloneApp.Start(settings); Console.WriteLine("Press any key to stop the server"); Console.ReadKey(); ``` And then I make a `GET` request to `{WiremockServerURL}/__admin/requests` with the `Bearer AadToken` set in the `Authorization` header and 200 for success 401 for authentication errors.
Author
Owner

@AchoArnold commented on GitHub (May 2, 2025):

@StefH I just did a regression test and this new version of the wiremock.net package doesn't authenticate when I use a ver1.0 AAD token

@AchoArnold commented on GitHub (May 2, 2025): @StefH I just did a regression test and this new version of the wiremock.net package doesn't authenticate when I use a `ver1.0` AAD token
Author
Owner

@StefH commented on GitHub (May 2, 2025):

@AchoArnold
I did a quick test for V1.
And the bearer token is decoded as:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "CNv0OI3RwqlHFEVnaoMAshCH2XE",
  "kid": "CNv0OI3RwqlHFEVnaoMAshCH2XE"
}.{
  "aud": "<RESOURCE_ID>",
  "iss": "https://sts.windows.net/<TENANT>/",
  "iat": 1746192065,
  "nbf": 1746192065,
  "exp": 1746195965,
  "aio": "k2RgYJilPWmi6W0Hn13H2QOiWbzmAgA=",
  "appid": "<RESOURCE_ID>",
  "appidacr": "1",
  "idp": "https://sts.windows.net/<TENANT>/",
  "oid": "558d2f5e-b37e-43e8-94ab-93f2a7cc8666",
  "rh": "1.AV4A8wwLArLWTkabLUXhJCREKBrVg-CmAWxEitUMXH8AIggRAQBeAA.",
  "sub": "558d2f5e-b37e-43e8-94ab-93f2a7cc8666",
  "tid": "<TENANT>",
  "uti": "MRNZngGQ2ESZJFYeCaIaAA",
  "ver": "1.0"
}.[Signature]

And calling an /__admin endpoint in WireMock.Net works fine.

Can you double check how the issuer looks on your side?

@StefH commented on GitHub (May 2, 2025): @AchoArnold I did a quick test for V1. And the bearer token is decoded as: ``` raw { "typ": "JWT", "alg": "RS256", "x5t": "CNv0OI3RwqlHFEVnaoMAshCH2XE", "kid": "CNv0OI3RwqlHFEVnaoMAshCH2XE" }.{ "aud": "<RESOURCE_ID>", "iss": "https://sts.windows.net/<TENANT>/", "iat": 1746192065, "nbf": 1746192065, "exp": 1746195965, "aio": "k2RgYJilPWmi6W0Hn13H2QOiWbzmAgA=", "appid": "<RESOURCE_ID>", "appidacr": "1", "idp": "https://sts.windows.net/<TENANT>/", "oid": "558d2f5e-b37e-43e8-94ab-93f2a7cc8666", "rh": "1.AV4A8wwLArLWTkabLUXhJCREKBrVg-CmAWxEitUMXH8AIggRAQBeAA.", "sub": "558d2f5e-b37e-43e8-94ab-93f2a7cc8666", "tid": "<TENANT>", "uti": "MRNZngGQ2ESZJFYeCaIaAA", "ver": "1.0" }.[Signature] ``` And calling an /__admin endpoint in WireMock.Net works fine. Can you double check how the **issuer** looks on your side?
Author
Owner

@AchoArnold commented on GitHub (May 2, 2025):

@StefH I don't think the problem is the issuer but the issue is with Audience in v1 the aud is api://UUID but in v2 the aud is UUID

So this can be fixed at the config level i.e

var settings = new WireMockServerSettings
{
    AdminAzureADAudience = "api//UUID",
};

instead of

var settings = new WireMockServerSettings
{
    AdminAzureADAudience = "UUID",
};

A few suggestions,

  1. Perhaps you can add some logging on the wire mock server so that when the AAD validation fails. You log exactly the error you get from the TokenValidator.
  2. Did you consider using the Microsoft library for validating the token instead of doing it yourself.

Thanks.

@AchoArnold commented on GitHub (May 2, 2025): @StefH I don't think the problem is the issuer but the issue is with `Audience` in `v1` the `aud` is `api://UUID` but in v2 the `aud` is `UUID` So this can be fixed at the config level i.e ```c# var settings = new WireMockServerSettings { AdminAzureADAudience = "api//UUID", }; ``` instead of ```c# var settings = new WireMockServerSettings { AdminAzureADAudience = "UUID", }; ``` A few suggestions, 1. Perhaps you can add some logging on the wire mock server so that when the AAD validation fails. You log exactly the error you get from the TokenValidator. 2. Did you consider using the Microsoft library for validating the token instead of doing it yourself. Thanks.
Author
Owner

@StefH commented on GitHub (May 2, 2025):

1‍⃣
For C# code I do:

server.SetAzureADAuthentication("TENANT", "api://UUID");

And I do a post to URL: https://login.microsoftonline.com/<TENANT>/oauth2/token

With values:
Image

And result is:

{
    "token_type": "Bearer",
    "expires_in": "3599",
    "ext_expires_in": "3599",
    "expires_on": "1746197909",
    "not_before": "1746194009",
    "resource": "api://UUID",
    "access_token": "ey..."
}

When I use this access_token in a GET request to http://localhost:9091/__admin/settings (when running in debug step).
All is fine.

(A NuGet version should be WireMock.1.8.1-ci-19970)

2‍⃣
Yes I checked that link, however, I thought I needed to include another NuGet for that logic, so I just build the logic myself.

3‍⃣
I think when you get the logging from wiremock, the exception should be present.

@StefH commented on GitHub (May 2, 2025): 1‍⃣ For C# code I do: ``` c# server.SetAzureADAuthentication("TENANT", "api://UUID"); ``` And I do a post to URL: `https://login.microsoftonline.com/<TENANT>/oauth2/token` With values: ![Image](https://github.com/user-attachments/assets/feaa179d-b824-4571-ac45-d2cc2f81af11) And result is: ``` json { "token_type": "Bearer", "expires_in": "3599", "ext_expires_in": "3599", "expires_on": "1746197909", "not_before": "1746194009", "resource": "api://UUID", "access_token": "ey..." } ``` When I use this access_token in a GET request to `http://localhost:9091/__admin/settings` (when running in debug step). All is fine. (A NuGet version should be WireMock.1.8.1-ci-19970) 2‍⃣ Yes I checked that link, however, I thought I needed to include another NuGet for that logic, so I just build the logic myself. 3‍⃣ I think when you get the logging from wiremock, the exception should be present.
Author
Owner

@AchoArnold commented on GitHub (May 5, 2025):

I think when you get the logging from wiremock, the exception should be present.

@StefH Any tips on how we can do this? I use the WiremockConsoleLogger and when there is an error this is the only log line which I see cfc13b2449/src/WireMock.Net/Owin/WireMockMiddleware.cs (L141) I don't see the results of the token validator to see if the problem is with the resource, issuer, or some other problem I had to reverse engineer the code to discover that the reason for authentication failures was because of issuer mismatch.

Perhaps we can log matchResult.Exception

@AchoArnold commented on GitHub (May 5, 2025): > I think when you get the logging from wiremock, the exception should be present. @StefH Any tips on how we can do this? I use the `WiremockConsoleLogger` and when there is an error this is the only log line which I see https://github.com/WireMock-Net/WireMock.Net/blob/cfc13b2449b275d074b967c0abf5ebcf3be6ef08/src/WireMock.Net/Owin/WireMockMiddleware.cs#L141 I don't see the results of the token validator to see if the problem is with the resource, issuer, or some other problem I had to reverse engineer the code to discover that the reason for authentication failures was because of issuer mismatch. Perhaps we can log `matchResult.Exception`
Author
Owner

@StefH commented on GitHub (May 6, 2025):

@AchoArnold
Exception has been added to the logging.

Please test preview : 1.8.2-ci-19997

@StefH commented on GitHub (May 6, 2025): @AchoArnold Exception has been added to the logging. Please test preview : `1.8.2-ci-19997`
Author
Owner

@AchoArnold commented on GitHub (May 6, 2025):

Thanks @StefH Now the error is available in the logs.

Image
@AchoArnold commented on GitHub (May 6, 2025): Thanks @StefH Now the error is available in the logs. <img width="1277" alt="Image" src="https://github.com/user-attachments/assets/d84d2f5f-4417-4a0f-b436-df7bdc509f13" />
Author
Owner

@StefH commented on GitHub (May 6, 2025):

@AchoArnold

You need to use the correct audience in the setting I guess?

var settings = new WireMockServerSettings
{
    AdminAzureADAudience = "api://UUID",
};
@StefH commented on GitHub (May 6, 2025): @AchoArnold You need to use the correct audience in the setting I guess? ``` c# var settings = new WireMockServerSettings { AdminAzureADAudience = "api://UUID", }; ```
Author
Owner

@AchoArnold commented on GitHub (May 6, 2025):

@StefH I was configured the audience wrongly on purpose to test that the Exception logging now works. And I can confirm that it works.

Thanks.

@AchoArnold commented on GitHub (May 6, 2025): @StefH I was configured the `audience` wrongly on purpose to test that the `Exception` logging now works. And I can confirm that it works. Thanks.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/WireMock.Net-wiremock#688