Friday, December 24, 2021

ADFS - Windows authentication

One of my clients is using Microsoft ADFS as their Security Token Service. Through ADFS people can login either by using their e-id(Belgian passport) or through their internal domain account (using Windows authentication).

After upgrading our ADFS server, we noticed that people were asked for their credentials and that their windows credentials were not passed automatically.

This is of course quite annoying.

We took a look at the ADFS settings and noticed that ‘mozilla/5.0’ was missing from the list of user agents:

PS C:\Users\bawu> (Get-AdfsProperties).Wiasupporteduseragents

MSAuthHost/1.0/In-Domain

MSIE 6.0

MSIE 7.0

MSIE 8.0

MSIE 9.0

MSIE 10.0

Trident/7.0

MSIPC

Windows Rights Management Client

MS_WorkFoldersClient

=~Windows\s*NT.*Edge

To fix it we updated the list of supported agents:

Set-ADFSProperties -WIASupportedUserAgents @("MSAuthHost/1.0/In-Domain","MSIE 6.0", "MSIE 7.0", "MSIE 8.0", "MSIE 9.0", "MSIE 10.0", "Trident/7.0", "MSIPC", "Windows Rights Management Client", "MS_WorkFoldersClient", "=~Windows\s*NT.*Edge", "Mozilla/5.0")

That did the trick!

Thursday, December 23, 2021

Elastic APM–Use .NET OpenTelemetry

Elastic has their own Application Performance Monitoring solution as part of their Elastic Observability product.

An important part of the solution are ‘agents’. Agent are responsible for instrumenting your application, collecting all the metrics and sending it to the APM server.

Specifically for .NET the APM agent is released as a serie of NuGet packages.

With the release of OpenTelemetry for .NET I was wondering if we could replace this Elastic APM specific solution with standard OpenTelemetry.

In a first incarnation APM server didn’t support the OpenTelemetry standard and you had to use a seperate collector that converts the OpenTelemetry data to the Elastic APM format:

Since version 7.13 of Elastic APM this is no longer necessary.The OpenTelemetry Collector exporter for Elastic was deprecated and replaced by the native support of the OpenTelemetry Line Protocol in Elastic Observability (OTLP).

Now the only thing you need to do is to add some specific attributes and configure the OltpExporter:

Wednesday, December 22, 2021

Visual Studio 2022–New breakpoint types

Of course it is always better to not write any bugs, but I know I make mistakes. So sooner or later I need to debug my code to find what’s going wrong.

In that case breakpoints are an important aid to halt your application at the right location. Visual Studio 2022 introduces 2 new breakpoint types; temporary and dependent breakpoints:

The Temporary breakpoint is used to set a breakpoint that will only break once. When debugging, the Visual Studio debugger only pauses the running application once for this breakpoint and then removes it immediately after it has been hit.

To set a temporary breakpoint, hover over the breakpoint symbol, choose the Settings icon, and then select Remove breakpoint once hit in the Breakpoint Settings window:

You can also use the right-click context menu to set the temporary breakpoint by selecting Insert Temporary Breakpoint from the context menu:

The Dependent breakpoint is used to set a breakpoint that will only break when another breakpoint is hit. This can make debugging code in common paths such as game loop or a utility API much easier because a breakpoint in those functions can be configured to enable only if the function is invoked from a specific part of your application.

To set a dependent breakpoint, hover over the breakpoint symbol, choose the Settings icon, and then select Only enable when the following breakpoint is hit in the Breakpoint Settings window.

In the dropdown, select the prerequisite breakpoint you want your current breakpoint to be dependent on.

You can also use the right-click context menu to set the dependent breakpoint by selecting Insert Dependent Breakpoint from the context menu:

Tuesday, December 21, 2021

Error after upgrading to Microsoft.Data.SqlClient 4

After upgrading to Microsoft.Data.SqlClient 4, I immediatelly started to get connection failures.

Let’s have a look at the exact error message:

A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.)

The reason of this error is that with the release of version 4.0, a breaking change was introduced to improve security:

The default value of the `Encrypt` connection option changed from `false` to `true`. With the increased emphasis on secure-by-default, the growing use of cloud databases, and the need to ensure connections are secure, Microsoft decided it was time for this backwards-compatibility-breaking change.

You can of course get back to the previous situation by explicitly setting the ‘encrypt’ option to ‘false’.

"server=exampleserver;database=ExampleDB;integrated security=True;Encrypt=False"

But a better solution is to enable encrypted connections to the database.

Monday, December 20, 2021

Kubernetes Job Containers - (Forbidden): jobs.batch "example-migration" is forbidden

For our database migrations we are usingKubernetes Jobs and init containers as discussed here.

However when we tried to deploy the job container, it failed with the following error:

Error from server (Forbidden): jobs.batch "example-migration" is forbidden: User "system:serviceaccount:example-ns:default" cannot get resource "jobs" in API group "batch" in the namespace "example-ns": Azure does not have opinion for this user.

To read and list jobs, the deployment is using the default service account in the “example-ns” namespace. This default service account does not have the necessary api rights in the kubernetes cluster.

To fix it we created a new service account, role and role binding:

After doing that, we had to update our deployment to use this service account:

Friday, December 17, 2021

Become a master at Git and Open Source

Interested in contributing to an Open Source project? But your experience with Git is rather limited, than this learning course is one for you!

Sign up for this free course here.

You’ll get regular mails covering following topics:

  • Learning Git by exploring open-source repositories
  • Exploring a Git repository using Visual Studio
  • Contributing to an open source project
  • Add your existing code to Git and GitHub
  • Next steps

Thursday, December 16, 2021

Swagger UI - Add required header

One of our REST API’s always requires an ‘X-API-Key’ header. To simplify testing, I wanted to have the option to specify the header in the Swagger UI.

Let’s see how we can get this done through SwashBuckle.

First I need to create a custom IOperationFilter that will add the header:

Now we need to update our Swagger configuration to use this header:

If we now run our application and browse to the Swagger UI, we should see the extra header parameter:

Wednesday, December 15, 2021

ASP.NET Core 6–Http Logging

With the release of ASP.NET Core 6, a new Http Logging middleware became available. This middleware transmit requests as log messages through the Microsoft.Extensions.Logging framework.

To enable the middleware, add the following code to your Program.cs:

This will add an instance of the Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware class.

If you now run your application, you will notice no difference and nothing is logged. This is because the HTTP Logging messages are handled as informational messages and these are not enabled by default.

To adjust the log level, you can add the following line to the Logging > LogLevel configuration in the appsettings.json file:

You can further configure the logging through the AddHttpLogging method:

Tuesday, December 14, 2021

Visual Studio 2022–Inline hints

Bring enough .NET developers together and sooner or later someone will start the discussion if we should use the ‘var’ keyword or not.

You have people in the ‘pro’ camp who like that they have to type less and don’t worry too much about the specific type. You have people in the ‘contra’ camp who prefer to have explicit typing.

In Visual Studio 2022, you can get the best of both worlds by enabling ‘inline hints hints’. Inlay hints can display parameter name hints for literals, function calls and more.

  • To enable this feature, go to Tools > Options > Text Editor > C# or Basic > Advanced.
  • Check the ‘Display inline parameter name hints’ checkbox
  • Check the ‘Display inline type hints’ checkbox

Now we can see that both our arguments and the var types are annotated:

Monday, December 13, 2021

ASP.NET Core - Who stole my cookie?

I stumbled over a strange issue I had in ASP.NET Core. A cookie created in ASP.NET MVC didn’t show up in my requests in ASP.NET Core.

This cookie was used to share some user preferences between 2 subsites in the same domain. The original cookie was created without setting a SameSite value but was also not marked as secure or httponly.

I first updated the cookie generation logic to set both the Secure and HttpOnly value to true. This is not strictly required but as I’m only reading the cookie in the backend this is already a step in the right direction.

Let’s now focus on the SameSite value. SameSite is an IETF draft standard designed to provide some protection against cross-site request forgery (CSRF) attacks. Cookies without SameSite header are treated as SameSite=Lax by default. If you know that we need SameSite=None to allow this cookie for cross-site usage, that should certainly be our next step.

Remark: Cookies that assert SameSite=None must also be marked as Secure.

Although I could now finally see using the Web Developer tools that the cookie certainly is available as part of my request, it didn’t show up yet inside my ASP.NET Core application when checking the Cookies property on my Http request.

There was one other problem with my cookie and this was related to the data in the cookie.Let’s have a look:

Do you notice the ‘\’ in the value? This is causing the problem; the original code didn’t URL encode the cookie values. Although the old ASP.NET MVC application didn’t seem to bother, ASP.NET Core certainly does and didn’t want to load and parse my cookie.

I had to update the original code to start url encoding the value:

Friday, December 10, 2021

Simple made easy

One of the inspirations for the name of my blog comes from this presentation by Rick Hickey.  In case you’ve never watched this video, time to take it off your bucket list!

Watch it on InfoQ or on Youtube.

Thursday, December 9, 2021

Visual Studio 2022 - Test execution with Hot reload

Building your code before you can run your tests can be a big part of the time needed to run your tests. The build time inside Visual Studio can vary depending on the kind of changes made to the code. For larger solutions, builds can be the most expensive part of the test run.

Visual Studio 2022 includes an experimental(!) feature, that allow you to use hot reload to speed up the test execution by skipping builds for supported scenarios.

To start using this feature, you first need to enable it by choosing Test > Options > "(Experimental) Enable Hot Reloaded Test Runs for C# and VB test projects targeting .NET 6 and higher":

Now when you execute a test in Visual Studio, Test Explorer will automatically use test execution with hot reload when possible. If a hot reload is not possible, it will fall back to the regular behavior of building and running tests. The nice thing is that this is all happening behind the scenes and you, as a developer, should not do anything different.

Remark: This feature only works when building your projects for the DEBUG configuration

More information: https://docs.microsoft.com/en-us/visualstudio/test/test-execution-with-hot-reload?amp;view=vs-2022

Wednesday, December 8, 2021

ASP.NET Core - Could not load type 'Microsoft.AspNetCore.Mvc.MvcJsonOptions'

After upgrading some packages in an ASP.NET Core application, I got the following error message when I tried to run the application:

System.TypeLoadException: Could not load type 'Microsoft.AspNetCore.Mvc.MvcJsonOptions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.1.20.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

   at System.Signature.GetSignature(Void* pCorSig, Int32 cCorSig, RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo methodHandle, RuntimeType declaringType)

   at System.Reflection.RuntimeConstructorInfo.get_Signature()

   at System.Reflection.RuntimeConstructorInfo.GetParametersNoCopy()

   at System.Reflection.RuntimeConstructorInfo.GetParameters()

   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)

   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_0.<UseMiddleware>b__0(RequestDelegate next)

   at Microsoft.AspNetCore.Builder.ApplicationBuilder.Build()

   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)

This issue was caused because I was using a newer Microsoft.AspNetCore.Mvc package together with an older Swashbuckle package. To solve the problem I had to update Swagger to version 5:

<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
That’s it!

Tuesday, December 7, 2021

Blazor–Using Basic authentication

For an internal application I’m building I needed to use Basic authentication.

Remark: In case you forgot, Basic Authentication transmits credentials like user ID/password as a base64 encoded string in the Authorization header. This is of course not the most secure way as any man-in-the-middle can capture and read this header data.

If you look around on the Internet, a typical example on how to this in .NET looks like this:

We create a HttpClientHandler, set the Credentials and pass it to our HttpClient as a parameter.

Of course it is even better to not create an HttpClient instance yourself but instead use the HttpClientFactory to create a Named or Typed HttpClient.

But if you try to use the code above in a Blazor application, you’ll end up with the following runtime error:

System.PlatformNotSupportedException: Property Credentials is not supported.

As the HttpClient implementation for Blazor stays as close as possible to the fetch api the browser, you’ll need to take a different approach and set the authorization header directly:

Monday, December 6, 2021

.NET 6 - The ArgumentNullException helper class

.NET 6 introduces a new helper class that uses the new [CallerArgumentExpression] attribute and the [DoesNotReturn] attribute; the ArgumentNullException helper class.

This class gives you an easy-to-use helper class that throws an ArgumentNullException for null values.

Thanks to the [CallerArgumentExpression] attribute this helper method gives you better error messages as it can capture the expressions passed to a method.

This is the implementation of this helper class:

Before C# 10, you probably would have used the nameof keyword and implemented this helper class like this:

Friday, December 3, 2021

.NET Conf 2021 - Sessions, slides and demos are online

In case you missed .NET Conf 2021, no worries, all sessions are recorded and available on the .NET YouTube channel or the new Microsoft Docs events hub. With over 80 sessions, you will know what to do during the Christmas Holidays. Slidedecks and demos can be found on the .NET Conf 2021 GitHub page. Have fun!

 

Thursday, December 2, 2021

.NET Core–The case of the disappearing authorization header

While building an internal (Blazor) application, I stumbled over some CORS issues. As this was an internal application, I decided to be lazy and just disable CORS on my request:

In the example above I’m using the  SetBrowserRequestMode() to disable the CORS preflight check.

Afther doing that the CORS issue was gone, unfortunately my application still didn’t work because now I got a 401 response back?!

I was quite confident that the provided username/password combination was correct. So what is going on?

I monitored my request using the browser developer tools and I noticed that the authorization header was missing:

What was going on?

The MDN documentation brought me the answer when I had a look at the request mode documentation specifically for ‘no-cors’:

no-cors — Prevents the method from being anything other than HEAD, GET or POST, and the headers from being anything other than simple headers. If any ServiceWorkers intercept these requests, they may not add or override any headers except for those that are simple headers. In addition, JavaScript may not access any properties of the resulting Response. This ensures that ServiceWorkers do not affect the semantics of the Web and prevents security and privacy issues arising from leaking data across domains.

So this explains why the authorization header is removed before sending the request.

Another reason why being lazy as a developer is not always a good idea. So I went back to my application and enabled CORS on the API I was calling…

Wednesday, December 1, 2021

Keep your project dependencies up to date with dotnet outdated

From the documentation:

When using Visual Studio, it is easy to find out whether newer versions of the NuGet packages used by your project are available, by using the NuGet Package Manager. However, the .NET Core command-line tools do not provide a built-in way for you to report on outdated NuGet packages.

dotnet-outdated is a .NET Core Global tool that allows you to quickly report on any outdated NuGet packages in your .NET Core and .NET Standard projects.

This is a great way to keep your applications up-to-date and can easily be integrated as part of your DevOps processes.

Install dotnet-outdated as a global tool:

dotnet tool install --global dotnet-outdated-tool

Now you can invoke it from your project or solution folder:

dotnet outdated

This is how the output looks like for one of my projects:

The colors make it very clear. Here is the related legend:

You can automatically upgrade packages by passing the ‘-u’ parameter:

dotnet outdated -u