Years ago, in 2011, I've blogged about an old DOS game I've ported to XNA. This weekend I've found the code and spent a while to make sure it works.
The new version targets .NET8/Monogame and is available on my Github.
Years ago, in 2011, I've blogged about an old DOS game I've ported to XNA. This weekend I've found the code and spent a while to make sure it works.
The new version targets .NET8/Monogame and is available on my Github.
Bumped the OldMusicBox.ePUAP.Client to 1.25.3.
For some unknown reason that doesn't seem to be announced, the TpSiging5::GetSignedDocument changed the format of natural person's data (givenname, surname, personal identification number).
From the very beginning of the old TpSigning service, the document contained user signatures in the PodpisZP node. Later on, when TpSigning5 was introduced, they changed the signature to EPSignature node (different node with different model).
And guess what, starting from somewhere between 07-03-2025 and 10-03-2025, the new service (TpSigning5) returns the natural person's data in the old format (back to PodpisZP). Be aware of this and update your code accordingly.
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:
And what? And there it is, https://github.com/wzychla/AspJwtBearerDemo. A complete demo. Here's an overview:
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.
public class TaskExperiments { public async Task<string> DelayWithEcho( int ms, string echo, string id ) { await Task.Delay( ms ); Console.WriteLine( echo ); return id; } }
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}" );
Task2 after 3000ms - actual task Finished one of the two : i2 Task1 after 5000ms - time constraint
... var t2 = e.DelayWithEcho( 7000, "Task2 po 7000ms - actual task", "i2" ); ...
Task1 after 5000ms - time constraint Finished one of the two : i1 Task2 after 7000ms - actual task
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 "?"; }
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 "?"; }