dotnet Exception Handling

.NET Exceptions – System.Security.Authentication.InvalidCredentialException

Making our way through the detailed .NET Exception Handling series we’ve been creating, today we’ll be looking into the System.Security.Authentication.InvalidCredentialException. The InvalidCredentialException occurs when authentication fails for one of a number of authentication stream classes in the .NET Framework.

In this article we’ll explore the InvalidCredentialException by looking at where it resides in the overall .NET exception hierarchy. Then, we’ll also show a fully-functional code sample that illustrates how an authentication stream might be created between a client and a listener (server), both successfully and failingly, so let’s get to it!

The Technical Rundown

All .NET exceptions are derived classes of the System.Exception base class, or derived from another inherited class therein. The full exception hierarchy of this error is:

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

When Should You Use It?

The whole of the connectivity support provided in the .NET Framework is far too intricate and advanced to even begin to scratch the surface in this tiny tutorial. Therefore, we’ll simply rely on the code sample to illustrate the basics of (one small) example for how a client/server connection might be established to send some data, and how failure to authenticate can potentially lead to a InvalidCredentialException.

In the realm of DevOps, there’s little doubt when it comes to the “chicken-or-the-egg” concerns of the server and the client: The server always comes first. Thus, we’ll continue this trend by going over our server project code first. We accomplish this with a TcpListener instance, which blocks the thread while listening for incoming connections from a TCP client. This occurs within the Listener class:

The CreateListener(IPAddress ipAddress, int port = 11000) method performs the basic creation and execution of the TcpListener, then blocks until a client connects, which we then attempt to authenticate via the AuthenticateClient(TcpClient client) method:

We start by converting the TcpClient stream into a NegotiateStream, which uses the negotiate security policy to determine the best way for the client/server to authenticate with one another. The generated ClientState instance can be passed to the negotiateStream.BeginAuthenticateAsServer(...) method, along with a reference to our callback method, which awaits the client’s authentication request.

Once client authentication is complete, we then use the stream’s BeginRead(...) method to read the byte-message that was sent by the client, before closing the stream and client entirely.

Both the callback methods are fairly simple:

EndAuthenticateCallback(IAsyncResult asyncResult) basically performs the same process we did before to begin authentication, but in reverse. After converting the IAsyncResult parameter to a NegotiateStream once again, we attempt to end the authentication process via EndAuthenticateAsServer(...), then outputting the authentication result. We also make sure to invoke Waiter.Set() on the client, which frees up the current thread for other execution.

The EndReadCallback(IAsyncResult asyncResult) method does much the same, except it finalizes reading of the byte data sent by the client.

Lastly for the server, the ClientState class is just the basic object, with a few important properties, that we’re passing along as our asynchronous object when attempting to authenticate the client:

Now, let’s move on to the client code, which is contained in the Client class:

The Connect(...) method performs most of the logic, which is explained by the comments so we won’t go through it step by step. In essence, the client creates a TcpClient instance and attempts to connect to the specified host and port. Once connected, authentication is attempted by invoking the NegotiateStream.BeginAuthenticateAsClient(...) method (the opposite of the Server version we used in the server code). If authentication succeeds, it then attempts to send a simple message to the server, in the form of encoded bytes, before closing the stream.

The EndAuthenticateCallback(IAsyncResult asyncResult) and EndWriteCallback(IAsyncResult asyncResult) methods perform the opposing actions that we saw on the server side, this time invoking EndAuthenticateAsClient(...) and EndWrite(...), respectively:

Now, to test everything out we need to execute both our projects simultaneously, starting with the Listener class. Once it is up and running, we can then test out some client connections, as seen in the code below:

Nothing too fancy going on here. We’re simply creating new Client instances, the first with default values, and the second by using credential authentication with a username and password. Both our client and server are running separately, but, if all goes as expected, they’ll connect to one another and the various Logging.Log(...) outputs in the code will show some results.

Here is the output produced from the first new Client(); call, on the client side:

And here’s the output from the server:

Cool! Everything seems to be working as expected. As you can see from the client output, the default ImpersonationLevel when we don’t provide any credentials is Identification. Since both the client and server are running on the localhost (127.0.0.1:11000), there’s no trouble authenticating, so the client was able to send its message and the server received it!

Now, let’s see what the output is from the second Client("username", "password") call, starting with the client:

And the server output:

Lo and behold, a InvalidCredentialException is thrown our way, indicating on both the client and server side that the logon attempt using "username" and "password" as our credentials has failed.

To get the most out of your own applications and to fully manage any and all .NET Exceptions, check out the Airbrake .NET Bug Handler, offering real-time alerts and instantaneous insight into what went wrong with your .NET code, along with built-in support for a variety of popular development integrations including: JIRA, GitHub, Bitbucket, and much more.