static void TestApi_ExpiredTokens()
        {
            Console.WriteLine(nameof(TestApi_ExpiredTokens));
            foreach (var badToken in BadTokens)
            {
                try {
                    var nullId = GoogleUserId.TryParseIdToken(badToken.Token, badToken.AppIds);
                    Debug.Assert(nullId == null, "GoogleUserId.TryValidateIdToken should have returned a null token");
                }
                catch (ArgumentNullException) { }
                catch (ArgumentException) { }
                catch (Exception) { Debug.Fail("Google.TryValidateIdToken should never throw anything but ArgumentNullException/ArgumentException"); }

                try {
                    var thrownId = GoogleUserId.FromIdToken(badToken.Token, badToken.AppIds);
                    Debug.Fail("GoogleUserId.FromIdToken should have thrown for badToken");
                }
                catch (WebException) { }                   // TODO: Inconclusive
                catch (Exception e) { Debug.Assert(badToken.ExpectedExceptionType.IsAssignableFrom(e.GetType()), "Google.ValidateIdToken should have thrown a SecurityTokenException for badToken"); }
            }
        }
        static void TestApi_ViaTestPage()
        {
            var exe       = @"I:\home\projects\new\MicroServer\StaticMicroServer\bin\Release\StaticMicroServer.exe";
            var testPage  = Path.GetFullPath(@"..\..\..\TestPage\");
            var args      = TestWebsitePrefix + " " + testPage + " --timeout=10s";
            var launchUrl = TestWebsitePrefix + "?auto_close=1&client_id=" + GoogleAppId;

            string auth;

            var psi = new ProcessStartInfo(exe, args)
            {
                CreateNoWindow   = true,
                WindowStyle      = ProcessWindowStyle.Hidden,
                WorkingDirectory = Environment.CurrentDirectory,
            };

            using (var server = Process.Start(psi)) {
                if (server.WaitForExit(100))
                {
                    Debug.Fail("Server failed already");
                }

                var l = new HttpListener()
                {
                    Prefixes = { TestWebsitePrefix + "api/" }
                };
                l.Start();
                Process.Start(launchUrl);
                var c = l.GetContext();

                try {
                    auth = c.Request.Headers["Authorization"];
                    if ((c.Request.HttpMethod == "POST") && c.Request.Url.AbsolutePath.EndsWith("/api/0/auth"))
                    {
                        c.Response.StatusCode        = 200;
                        c.Response.StatusDescription = "Recieved response";
                    }
                    else
                    {
                        c.Response.StatusCode = 500;
                    }
                } finally {
                    c.Response.Close();
                    l.Close();
                }
            }

            var m = reAuthorizationHeader.Match(auth ?? "");

            Debug.Assert(m.Success);
            var goodIdToken = m.Groups["jwt"].Value;

            try {
                var tryGetId = GoogleUserId.TryParseIdToken(goodIdToken, new[] { GoogleAppId });
                Debug.Assert(tryGetId != null);

                var getId = GoogleUserId.FromIdToken(goodIdToken, new[] { GoogleAppId });
                Debug.Assert(getId != null);
            }
            catch (WebException) { }             // TODO: Inconclusive
            catch (Exception) when(!Debugger.IsAttached)
            {
                Debug.Fail("Exception trying to handle known good tokens");
            }
        }