Friday, August 26, 2022

TypeScript puzzle No.2 (easy)

Consider following code

class Foo {
  bar() {
    return 1
  }
}

const foo = new Foo
foo.bar()

Now consider that for some reason we want to include a type assertion, rather than just foo we want foo as Foo.

class Foo {
  bar() {
    return 1
  }
}

const foo = new Foo
(foo as Foo).bar()

Surprisingly, this one doesn't compile, the error points to the last line and says

Block-scoped variable 'foo' used before its declaration

As always, your goal is to explain the unexpected behavior here.

Monday, August 22, 2022

TypeScript's Conditional Type Operator example

We all like good examples, examples which, when seen, make us grasp new ideas easier. I believe the notion of Conditional Types in TypeScript is often demonstrated in a way that makes you wonder what's the real purpose of it. Let's just have yet another example that could be useful here.

Let's start with a function type, it could be a type of a custom function or a built in function.

// a custom function
function foo( s: string, n: number, b: boolean | symbol) : void {

}
type FooType = typeof foo;
// FooType = (s: string, n: number, b: boolean | symbol) => void

// the built in function, Array.prototype.slice
type ArraySliceType = typeof Array['prototype']['slice'];
// ArraySliceType = (start?: number | undefined, end?: number | undefined) => any[]
The two function types have a different number of parameters but with the TypeScript's type system, we can write an auxiliary type that just picks a type of specific parameter.

This is where the actual example begins. The initial part of the example is based on a code presented in Programming TypeScript by B. Cherny. Let's start with a type that picks a parameter that comes, like, second on the list of parameters.

type SecondArg<F> = F extends (a: any, b: infer B, ...c: any ) => any ? B : never;
Let's comment that. The SecondArg type is a generic type that expects a single generic parameter, F. This single generic parameter type is checked to be a function (the F extends ... part) and depending on the test, the conditional operator either returns an inferred type of the second argument (infer B) or fails (never).

Let's test this

type SecondArgString = SecondArg<string>;
// never
type SecondArgFoo = SecondArg<FooType>;
// number
type SecondArgArraySlice = SecondArg<ArraySliceType>;
// number | undefined
This works great and to me - it's enough to demonstrate how useful the ? : conditional operator is. Using this technique we can pick a type of a specific argument of specific function type, which, as far as I know, is not possible in languages like, say, C# or Java. In C#, if there's a function and the second argument of the function is, say, int and you have to declare a variable of this type, you have to know this type in advance to even start writing code.

But, this example can easily be pushed forward, why just pick a specific, second parameter? Why not write a generic type that picks the argument we want, first, second, third? Let's go beyond the original example from the book.

The question is, can numbers (parameter indexes) can be arguments of generic types?

The answer is, sure, it's TypeScript, literals can be types. Remember the part of the TypeScript tutorial when you learn that a string 'foo' can also be a type that is just a subtype of string? Well, 5 can be a type that's just a subtype of number.

The only small technical issue is that our new generic type needs two generic arguments and we need constraints on both of them which the ? : operator doesn't support directly (or I don't know how to do it :).

The first approach involves nesting the ? : operator so that the nested part introduces the constraint on the second argument.

type NThArg<F, N> = 
    F extends (...c: infer C) => any 
    ? (N extends number ? C[N] : never ) 
    : never;  
  
Please take a while to compare this new type to the previous one. As you can see, the parameter list is expressed in a more concise way, we don't need to introduce specific parameters, instead we hide them all under the spread (...) operator. But the other generic parameter, N is constrained to be a number (the N extends a number part) so that it can be used to just index the signature type (the C[N]) part.

If nesting the conditional type operator bothers you as much as it bothers me, there's another version

type NThArg2<F, N extends number> = 
    F extends (...c: infer C) => any 
    ? C[N]
    : never;  
  
This time there's no nesting of the conditional operator. Instead, the constraint on N is expressed directly in type signature which works here as the constraint on N doesn't really invole anything fancy (no need for conditionals, infer, etc.)

Both operators work, please take your time to play around

type FooTypeNth10  = NThArg<FooType, 0>
// string
type FooTypeNth11  = NThArg<FooType, 1>
// number
type NThArgSlice = NThArg<ArraySliceType, 1>;
// number | undefined

type FooTypeNth2  = NThArg2<FooType, 1>
// number
type NThArgSlice2 = NThArg2<ArraySliceType, 1>;
// number | undefined  
  

And just by the way, did you know that TypeScript's type system is Turing Complete (please also take a look here and here and here)? This basically means that we could write actual programs using types only and results would be computed by the compiler during compilation. Please check the linked article to find few possible ways this could be used (and no, it's not quite useful, you wouldn't like to write code that runs on your type system).

Happy coding.

Thursday, August 11, 2022

Node.js + Express + TypeScript (ts-node reloaded)

This is a short follow up to one of my previous posts, the Node.js + Express + TypeScript where a tiny tutorial on writing node.js node with TypeScript was discussed.
This time I'd like to introduce the ts-node which basically wraps the TypeScript execution in a single pipeline so that there's no need to precompile the TypeScript to JavaScript.
One of ways to use the ts-node is to install it globally and run the TypeScript code with
ts-node app.ts
Note only that the tsconfig.json has to be present, just to configure TypeScript options.
However, to also be able to debug the TypeScript from the Visual Studio Code and still use the ts-node to execute the code, install the ts-node locally and create a .vscode/launch.json section
{
    "name": "ts-node",
    "type": "node",
    "request": "launch",
    "args": ["${workspaceFolder}\\app.ts"],
    "runtimeArgs": ["-r", "ts-node/register"],
    "cwd": "${workspaceRoot}",
}
A tiny code to test this
// app.ts
import * as http from 'http';
import express, { Express, Request, Response } from 'express';

var app: Express = express();

app.get('/', (req: Request, res: Response) => {
    res.write('hello world');
    res.end();
});

var server = http.createServer(app)

server.listen(3000);

console.log( 'started' );

Thursday, June 9, 2022

Cannot navigate to the symbol under the carret after updating VS 2022

After VS 2022 has been updated, suddenly some projects refuse to provide navigation between tokens, you can't just click on anything and "Go to Definition..."
There are couple of suggested workarounds, including restarting VS, deleting *.user files or going to Options/Debugging/Symbols and invoking Empty symbol cache
A working solution has been found here and it consists in updating the Microsoft.Net.Compilers package referenced in affected projects to a newer version.
This however, potentially causes another issue, this time on a build server, if you happen to rely on msbuild. This time, the MSB4184 is thrown
error MSB4184: The expression ""App_Start\RouteConfig.cs".GetPathsOfAllDirectoriesAbove()" 
cannot be evaluated. Method 'System.String.GetPathsOfAllDirectoriesAbove' not found
Turns out, this also has been observed. The proposed solution is to uninstall the Microsoft.Net.Compilers, this breaks token resolution is VS, though.
What seems to work for us is to have
  • Microsoft.Net.Compilers 2.10
  • Microsoft.CodeDom.Providers.DotNetCompilerPlatform 3.60

Thursday, May 5, 2022

Visual Studio 2022, Environment/Startup

What happens when you just open the Visual Studio? It displays a solution selector where you pick up one of latests solutions. This is the default behavior. This can be changed, if you go to Options and then go to Environment/Startup page, you can change this to just show an empty environment.

People got used to it, also, it's mentioned in the documentation

The only problem is that the Environment/Startup is not there in Visual Studio 2022.

Instead, it's been moved to Environment/General, unfortunately, it's at the very bottom of the page and can easily be missed because you need to actually scroll down the page to see it

Monday, April 25, 2022

Node.js + Express + TypeScript

The Node.js + Express + Javascript is a great platform and this short tutorial shows how to add TypeScript to the picture.

Start with installing TypeScript compiler globally

npm install typescript -g
so that whenever you invoke tsc from the command line, the compiler follows your tsconfig.json and does its job. Then add the tsconfig.json
{
    "compilerOptions": {
        "lib": [
            "es2021", "DOM"
        ],
        "module": "commonjs",
        "target": "es2021",
        "strict": true,
        "esModuleInterop": true,
        "sourceMap": true                
    }
}
Note that we specifically ask the module subsystem to translate TypeScript's export/imports into node.js commonjs modules. We also turn on the esModuleInterop flag for better node.js compatibility.

Now install some typings for node and express (other typings should possibly also be installed, depending on what packages you are going to use.

npm i --save-dev @types/node
npm i --save-dev @types/express

Create the app.ts file, which will be the starting point of the app and add .vscode/launch.json with

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "pwa-node",
            "request": "launch",
            "name": "Launch Program",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\app.ts",
            "outFiles": [
                "${workspaceFolder}/**/*.js"
            ]
        }
    ]
}
Note how the entry point (program) is set up here. Because the Typescript compiler will generate source maps, you would be able to run&debug the Typescript code.

Writing Typescript is easy now. Create an example express app

import * as http from 'http';
import express from 'express';

var app = express();

app.get('/', (req, res) => {
    res.write('hello world');
    res.end();
});

var server = http.createServer(app)

server.listen(3000);

console.log( 'started' );
Note how express is imported and how a library module (http) is imported - in both cases, there's the full intellisense support in VS Code as well as the support from the Typescript compiler.

To compile the code just invoke

tsc
once. To run the compiler in the watch mode:
tsc --watch

To show how TS modules work, create a simple module, m.ts with

class Example {
    DoWork( s: string ) : string {
        return s + ' from Example';
    }
}

export default Example;
and import it in the app.ts
import * as http from 'http';
import express from 'express';
import Example from './m';

var app = express();

app.get('/', (req, res) => {

    let e = new Example();

    res.write(e.DoWork('hello world'));
    res.end();
});

var server = http.createServer(app)

server.listen(3000);

console.log( 'started' );
Note how m.ts is compiled to m.js and also note how m.js is imported in the app.js (compiled from app.ts).

Wednesday, April 13, 2022

Simplest SAML1.1 (WS-Federation) Federated Authentication in .NET 6

Years ago I've blogged on how to do the WS-Fed using code only in .NET. The approach had multiple advantages over the static WSFederationAuthenticationModule (the WSFam) configured in web.config. The most important feature here is that you have the most control over what happens: how the redirect to the Identity Provider is created and how the SAML token response is consumed.
This is extremely useful in complex scenarios, e.g. in multitenant apps where tenants are configured individually or multiple identity providers are allowed or even when the federated authentication is optional at all.
.NET 6 (.NET Core) has its own way of handling WS-Federation, namely, it registers its own middleware (AddWsFederation) that can be controlled with few configuration options. However, I still feel I miss some features, like triggering the flow conditionally in a multitenant app. What I need is not yet another middleware but rather, a slightly more low-level approach following the basic principle: not use module/middleware but rather have a custom code in the logon controller/action:
if ( !IsWsFedResponse() )
{
   RedirectToIdP();
}
else
{
  var token = GetTokenFromResponse();
  var principal = ValidateToken();
  
  AuthenticateUsingPrincipal(principal);
}
Please refer to the blog entry I've linked at the top to see the approach presented there follows this.
Can we have a similar flow in .NET Core? Ignore the middleware but rather have a total control over the WS-Fed?
The answer is: sure. Since the docs are sparse and there are not-that-much examples, a decompiler is handy to just see how things are done internally so we don't rewrite anything that's already there.
So, just setup your cookie authentication and point your unauthenticated users to /Account/Logon. And then:
public class AccountController : Controller
{
	private KeyValuePair Convert(KeyValuePair pair)
	{
		return new KeyValuePair(pair.Key, pair.Value);
	}

	public async Task Logon()
	{
		WsFederationMessage wsFederationMessage = null;

		if (HttpMethods.IsPost(base.Request.Method))
		{
			var parameters = (await this.Request.ReadFormAsync()).Select(Convert);
			wsFederationMessage = new WsFederationMessage(parameters);
		}

		if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage)
		{
			var signInMessage           = new WsFederationMessage();
            
			signInMessage.IssuerAddress = "https://issuer.address/tenant/fs/ls";
			signInMessage.Wtrealm       = "https://realm.address/tenant/account/logon";

			var redirectUri = signInMessage.CreateSignInUrl();

			return Redirect(redirectUri);
		}
		else
		{
			string token = wsFederationMessage.GetToken();

			SecurityToken validatedToken;

			var tokenHandler = 
				new SamlSecurityTokenHandler()
				{
					MaximumTokenSizeInBytes = Int32.MaxValue                        
				};
			var tokenValidationParameters = new TokenValidationParameters()
			{
				AudienceValidator = (audiences, token, parameters) =>
				{
					return true;
				},
				IssuerValidator = (issuer, token, parameters) =>
				{
					return issuer;
				},
				IssuerSigningKeyValidator = (key, token, parameters) =>
				{
					if ( key is X509SecurityKey )
					{
						X509SecurityKey x509Key = (X509SecurityKey)key;

						// validate cert thumb
						// return x509Key.Certificate.Thumbprint == "alamakota";
						return true;
					}
					else
					{
						return false;
					}
				},   
				IssuerSigningKeyResolver = (token, securityToken, kid, parameters) =>
				{
					var samlToken   = (SamlSecurityToken)securityToken;
					var signature   = samlToken.Assertion.Signature;
                    
					// rewrite this to handle edge cases!
					var certificate = signature.KeyInfo.X509Data.FirstOrDefault().Certificates.FirstOrDefault();

					var x509Certificate2 = new X509Certificate2(System.Convert.FromBase64String(certificate));

					return new List()
					{
						new X509SecurityKey(x509Certificate2)
					};
				}
			};

			// if this succeeds - we have the principal
			var validatedPrincipal = tokenHandler.ValidateToken(token, tokenValidationParameters, out validatedToken);

			// we strip all claims except the user name to have a complete control over how the cookie is issued
			List claims = new List
				{
					new Claim(ClaimTypes.Name, validatedPrincipal.Identity.Name)
				};

			// create identity
			var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
			ClaimsPrincipal principal = new ClaimsPrincipal(identity);

			await this.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

			return Redirect("/");
		}
	}
}
Note how the IssuerSigningKeyValidator delegate replaces the IssuerNameRegistry from the .NET Framework example (linked blog entry). Also note, that other configuration options of the TokenValidationParameters can be changed freely here on a per-request basis.

Friday, March 25, 2022

Guess6 published on GitHub

Decided to polish the code a little bit and published it on GitHub. Such small yet complete apps are a great sandboxes to test/show various programming techniques. Expect more features soon then (a redux store is the next step).

Tuesday, February 22, 2022

Guess6 in React - source

The game itself is published here. Here's the full source code. It's surprisingly consise, feel free to study it on your own.
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';

import App from '@/app';

/**
 * Entry point
 */
class Program {
    
    Main() {

        var app = (
                <App />
        );

        ReactDOM.render(app, document.getElementById('guess6'));
    }
}

new Program().Main();

// App.tsx
import React, { useEffect, useState } from 'react';
import Dictionary from './dictionary';
import Keyboard from './keyboard';
import WordMatch from './wordMatch';

const App = () => {

  const EXPECTEDLENGTH = 6;

  const [words, setWords] = useState<Array<string>>([]);
  const [secretWord, setSecretWord] = useState<string>('');

  function getRandomWord(Dictionary: string[]): string {
    const randomIndex = Math.floor(Math.random() * (Dictionary.length));
    return Dictionary[randomIndex].toUpperCase();
  }

  function restartGame() {
      setWords([]);
      setSecretWord(getRandomWord(Dictionary));
  }
  
  function onWordTyped( newWord: string ) {
    setWords( words => words.concat([newWord]) );   
  }

  function giveUp() {
    if ( secretWord.length == EXPECTEDLENGTH ) {
      setWords( words => words.concat( secretWord ) );
    }
  }

  useEffect( () => {
    restartGame();
  }, []);

  return <>  
    <div>
      <button className='flatButton' onClick={() => restartGame()}>NEW GAME</button>
      <button className='flatButton' onClick={() => giveUp()}>GIVE UP</button>
    </div>
    <h1>Enter {EXPECTEDLENGTH}-letter word</h1>
    <Keyboard dictionary={Dictionary} expectedLength={EXPECTEDLENGTH} onWordTyped={onWordTyped} />
    {words.map( (word, index) => <ordMatch candidate={word} secret={secretWord} key={index} />)}
  </>
};

export default App;

// Keyboard.tsx
import React, { KeyboardEvent, useEffect, useState } from 'react';

const Keyboard = ({dictionary, expectedLength, onWordTyped} : 
    {dictionary: string[] | undefined, expectedLength: number, onWordTyped: (word: string) => void}) => {

    const [message, setMessage] = useState<string>('');
    const [word, setWord]       = useState<string>('');

    const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const QWERTY  = "QWERTYUIOP";
    const ASDF    = "ASDFGHJKL";
    const ZXCV    = "ZXCVBNM";

    function appendLetter(letter: string) {
        if ( word.length < expectedLength ) {
            setWord( w => w + letter );
            setMessage('');
        }
    }

    function clearWord() {
        setWord('');
        setMessage('');
    }

    function tryAcceptWord() {
        if ( word.length != expectedLength ) {
            setMessage(`Expected ${expectedLength} characters, got ${word.length} so far`);
            return;
        } 
        
        if ( dictionary !== undefined && dictionary.map( w => w.toUpperCase() ).indexOf( word ) < 0 ) {
            setMessage(`Word ${word} not in dictionary`);
            return;
        }
      
        onWordTyped(word);
        setWord('');
    }

    return <div>
        <div>
            <input className='keyboardInput' value={word} readOnly />
        </div>
        <div className='firstRow'>
            {QWERTY.split('').map( (letter) => <button className='letterButton flatButton'  
              onClick={() => appendLetter(letter)} key={letter}>{letter}</button> )}
        </div>
        <div className='secondRow'>
            {ASDF.split('').map( (letter) => <button className='letterButton flatButton'  
              onClick={() => appendLetter(letter)} key={letter}>{letter}</button> )}
            <button className='flatButton' onClick={() => clearWord()}>DEL</button>
        </div>
        <div className='thirdRow'>
            {ZXCV.split('').map( (letter) => <button className='letterButton flatButton'  
              onClick={() => appendLetter(letter)} key={letter}>{letter}</button> )}
            <button className='flatButton' onClick={() => tryAcceptWord()}>ENTER</button>
        </div>
        <div>{message}</div>
    </div>;
}

export default Keyboard;

// WordMatch.tsx
import React from 'react';

const WordMatch = ({candidate, secret} : {candidate: string, secret: string}) => {

    if ( candidate.length != secret.length ) {
        throw new Error('candidate and secret word must have same length');
    }

    type  letterState = 'USED' | undefined;
    const letterStates: Array<letterState> = Array<letterState>(candidate.length);

    function getLetterClass( index: number ) : string {        
        // match
        if ( secret[index] == candidate[index] ) {
            letterStates[index] = 'USED';
            return 'letter letterMatch';
        }
        // possible
        for ( let i=0; i<secret.length; i++ ) {
            if ( secret[i] == candidate[index] &&
                 letterStates[i] == undefined 
                ) {
                letterStates[i] = 'USED';
                return 'letter letterPossible';
            }
        }
        // none
        return 'letter letterWrong';
    }

    return <div>
        {candidate.split('').map( (letter, index) => 
            <span className={getLetterClass(index)} key={index}>{letter}</span>
        )}
    </div>;
}

export default WordMatch;

// Dictionary.ts
// https://eslforums.com/6-letter-words/

const Dictionary: Array<string> = [
"abacus",
// the rest of the dictionary here
"zoning"
];

export default Dictionary;

Guess6 in React

Saturday, January 8, 2022

WinForms Dependency Injection in .NET6

This entry is motivated by the discussion in one of StackOverflow's questions, the How to use dependency injection in WinForms. A correct answer by Reza Aghaei shows how the container can be created and used, however, one of drawbacks of Reza's answer is that the container is used directly, which makes this approach fall under the Service Locator antipattern.
I've decided then to create a follow up and show one of possible correct solutions. This solution is based on the Local Factory pattern.
We'll start with a form factory
public interface IFormFactory
{
	Form1 CreateForm1();
	Form2 CreateForm2();
}

public class FormFactory : IFormFactory
{
	static IFormFactory _provider;

	public static void SetProvider( IFormFactory provider )
	{
		_provider = provider;
	}

	public Form1 CreateForm1()
	{
		return _provider.CreateForm1();
	}

	public Form2 CreateForm2()
	{
		return _provider.CreateForm2();
	}
}
From now on, this factory is the primary client's interface to creating forms. The client code is no longer supposed to just call
var form1 = new Form1();
No, it's forbidden. Instead, the client should always call
var form1 = new FormFactory().CreateForm1();
(and similarily for other forms).
Note that while the factory is implemented, it doesn't do anything on its own! Instead it delegates the creation to a somehow mysterious provider which has to be injected into the factory. The idea behind this is that the provider will be injected, once, in the Composition Root which is a place in the code, close to the startup, and very high in the application stack so that all dependencies can be resolved there. So, the form factory doesn't need to know what provider will be ultimately injected into it.
This approach has a significant advantage - depending on actual requirements, different providers can be injected, for example you could have a DI-based provider (we'll write it in a moment) for an actual application and a stub provider for unit tests.
Anyway, let's have a form with a dependency:
public partial class Form1 : Form
{
	private IHelloWorldService _service;

	public Form1(IHelloWorldService service)
	{
		InitializeComponent();

		this._service = service;
	}
}
This form depends on a service and the service will be provided by the constructor. If the Form1 needs to create another form, Form2, it does it it a way we already discussed:
var form2 = new FormFactory().CreateForm2();
Things become more complicated, though, when a form needs not only dependant services but also, just some free parameters (strings, ints etc.). Normally, you'd have a constructor
public Form2( string something, int somethingElse ) ...
but now you need something more like
public Form2( ISomeService service1, IAnotherService service2, string something, int somethingElse ) ...
This is something we should really take a look into. Look once again, a real-life form possibly needs
  1. some parameters that are resolved by the container
  2. other parameters that should be provided by the form creator (not known by the container!).
How do we handle that?
To have a complete example, let's then modify the form factory
public interface IFormFactory
{
	Form1 CreateForm1();
	Form2 CreateForm2(string something);
}

public class FormFactory : IFormFactory
{
	static IFormFactory _provider;

	public static void SetProvider( IFormFactory provider )
	{
		_provider = provider;
	}

	public Form1 CreateForm1()
	{
		return _provider.CreateForm1();
	}

	public Form2 CreateForm2(string something)
	{
		return _provider.CreateForm2(something);
	}
}
And let's see how forms are defined
public partial class Form1 : Form
{
	private IHelloWorldService _service;

	public Form1(IHelloWorldService service)
	{
		InitializeComponent();

		this._service = service;
	}

	private void button1_Click( object sender, EventArgs e )
	{
		var form2 = new FormFactory().CreateForm2("foo");
		form2.Show();
	}
}

public partial class Form2 : Form
{
	private IHelloWorldService _service;
	private string _something;

	public Form2(IHelloWorldService service, string something)
	{
		InitializeComponent();

		this._service = service;
		this._something = something;

		this.Text = something;
	}
}
Can you see a pattern here?
  1. whenever a form (e.g. Form1) needs only dependand services, it's creation method in the FormFactory is empty (dependencies will be resolved by the container).
  2. whenever a form (e.g. Form2) needs dependand services and other free parameters, it's creation method in the FormFactory contains a list of arguments corresponding to these free parameters (service dependencies will be resolved by the container)
Now finally to the Composition Root. Let's start with the service
public interface IHelloWorldService
{
	string DoWork();
}

public class HelloWorldServiceImpl : IHelloWorldService
{
	public string DoWork()
	{
		return "hello world service::do work";
	}
}
Note that while the interface is supposed to be somewhere down in the stack (to be recognized by everyone), the implementation is free to be provided anywhere (forms don't need reference to the implementation!). Then, follows the starting code where the form factory is finally provided and the container is set up
internal static class Program
{
	[STAThread]
	static void Main()
	{
		var formFactory = CompositionRoot();

		ApplicationConfiguration.Initialize();
		Application.Run(formFactory.CreateForm1());
	}

	static IHostBuilder CreateHostBuilder()
	{
		return Host.CreateDefaultBuilder()
			.ConfigureServices((context, services) => {
				services.AddTransient<IHelloWorldService, HelloWorldServiceImpl>();
				services.AddTransient<Form1>();
				services.AddTransient<Func<string,Form2>>(
					container =>
						something =>
						{
							var helloWorldService = 
                                container.GetRequiredService<IHelloWorldService>();
							return new Form2(helloWorldService, something);
						});
			});
	}

	static IFormFactory CompositionRoot()
	{
		// host
		var hostBuilder = CreateHostBuilder();
		var host = hostBuilder.Build();

		// container
		var serviceProvider = host.Services;

		// form factory
		var formFactory = new FormFactoryImpl(serviceProvider);
		FormFactory.SetProvider(formFactory);

		return formFactory;
	}
}

public class FormFactoryImpl : IFormFactory
{
	private IServiceProvider _serviceProvider;

	public FormFactoryImpl(IServiceProvider serviceProvider)
	{
		this._serviceProvider = serviceProvider;
	}

	public Form1 CreateForm1()
	{
		return _serviceProvider.GetRequiredService<Form1>();
	}

	public Form2 CreateForm2(string something)
	{
		var _form2Factory = _serviceProvider.GetRequiredService<Func<string, Form2>>();
		return _form2Factory( something );
	}
}
First note how the container is created with Host.CreateDefaultBuilder, an easy task. Then note how services are registered and how forms are registered among other services.
This is straightforward for forms that don't have any dependencies, it's just
services.AddTransient<Form1>();
However, if a form needs both services and free parameters, it's registered as ... form creation function, a Func of any free parameters that returns actual form. Take a look at this
services.AddTransient<Func<string,Form2>>(
	container =>
		something =>
		{
			var helloWorldService = container.GetRequiredService<IHelloWorldService>();
			return new Form2(helloWorldService, something);
		});
That's clever. We register a form factory function using one of registration mechanisms that itself uses a factory function (yes, a factory that uses another factory, a Factception. Feel free to take a short break if you feel lost here). Our registered function, the Func<string, Form2> has a single parameter, the something (that corresponds to the free parameter of the form constructor) but its other dependencies are resolved ... by the container (which is what we wanted).
This is why the actual form factory needs to pay attention of what it resolves. A simple form is resolved as follows
return _serviceProvider.GetRequiredService<Form1>();
where the other is resolved in two steps. We first resolve the factory function and then use the creation's method parameter to feed it to the function:
var _form2Factory = _serviceProvider.GetRequiredService<Func<string, Form2>>();
return _form2Factory( something );
And, that's it. Whenever a form is created, is either
new FormFactory().CreateForm1();
for "simple" forms (with service dependencies only) or just
new FormFactory().CreateForm2("foo");
for forms that need both service dependncies and other free parameters.
Happy coding. A simple excercise for you is to provide a different form factory, a factory that doesn't use a container but composes dependencies directly, for example for unit tests.

.NET 6 WebAPI with JWT bearer token authentication minimal example

The console client:
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

var plainTextSecurityKey = "This is secret";

var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(plainTextSecurityKey));
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256Signature);

var claimsIdentity = 
    new ClaimsIdentity(new List<Claim>()
    {
        new Claim(JwtRegisteredClaimNames.Name, "username1")
    });

var securityTokenDescriptor = new SecurityTokenDescriptor()
{
    Subject = claimsIdentity,
    SigningCredentials = signingCredentials
};

var tokenHandler          = new JwtSecurityTokenHandler();
var plainToken            = tokenHandler.CreateToken(securityTokenDescriptor);
var signedAndEncodedToken = tokenHandler.WriteToken(plainToken);

Console.WriteLine(plainToken);
Console.WriteLine(signedAndEncodedToken);

HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {signedAndEncodedToken}");

var res = await client.GetAsync("http://localhost:5242/api/Example");
if (res.StatusCode == System.Net.HttpStatusCode.OK)
{
    var result = await res.Content.ReadAsStringAsync();
    Console.WriteLine(result);
}
else
{
    Console.WriteLine($"Status: {res.StatusCode}");
}

Console.ReadLine();
The server:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddAuthentication()
    .AddJwtBearer(cfg =>
    {
        var plainTextSecurityKey = "This is secret";

        var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(plainTextSecurityKey));
        var signingCredentials = new SigningCredentials(signingKey,SecurityAlgorithms.HmacSha256Signature);

        cfg.RequireHttpsMetadata = false;
        cfg.TokenValidationParameters = new TokenValidationParameters()
        {            
            ValidateAudience = false,
            ValidateIssuer   = false,
            IssuerSigningKey = signingKey            
        };

        cfg.Events = new JwtBearerEvents()
        {
            OnAuthenticationFailed = async context =>
            {
                var ex = context.Exception;
                Console.WriteLine(ex.Message);
            }
        };
    });

builder.Services.AddControllers();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("api/[controller]")]
public class ExampleController : ControllerBase
{

	public ExampleController()
	{
	}

	[HttpGet]
	public IActionResult Get()
	{
		return this.Ok("webapi service. authenticated as " + this.User.Identity.Name);
	}
}
I love .NET6 way of providing concise examples.