4
votes

I am trying to setup availability testing (URL Ping Test) with Azure Application Insights on an endpoint that requires basic authentication. It seems that the standard approach with https://username:[email protected] isn't accepted by Azure (error message says that the URL is malformed and maybe I am missing https/http at the beginning).

Is there any other way to achieve this except of using multi-step web test or Azure Functions, assuming I want to stay in Azure ecosystem? :)

3

3 Answers

2
votes

Passing the basic auth credentials in the URL has been deprecated by RFC 3986 (Here is a snippet from the RFC)

3.2.1. User Information

The userinfo subcomponent may consist of a user name and, optionally, scheme-specific information about how to gain authorization to access the resource. The user information, if present, is followed by a commercial at-sign ("@") that delimits it from the host.

  userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )

Use of the format "user:password" in the userinfo field is deprecated.

The alternate would be to use the Authorization header to pass the credentials. Here is a snippet from Wikipedia (Basic Auth) on how this header is constructed.

The Authorization field is constructed as follows:[6]

  1. The username and password are combined with a single colon. (:)
  2. The resulting string is encoded into an octet sequence.[7]
  3. The resulting string is encoded using a variant of Base64.[8]
  4. The authorization method and a space is then prepended to the encoded string, separated with a space (e.g. "Basic ").

For example, if the browser uses Aladdin as the username and OpenSesame as the password, then the field's value is the base64-encoding of Aladdin:OpenSesame, or QWxhZGRpbjpPcGVuU2VzYW1l. Then the Authorization header will appear as:

Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l

You can create a Web Test file in Visual Studio Enterprise and then upload it in Application insights and use that. Refer this doc: https://docs.microsoft.com/en-us/azure/application-insights/app-insights-monitor-web-app-availability

  1. In Visual Studio Enterprise, You can create a WebTest project.
  2. Right Click on your project name and select Add Request. enter image description here
  3. Now right click on the link and select Add Header. enter image description here
  4. You can add the headers as per your requirement. enter image description here
  5. When you review the .webtest file you will see that the Headers section gets appended under Requests.
<Request>
  <Headers>
    <Header Name="Authorization" Value="Basic QWxhZGRpbjpPcGVuU2VzYW1l" />
  </Headers>
</Request>
1
votes

Ping test should be relatively simple, there are currently several possibilities to prevent such url from anonymous access, like policies or action filters.

Policy-based authorization

Let's create a simple policy that requires a Secret key in the query string.

First, create the requirement with handler e.g.:

public class HasSecretKeyRequirement : IAuthorizationRequirement
{
}

public class HasSecretKeyRequirementHandler : AuthorizationHandler<HasSecretKeyRequirement>
{
   private readonly IHttpContextAccessor _httpContextAccessor;
   private readonly IConfiguration _configuration;

   public HasSecretKeyRequirementHandler(IHttpContextAccessor httpContextAccessor, IConfiguration configuration)
   {
       _httpContextAccessor = httpContextAccessor;
       _configuration = configuration;
   }

   protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasApiKeyRequirement requirement)
   {
       var httpContext = _httpContextAccessor.HttpContext;
            
       if (httpContext == null)
       {
           context.Fail();
           return Task.CompletedTask;
       }
            
       if (httpContext.Request.Query.TryGetValue("SecretKey", out extractedSecretKey))
       {
           var secretKey= _configuration.GetValue<string>("SecretKey");
 
           if (secretKey.Equals(extractedSecretKey))
           {
               context.Succeed(requirement);
               return Task.CompletedTask;
           }
        }
            
        httpContext.Response.ContentType = "text/plain";
        httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    
        context.Fail();
        return Task.CompletedTask;  
    }
}

Then create a new policy with requirement and register requirement handler:

services.AddAuthorization(options =>
{
   options.AddPolicy("HasSecretKey", policy =>
      policy.Requirements.Add(new HasSecretKeyRequirement()));
);

services.AddSingleton<IAuthorizationHandler, HasSecretKeyRequirementHandler>();

Apply policy to your ping endpoint:

[Authorize(Policy = "HasSecretKey")]
[HttpGet("api/ping")]
public IActionResult Ping()
{
   return Ok();
}

Finallddd configure your availability test:

https://yourapi.com/api/ping?SecretKey=yoursecretkey

This is just an example, you can create your own requirement and handling logic. You can reuse these policies in your application, e.g. in the healthcheck endpoint:

endpoints.MapHealthChecks("/health")
    .RequireAuthorization("HasSecretKey");

This form of authorization may be sufficient in such a simple case, but definitely should not be used in more advanced scenarios.

0
votes

For reference: Based on the snippet in the answer of @kaushal above, I was able to extend the Basic URL Ping test :) I use the XML in Terraform but you can also submit this via the REST API directly. In my case I needed to add an arbitrary header

<WebTest Name="appinsights-webtest" Id="ABD48585-0831-40CB-9069-682EA6BB3583" Enabled="True" CssProjectStructure="" CssIteration="" Timeout="30" WorkItemIds="" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" Description="" CredentialUserName="" CredentialPassword="" PreAuthenticate="True" Proxy="default" StopOnError="False" RecordedResultFile="" ResultsLocale="">
  <Items>
    <Request Method="GET" Guid="a5f10126-e4cd-570d-961c-cea43999a200" Version="1.1" Url="https://example.com/health/stamp" ThinkTime="0" Timeout="30" ParseDependentRequests="False" FollowRedirects="False" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8" ExpectedHttpStatusCode="200" ExpectedResponseUrl="" ReportingName="" IgnoreHttpStatusCode="False">
      <Headers>
        <Header Name="X-Azure-FDID" Value="xxxxxxxxxxxx-xxxxxxxx-xxx" />
      </Headers>
  </Request>
  </Items>
</WebTest>

I didn't find this documented anywhere but works just as expected for me.