Sunday, November 24, 2024

Yet another ASP.NET Core JWT tutorial?

Why would you need yet another ASP.NET Core JWT tutorial?

Well, becuse most of them are unnecessarily complicated. Some involve unnecessary APIs like the Identity API. Some try to introduce an OAuth2/OpenID Connect stack. There are ocassional cases where false assumptions are used to justify the need of JWT tokens.

An example of the latter is an argument that "cookies are bad since they require data stored in server's session container", given here. Of course, cookies do not require that anything is stored in the container session, a cookie can just contain a username and this is how authentication cookies are often used. Anyone using .NET cookie authentication for years would be surprised to hear that instead of just a username, an ASP.NET authentication cookie would contain a session ID and the server would only lookup user data at the server's session container.

It's not that unnecessarily elaborated demo or a demo that fails at some subtle reasons is bad. It's just harder to follow.

Why would you prefer JWT from cookies then?

There's only one valid argument: JWT tokens work crossdomain, cookies don't. In a setup where an authentication token is issued at domain A and is required at domain B, cookies don't work. JWT tokens on the other hand, are usually put in custom headers in web browser calls and thus can be easily sent anywhere.

What would I personally expect from a JWT tutorial?

Well, I'd expect following:

  • a web app that authenticates users somehow, could even be an endpoint that accepts a username/password pair but an easy approach would be a cookie-based authentication with a usual login page. An advantage of the latter is that I expect a good demo to show how to have both in a single app: a cookie-based authentication on some endpoints and JWT based authentication on other endpoints
  • I want to see an endpoint that is set up to issue JWT tokens then. The endpoint could assume the user is authenticated (e.g. with an already issued cookie) and would just create JWT containing the username
  • I want to see a data endpoint that is authenticated only with a JWT token, a cookie should not be accepted. The endpoint could be exposed on the very same app but I'd easily think of any other app (a different domain) exposing it
  • I don't want any extra frameworks, APIs, anything, just bare minimum

And what? And there it is, https://github.com/wzychla/AspJwtBearerDemo. A complete demo. Here's an overview:

  • the demo contains both MVC and WebAPI controllers
  • there are two MVC controllers, the Home controller is only accessible by users authenticated with a cookie, the Account controller is used as a default redirect for unauthenticated calls and uses SignInAsync to issue the cookie
  • as long as the cookie is issued, the Home::Index view is rendered
  • there are two active endpoints
  • first, the Token::Acquire is used to get the JWT token. Note how easy it is: the used should be authenticated somehow (= cookie in my demo) and the code just creates a plain JWT with just the username
  • then, the Data::Get WebAPI endpoint is secured with the JWT token. Note that by configuring the JWT stack to recognize the JWT name claim as username I can access this.User.Identity.Name in a WebAPI code and still have the username. The this.User.Identity.Name is then used in the very same way in both MVC cookie secured controllers and WebAPI JWT secured controllers. The key point of this JWT config is
    cfg.TokenValidationParameters = new TokenValidationParameters()
    {
       ValidateAudience = false,
       ValidateIssuer   = false,
       IssuerSigningKey = signingKey,
       NameClaimType    = "name"     // map JWT's name claim to NET's IPrincipal::IIdentity::Name
    }; 
    

What is actually really nice in ASP.NET Core is the idea of authentication scheme - we define multiple authentication schemes (in this demo, there are two: cookie based and JWT based) and then each time Authorize is used on an action (both MVC and WebAPI) we specify which authentication scheme should is accepted. Thus, having some endpoints secured with a cookie and other secured with JWT tokens is easy and clean.

That's it. Clone the Github demo, follow the flow, take a look how both schemes are configured in AddCookies and AddJwtBearer. Enjoy.

Friday, October 25, 2024

Using Task.WhenAny to time bound a task

Just for a future reference - suppose a task should not take longer than ... but there's still a need to finish it. It's just the caller that cannot wait longer
One of use cases involves a REST service where you provide the server part. There's a client that should get an answer in max X seconds, regardless of whether the actual task finishes or not. You want a timer that runs together with the task and in case any of the two finishes first, you want to know which one was it.
Let's stage actors first:
public class TaskExperiments
{
	public async Task<string> DelayWithEcho( int ms, string echo, string id )
	{
		await Task.Delay( ms );
		Console.WriteLine( echo );

		return id;
	}
}
This one above is supposed to be a factory of tasks. A task is given a time and an id so that when it finishes, I know the id.
Now, the runner:
var e = new TaskExperiments();

var t1 = e.DelayWithEcho( 5000, "Task1 after 5000ms - time constraint", "i1" );
var t2 = e.DelayWithEcho( 3000, "Task2 after 3000ms - actual task",     "i2" ); 

var t  = await Task.WhenAny( t1, t2 );
var id = await t;

Console.WriteLine( $"Finished one of the two : {id}" );
As you can see, Task.WhenAny is used and then the result task is awaited once again to get the id.
Running this gives:
Task2 after 3000ms - actual task
Finished one of the two : i2
Task1 after 5000ms - time constraint
Now change the second to last longer:
...
var t2 = e.DelayWithEcho( 7000, "Task2 po 7000ms - actual task",     "i2" ); 
...
and the result is
Task1 after 5000ms - time constraint
Finished one of the two : i1
Task2 after 7000ms - actual task

Monday, April 15, 2024

Rust/WebAssembly vs Javascript performance ... reloaded

Last year I've blogged about a tiny contest where Rust/WASM and Javascript were used to implement a simple Mandelbrot animation. I've polished the code, put it in a github repo, the rust-vs-js. I've also added a third contestant, the gpu.js accelerated version, which of course beats the heck out of the two (Javascript and Rust) since it's heavily parallelized.
Anyway, jump to the repository and enjoy the code.

Wednesday, April 3, 2024

Take just a single visible character?

Fairly simple requirement - get a first letter of profile description and present it together with a link. You get the idea, if I have two profiles, Foo and Bar, I want two links with F and B respectively.
The first version of the code (not even mentioned here) was just something like: if string has at least one character, take uppercase of the first character.
This seemingly simple approach completely ignores Emojis which are handled in C# strings as two consecutive chars. The second version of the code was then:
public static string ToShortDescription( this string source )
{
	var description = source?.Trim();

	if ( !string.IsNullOrWhiteSpace( description ) && description.Length >= 1 )
	{
		if ( char.IsSurrogatePair( description, 0 ) )
		{
			return description.Substring( 0, 2 ).ToUpper();
		}
		else
		{
			return description.Substring( 0, 1 ).ToUpper();
		}
	}

	return "?";
}
This is better, much better. It's not just the first character of the string, it's the substring that has the length of 2. This simple approach correctly handles many two-char Emojis, like the male mage Emoji, 🧙, encoded as &#x1F9D9;.
Unfortunately, it's just the beginning of the story. It turns out some Emojis are combined from other Emojis. Let's take the mage emoji. Its female version, 🧙‍♀️, is encoded as male mage Emoji followed by additional characters to indicate female version (&#x1F9D9;&#x200D;&#x2640;&#xFE0F;). The special character used to glue together emojis is the Zero-Width-Joiner (ZWJ).
Take a C# string that starts with the female mage emoji. This time it's not the 2 characters that should be taken from it, now it's 5! The two-char Emoji, the ZWJ, and another two-char Emoji!
Let this sink in - in order to have a single visible character on the screen, we need to take 5 first characters of the C# string!
And as you can expect, the above version of code correctly discovers the first surrogate but fails to discover the ZWJ.
There's even a discussion on SO on how to detect this.
My current approach is
public static string ToShortDescription( this string source, bool autoUpper = true )
{
	var description = source?.Trim();

	if ( !string.IsNullOrWhiteSpace( description ) && description.Length >= 1 )
	{
		// należy brać kolejne znaki na następujących zasadach
		// * jeśli zwykły znak - bierze się i koniec
		// * jeśli zjw - bierze się i nie koniec
		// * jeśli surrogatepair bierze się dwa i nie koniec
		char[] sourceChars = source.ToCharArray();
		List<char> destChars = new List<char>();

		var index = 0;
		bool takeAgain;
		bool zjw;

		do
		{
			takeAgain = false;

			// czy jest jeden i jeszcze jeden za nim (dwuznaki)
			if ( index < sourceChars.Length - 1 )
			{
				// surogat
				if ( char.IsSurrogatePair( sourceChars[index], sourceChars[index + 1] ) )
				{
					destChars.AddRange( new[] { sourceChars[index], sourceChars[index + 1] } );

					index    += 2;
					takeAgain = true;
				}
			}

			if ( index < sourceChars.Length - 2 )
			{
				// zjw - skleja dwa emoji
				if ( sourceChars[index] == (char)8205 )
				{
					destChars.AddRange( new[] { sourceChars[index], sourceChars[index + 1], sourceChars[index + 2] } );

					index    += 3;
					takeAgain = true;
				}

			}


		} while ( takeAgain && index < sourceChars.Length );

		// weź jeszcze jeden jeśli jeszcze nie ma nic lub zjw
		if ( !takeAgain && 
			 index <= sourceChars.Length-1 &&
			 destChars.Count == 0 
			)
		{
			destChars.Add( sourceChars[index] );
		}

		string _result = new string( destChars.ToArray() );

		return autoUpper ? _result.ToUpper() : _result;

		/*
		if ( char.IsSurrogatePair( description, 0 ) )
		{
			return description.Substring( 0, 2 ).ToUpper();
		}
		else
		{
			return description.Substring( 0, 1 ).ToUpper();
		}
		*/
	}

	return "?";
}
This passes some important unit tests. Namely, it correctly handles the England Emoji flag emoji, the 🏴󠁧󠁢󠁥󠁮󠁧󠁿 (&#x1F3F4;&#xE0067;&#xE0062;&#xE0065;&#xE006E;&#xE0067;&#xE007F;), which still is a single visible sign but in this extreme case it's the first 14 characters of the C# string! I believe there's still a room for improvement (and possibly other strange cases that I still miss).

Friday, February 23, 2024

Entity framework migrations under heavy load from a server farm

Entity Framework migrations are great. I particularily like the mechanism that prevents any query on the database until all pending migrations are applied. This solves a lot of issues and in most scenarios, you can even rely on the MigrateDatabaseToLatestVersion initializer in production. The initializer is smart enough to guard any instance of the dbcontext within the current process, this works correctly even from ASP.NET.
Well, mostly. Problems start when you have a farm of many ASP.NET servers that connect to the very same database. Each single server runs its own migration. Under heavy load this possibly means that your database is concurrently migrated from multiple servers.
Some time ago we almost had a disaster involving this scenario. A really busy app deployed on multiple servers was updated and, sadly, applying pending migrations was constantly failing. Yep, it was something specific in one of migrations but the result was as follows: one of servers tried to start the migration. Migration involved a heavy query that lasted a couple of seconds. All other servers were migrating too, trying to execute the very same heavy query. After the heavy query there was another lightweight query that was failing on the first server because the heavy query was pending on other servers. And as soon as any other server finished the heavy query, it immediately failed on the lightweight query because yet another server was just executing the heavy query.
There's a solution, though, involving two custom initializers. One just checks if there are pending migrations and throws. This one is configured as the default initializer. Another one actually migrates the database and is only invoked from a controlled environment, like a separate application or a specific controller/action.
Some code:
    public class DefaultDbContextInitializer : 
       IDatabaseInitializer<ExampleMigrationDbContext>
    {
        public void InitializeDatabase( ExampleMigrationDbContext context )
        {
            Configuration cfg = new Configuration(); // migration configuration class
            cfg.TargetDatabase =
               new DbConnectionInfo(
                  context.Database.Connection.ConnectionString,
                  "System.Data.SqlClient" );

            DbMigrator dbMigrator = new DbMigrator( cfg );

            if ( dbMigrator.GetPendingMigrations().Count() > 0 )
            {
                throw new MigrationsException( "pending migrations!" );
            }
        }
    }
    
    public class MigratingDbContextInitializer : 
       IDatabaseInitializer<ExampleMigrationDbContext>
    {
        public void InitializeDatabase( ExampleMigrationDbContext context )
        {
            Configuration cfg = new Configuration(); // migration configuration class
            cfg.TargetDatabase =
               new DbConnectionInfo(
                  context.Database.Connection.ConnectionString,
                  "System.Data.SqlClient" );

            DbMigrator dbMigrator = new DbMigrator( cfg );

            foreach ( string MigrationName in dbMigrator.GetPendingMigrations() )
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();

                dbMigrator.Update( MigrationName );

                watch.Stop();
            }
        }
    }    
The first default initializer is configured globally:
   Database.SetInitializer<ExampleMigrationDbContext>( new DefaultMigrationDbContextInitializer() );
Because of that, any attempt to touch the database that has pending migrations will fail with an exception you can catch and show a message.
But then, somewhere in a controlled environment you call this:
    var context = new ExampleMigrationDbContext();

    var migrator = new MigratingDbContextInitializer();
    migrator.InitializeDatabase( context );
This works. After the database is migrated in the controlled way, the default initializer stops throwing and the app is back to running.

Thursday, February 22, 2024

ASP.NET MVC and Bootstrap error classes

Bootstrap requires specific error classes to be applied to inputs (e.g. is-invalid). MVC on the other hand has its own error classes (e.g. field-validation-error). There are numerous ideas how to combine the two and this is another one.
The idea is to dirty replace MVC error classes using reflection. A starting point would be to change the input's error class:
typeof( HtmlHelper ).InvokeMember(
	nameof( HtmlHelper.ValidationInputCssClassName ),
	System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.SetField,
	null,
	typeof( HtmlHelper ),
	new[] { "is-invalid" } ); 
This one overwrites the const HtmlHelper.ValidationInputCssClassName from its default value (field-validation-error) to Bootstrap's is-invalid. Calling this early causes invalid inputs have Bootstrap class.