< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Hosting.KestrunHostAuthnExtensions
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Hosting/KestrunHostAuthnExtensions.cs
Tag: Kestrun/Kestrun@2d87023b37eb91155071c91dd3d6a2eeb3004705
Line coverage
79%
Covered lines: 254
Uncovered lines: 66
Coverable lines: 320
Total lines: 765
Line coverage: 79.3%
Branch coverage
75%
Covered branches: 85
Total branches: 112
Branch coverage: 75.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 09/12/2025 - 16:20:13 Line coverage: 78.5% (249/317) Branch coverage: 76.3% (84/110) Total lines: 731 Tag: Kestrun/Kestrun@bd014be0a15f3c9298922d2ff67068869adda2a009/15/2025 - 19:16:35 Line coverage: 79% (256/324) Branch coverage: 75.8% (85/112) Total lines: 760 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc909/16/2025 - 04:01:29 Line coverage: 79.3% (254/320) Branch coverage: 75.8% (85/112) Total lines: 763 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb10/13/2025 - 16:52:37 Line coverage: 79.3% (254/320) Branch coverage: 75.8% (85/112) Total lines: 765 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e 09/12/2025 - 16:20:13 Line coverage: 78.5% (249/317) Branch coverage: 76.3% (84/110) Total lines: 731 Tag: Kestrun/Kestrun@bd014be0a15f3c9298922d2ff67068869adda2a009/15/2025 - 19:16:35 Line coverage: 79% (256/324) Branch coverage: 75.8% (85/112) Total lines: 760 Tag: Kestrun/Kestrun@bfb58693b9baaed61644ace5b29e014d9ffacbc909/16/2025 - 04:01:29 Line coverage: 79.3% (254/320) Branch coverage: 75.8% (85/112) Total lines: 763 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb10/13/2025 - 16:52:37 Line coverage: 79.3% (254/320) Branch coverage: 75.8% (85/112) Total lines: 765 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e

Metrics

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Hosting/KestrunHostAuthnExtensions.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Authentication;
 2using Microsoft.AspNetCore.Authentication.Cookies;
 3using Microsoft.AspNetCore.Authentication.JwtBearer;
 4using Microsoft.AspNetCore.Authentication.OpenIdConnect;
 5using Microsoft.IdentityModel.Tokens;
 6using Microsoft.AspNetCore.Authorization;
 7using Microsoft.Extensions.Options;
 8using System.Text.RegularExpressions;
 9using Kestrun.Authentication;
 10using Serilog.Events;
 11using Kestrun.Scripting;
 12using Microsoft.AspNetCore.Authentication.Negotiate;
 13using Kestrun.Claims;
 14using Serilog;
 15
 16
 17namespace Kestrun.Hosting;
 18
 19/// <summary>
 20/// Provides extension methods for adding authentication schemes to the Kestrun host.
 21/// </summary>
 22public static class KestrunHostAuthnExtensions
 23{
 24    /// <summary>
 25    /// Adds Basic Authentication to the Kestrun host.
 26    /// <para>Use this for simple username/password authentication.</para>
 27    /// </summary>
 28    /// <param name="host">The Kestrun host instance.</param>
 29    /// <param name="scheme">The authentication scheme name (e.g. "Basic").</param>
 30    /// <param name="configure">Optional configuration for BasicAuthenticationOptions.</param>
 31    /// <returns>returns the KestrunHost instance.</returns>
 32    public static KestrunHost AddBasicAuthentication(
 33    this KestrunHost host,
 34    string scheme = "Basic",
 35    Action<BasicAuthenticationOptions>? configure = null
 36    )
 37    {
 38        // register in host for introspection
 839        _ = host.RegisteredAuthentications.Register(scheme, "Basic", configure);
 840        var h = host.AddAuthentication(
 841           defaultScheme: scheme,
 842           buildSchemes: ab =>
 843           {
 844               // ← TOptions == BasicAuthenticationOptions
 845               //    THandler == BasicAuthHandler
 846               _ = ab.AddScheme<BasicAuthenticationOptions, BasicAuthHandler>(
 847                   authenticationScheme: scheme,
 848                   displayName: "Basic Authentication",
 849                    configureOptions: opts =>
 850                   {
 851                       // let caller mutate everything first
 652                       configure?.Invoke(opts);
 653                       ConfigureBasicAuthValidators(host, opts);
 654                       ConfigureBasicIssueClaims(host, opts);
 1455                   });
 856           }
 857       );
 58        //  register the post-configurer **after** the scheme so it can
 59        //    read BasicAuthenticationOptions for <scheme>
 860        return h.AddService(services =>
 861        {
 862            _ = services.AddSingleton<IPostConfigureOptions<AuthorizationOptions>>(
 1163                sp => new ClaimPolicyPostConfigurer(
 1164                          scheme,
 1165                          sp.GetRequiredService<
 1166                              IOptionsMonitor<BasicAuthenticationOptions>>()));
 1667        });
 68    }
 69    /// <summary>
 70    /// Adds Basic Authentication to the Kestrun host using the provided options object.
 71    /// </summary>
 72    /// <param name="host">The Kestrun host instance.</param>
 73    /// <param name="scheme">The authentication scheme name (e.g. "Basic").</param>
 74    /// <param name="configure">The BasicAuthenticationOptions object to configure the authentication.</param>
 75    /// <returns>The configured KestrunHost instance.</returns>
 76    public static KestrunHost AddBasicAuthentication(
 77        this KestrunHost host,
 78        string scheme,
 79        BasicAuthenticationOptions configure
 80        )
 81    {
 182        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 83        {
 184            host.Logger.Debug("Adding Basic Authentication with scheme: {Scheme}", scheme);
 85        }
 86        // Ensure the scheme is not null
 187        ArgumentNullException.ThrowIfNull(host);
 188        ArgumentNullException.ThrowIfNull(scheme);
 189        ArgumentNullException.ThrowIfNull(configure);
 190        return host.AddBasicAuthentication(
 191            scheme: scheme,
 192            configure: opts =>
 193            {
 194                // Copy properties from the provided configure object
 295                opts.HeaderName = configure.HeaderName;
 296                opts.Base64Encoded = configure.Base64Encoded;
 297                if (configure.SeparatorRegex is not null)
 198                {
 299                    opts.SeparatorRegex = new Regex(configure.SeparatorRegex.ToString(), configure.SeparatorRegex.Option
 1100                }
 1101
 2102                opts.Realm = configure.Realm;
 2103                opts.RequireHttps = configure.RequireHttps;
 2104                opts.SuppressWwwAuthenticate = configure.SuppressWwwAuthenticate;
 1105                // Logger configuration
 2106                opts.Logger = configure.Logger == Log.ForContext<BasicAuthenticationOptions>() ?
 2107                            host.Logger.ForContext<BasicAuthenticationOptions>() : configure.Logger;
 1108
 1109                // Copy properties from the provided configure object
 2110                opts.ValidateCodeSettings = configure.ValidateCodeSettings;
 2111                opts.IssueClaimsCodeSettings = configure.IssueClaimsCodeSettings;
 1112
 1113                // Claims policy configuration
 2114                opts.ClaimPolicyConfig = configure.ClaimPolicyConfig;
 2115            }
 1116        );
 117    }
 118
 119
 120    /// <summary>
 121    /// Adds JWT Bearer authentication to the Kestrun host.
 122    /// <para>Use this for APIs that require token-based authentication.</para>
 123    /// </summary>
 124    /// <param name="host">The Kestrun host instance.</param>
 125    /// <param name="scheme">The authentication scheme name (e.g. "Bearer").</param>
 126    /// <param name="validationParameters">Parameters used to validate JWT tokens.</param>
 127    /// <param name="configureJwt">Optional hook to customize JwtBearerOptions.</param>
 128    /// <param name="claimPolicy">Optional authorization policy configuration.</param>
 129    /// <example>
 130    /// HS512 (HMAC-SHA-512, symmetric)
 131    /// </example>
 132    /// <code>
 133    ///     var hmacKey = new SymmetricSecurityKey(
 134    ///         Encoding.UTF8.GetBytes("32-bytes-or-more-secret……"));
 135    ///     host.AddJwtBearerAuthentication(
 136    ///         scheme:          "Bearer",
 137    ///         issuer:          "KestrunApi",
 138    ///         audience:        "KestrunClients",
 139    ///         validationKey:   hmacKey,
 140    ///         validAlgorithms: new[] { SecurityAlgorithms.HmacSha512 });
 141    /// </code>
 142    /// <example>
 143    /// RS256 (RSA-SHA-256, asymmetric)
 144    /// <para>Requires a PEM-encoded private key file.</para>
 145    /// <code>
 146    ///    using var rsa = RSA.Create();
 147    ///     rsa.ImportFromPem(File.ReadAllText("private-key.pem"));
 148    ///     var rsaKey = new RsaSecurityKey(rsa);
 149    ///
 150    ///     host.AddJwtBearerAuthentication(
 151    ///         scheme:          "Rs256",
 152    ///         issuer:          "KestrunApi",
 153    ///         audience:        "KestrunClients",
 154    ///         validationKey:   rsaKey,
 155    ///         validAlgorithms: new[] { SecurityAlgorithms.RsaSha256 });
 156    /// </code>
 157    /// </example>
 158    /// <example>
 159    /// ES256 (ECDSA-SHA-256, asymmetric)
 160    /// <para>Requires a PEM-encoded private key file.</para>
 161    /// <code>
 162    ///     using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
 163    ///     var esKey = new ECDsaSecurityKey(ecdsa);
 164    ///     host.AddJwtBearerAuthentication(
 165    ///         "Es256", "KestrunApi", "KestrunClients",
 166    ///         esKey, new[] { SecurityAlgorithms.EcdsaSha256 });
 167    /// </code>
 168    /// </example>
 169    /// <returns></returns>
 170    public static KestrunHost AddJwtBearerAuthentication(
 171      this KestrunHost host,
 172      string scheme,
 173      TokenValidationParameters validationParameters,
 174      Action<JwtBearerOptions>? configureJwt = null,
 175      ClaimPolicyConfig? claimPolicy = null)
 176    {
 177        // register in host for introspection
 3178        _ = host.RegisteredAuthentications.Register(scheme, "JwtBearer", configureJwt);
 3179        return host.AddAuthentication(
 3180            defaultScheme: scheme,
 3181            buildSchemes: ab =>
 3182            {
 3183                _ = ab.AddJwtBearer(scheme, opts =>
 3184                {
 0185                    opts.TokenValidationParameters = validationParameters;
 0186                    opts.MapInboundClaims = true;
 0187                    configureJwt?.Invoke(opts);
 3188                });
 3189            },
 3190            configureAuthz: claimPolicy?.ToAuthzDelegate()
 3191            );
 192    }
 193
 194    /// <summary>
 195    /// Adds Cookie Authentication to the Kestrun host.
 196    /// <para>Use this for browser-based authentication using cookies.</para>
 197    /// </summary>
 198    /// <param name="host">The Kestrun host instance.</param>
 199    /// <param name="scheme">The authentication scheme name (default is CookieAuthenticationDefaults.AuthenticationSchem
 200    /// <param name="configure">Optional configuration for CookieAuthenticationOptions.</param>
 201    /// <param name="claimPolicy">Optional authorization policy configuration.</param>
 202    /// <returns>The configured KestrunHost instance.</returns>
 203    public static KestrunHost AddCookieAuthentication(
 204        this KestrunHost host,
 205        string scheme = CookieAuthenticationDefaults.AuthenticationScheme,
 206        Action<CookieAuthenticationOptions>? configure = null,
 207     ClaimPolicyConfig? claimPolicy = null)
 208    {
 2209        _ = host.RegisteredAuthentications.Register(scheme, "Cookie", configure);
 210
 2211        return host.AddAuthentication(
 2212            defaultScheme: scheme,
 2213            buildSchemes: ab =>
 2214            {
 2215                _ = ab.AddCookie(
 2216                    authenticationScheme: scheme,
 2217                    configureOptions: opts =>
 2218                    {
 2219                        // let caller mutate everything first
 0220                        configure?.Invoke(opts);
 0221                        Log.Debug("Configured Cookie Authentication with LoginPath: {LoginPath}", opts.LoginPath);
 2222                    });
 2223            },
 2224             configureAuthz: claimPolicy?.ToAuthzDelegate()
 2225        );
 226    }
 227
 228
 229    /// <summary>
 230    /// Adds Cookie Authentication to the Kestrun host using the provided options object.
 231    /// </summary>
 232    /// <param name="host">The Kestrun host instance.</param>
 233    /// <param name="scheme">The authentication scheme name (default is CookieAuthenticationDefaults.AuthenticationSchem
 234    /// <param name="configure">The CookieAuthenticationOptions object to configure the authentication.</param>
 235    /// <param name="claimPolicy">Optional authorization policy configuration.</param>
 236    /// <returns>The configured KestrunHost instance.</returns>
 237    public static KestrunHost AddCookieAuthentication(
 238          this KestrunHost host,
 239          string scheme = CookieAuthenticationDefaults.AuthenticationScheme,
 240          CookieAuthenticationOptions? configure = null,
 241       ClaimPolicyConfig? claimPolicy = null)
 242    {
 243        // If no object provided just delegate to action overload without extra config
 0244        return configure is null
 0245            ? host.AddCookieAuthentication(
 0246                scheme: scheme,
 0247                configure: (Action<CookieAuthenticationOptions>?)null,
 0248                claimPolicy: claimPolicy)
 0249            : host.AddCookieAuthentication(
 0250            scheme: scheme,
 0251            configure: opts =>
 0252            {
 0253                // Copy relevant properties from provided options instance to the framework-created one
 0254                CopyCookieAuthenticationOptions(configure, opts);
 0255            },
 0256            claimPolicy: claimPolicy
 0257        );
 258    }
 259
 260
 261    /*
 262        public static KestrunHost AddClientCertificateAuthentication(
 263            this KestrunHost host,
 264            string scheme = CertificateAuthenticationDefaults.AuthenticationScheme,
 265            Action<CertificateAuthenticationOptions>? configure = null,
 266            Action<AuthorizationOptions>? configureAuthz = null)
 267        {
 268            return host.AddAuthentication(
 269                defaultScheme: scheme,
 270                buildSchemes: ab =>
 271                {
 272                    ab.AddCertificate(
 273                        authenticationScheme: scheme,
 274                        configureOptions: configure ?? (opts => { }));
 275                },
 276                configureAuthz: configureAuthz
 277            );
 278        }
 279    */
 280    /// <summary>
 281    /// Adds Windows Authentication to the Kestrun host.
 282    /// <para>
 283    /// The authentication scheme name is <see cref="NegotiateDefaults.AuthenticationScheme"/>.
 284    /// This enables Kerberos and NTLM authentication.
 285    /// </para>
 286    /// </summary>
 287    /// <param name="host">The Kestrun host instance.</param>
 288    /// <returns>The configured KestrunHost instance.</returns>
 289    public static KestrunHost AddWindowsAuthentication(this KestrunHost host)
 290    {
 1291        var options = new AuthenticationSchemeOptions();
 292
 1293        _ = host.RegisteredAuthentications.Register("Windows", "WindowsAuth", options);
 1294        return host.AddAuthentication(
 1295            defaultScheme: NegotiateDefaults.AuthenticationScheme,
 1296            buildSchemes: ab =>
 1297            {
 1298                _ = ab.AddNegotiate();
 1299            }
 1300        );
 301    }
 302
 303    /// <summary>
 304    /// Adds API Key Authentication to the Kestrun host.
 305    /// <para>Use this for endpoints that require an API key for access.</para>
 306    /// </summary>
 307    /// <param name="host">The Kestrun host instance.</param>
 308    /// <param name="scheme">The authentication scheme name (default is "ApiKey").</param>
 309    /// <param name="configure">Optional configuration for ApiKeyAuthenticationOptions.</param>
 310    /// <returns>The configured KestrunHost instance.</returns>
 311    public static KestrunHost AddApiKeyAuthentication(
 312    this KestrunHost host,
 313    string scheme = "ApiKey",
 314    Action<ApiKeyAuthenticationOptions>? configure = null)
 315    {
 316        // register in host for introspection
 6317        _ = host.RegisteredAuthentications.Register(scheme, "ApiKey", configure);
 6318        var h = host.AddAuthentication(
 6319           defaultScheme: scheme,
 6320           buildSchemes: ab =>
 6321           {
 6322               // ← TOptions == ApiKeyAuthenticationOptions
 6323               //    THandler == ApiKeyAuthHandler
 6324               _ = ab.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthHandler>(
 6325                   authenticationScheme: scheme,
 6326                   displayName: "API Key",
 6327                   configureOptions: opts =>
 6328                   {
 6329                       // let caller mutate everything first
 6330                       configure?.Invoke(opts);
 6331                       ConfigureApiKeyValidators(host, opts);
 6332                       ConfigureApiKeyIssueClaims(host, opts);
 12333                   });
 6334           }
 6335       );
 336        //  register the post-configurer **after** the scheme so it can
 337        //    read BasicAuthenticationOptions for <scheme>
 6338        return h.AddService(services =>
 6339        {
 6340            _ = services.AddSingleton<IPostConfigureOptions<AuthorizationOptions>>(
 9341                sp => new ClaimPolicyPostConfigurer(
 9342                          scheme,
 9343                          sp.GetRequiredService<
 9344                              IOptionsMonitor<ApiKeyAuthenticationOptions>>()));
 12345        });
 346    }
 347
 348    /// <summary>
 349    /// Configures the validators for Basic authentication.
 350    /// </summary>
 351    /// <param name="host">The Kestrun host instance.</param>
 352    /// <param name="opts">The options to configure.</param>
 353    private static void ConfigureBasicAuthValidators(KestrunHost host, BasicAuthenticationOptions opts)
 354    {
 6355        var settings = opts.ValidateCodeSettings;
 6356        if (string.IsNullOrWhiteSpace(settings.Code))
 357        {
 3358            return;
 359        }
 360
 3361        switch (settings.Language)
 362        {
 363            case ScriptLanguage.PowerShell:
 1364                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 365                {
 1366                    opts.Logger.Debug("Building PowerShell validator for Basic authentication");
 367                }
 368
 1369                opts.ValidateCredentialsAsync = BasicAuthHandler.BuildPsValidator(host, settings);
 1370                break;
 371            case ScriptLanguage.CSharp:
 1372                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 373                {
 1374                    opts.Logger.Debug("Building C# validator for Basic authentication");
 375                }
 376
 1377                opts.ValidateCredentialsAsync = BasicAuthHandler.BuildCsValidator(host, settings);
 1378                break;
 379            case ScriptLanguage.VBNet:
 1380                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 381                {
 1382                    opts.Logger.Debug("Building VB.NET validator for Basic authentication");
 383                }
 384
 1385                opts.ValidateCredentialsAsync = BasicAuthHandler.BuildVBNetValidator(host, settings);
 1386                break;
 387            default:
 0388                if (opts.Logger.IsEnabled(LogEventLevel.Warning))
 389                {
 0390                    opts.Logger.Warning("No valid language specified for Basic authentication");
 391                }
 392                break;
 393        }
 0394    }
 395
 396    /// <summary>
 397    /// Configures the issue claims for Basic authentication.
 398    /// </summary>
 399    /// <param name="host">The Kestrun host instance.</param>
 400    /// <param name="opts">The options to configure.</param>
 401    /// <exception cref="NotSupportedException">Thrown when the language is not supported.</exception>
 402    private static void ConfigureBasicIssueClaims(KestrunHost host, BasicAuthenticationOptions opts)
 403    {
 6404        var settings = opts.IssueClaimsCodeSettings;
 6405        if (string.IsNullOrWhiteSpace(settings.Code))
 406        {
 3407            return;
 408        }
 409
 3410        switch (settings.Language)
 411        {
 412            case ScriptLanguage.PowerShell:
 1413                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 414                {
 1415                    opts.Logger.Debug("Building PowerShell Issue Claims for API Basic authentication");
 416                }
 417
 1418                opts.IssueClaims = IAuthHandler.BuildPsIssueClaims(host, settings);
 1419                break;
 420            case ScriptLanguage.CSharp:
 1421                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 422                {
 1423                    opts.Logger.Debug("Building C# Issue Claims for API Basic authentication");
 424                }
 425
 1426                opts.IssueClaims = IAuthHandler.BuildCsIssueClaims(host, settings);
 1427                break;
 428            case ScriptLanguage.VBNet:
 1429                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 430                {
 1431                    opts.Logger.Debug("Building VB.NET Issue Claims for API Basic authentication");
 432                }
 433
 1434                opts.IssueClaims = IAuthHandler.BuildVBNetIssueClaims(host, settings);
 1435                break;
 436            default:
 0437                if (opts.Logger.IsEnabled(LogEventLevel.Warning))
 438                {
 0439                    opts.Logger.Warning("{language} is not supported for API Basic authentication", settings.Language);
 440                }
 0441                throw new NotSupportedException("Unsupported language");
 442        }
 443    }
 444
 445    /// <summary>
 446    /// Configures the API Key validators.
 447    /// </summary>
 448    /// <param name="host">The Kestrun host instance.</param>
 449    /// <param name="opts">The options to configure.</param>
 450    /// <exception cref="NotSupportedException">Thrown when the language is not supported.</exception>
 451    private static void ConfigureApiKeyValidators(KestrunHost host, ApiKeyAuthenticationOptions opts)
 452    {
 6453        var settings = opts.ValidateCodeSettings;
 6454        if (string.IsNullOrWhiteSpace(settings.Code))
 455        {
 3456            return;
 457        }
 458
 3459        switch (settings.Language)
 460        {
 461            case ScriptLanguage.PowerShell:
 1462                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 463                {
 1464                    opts.Logger.Debug("Building PowerShell validator for API Key authentication");
 465                }
 466
 1467                opts.ValidateKeyAsync = ApiKeyAuthHandler.BuildPsValidator(host, settings);
 1468                break;
 469            case ScriptLanguage.CSharp:
 1470                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 471                {
 1472                    opts.Logger.Debug("Building C# validator for API Key authentication");
 473                }
 474
 1475                opts.ValidateKeyAsync = ApiKeyAuthHandler.BuildCsValidator(host, settings);
 1476                break;
 477            case ScriptLanguage.VBNet:
 1478                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 479                {
 1480                    opts.Logger.Debug("Building VB.NET validator for API Key authentication");
 481                }
 482
 1483                opts.ValidateKeyAsync = ApiKeyAuthHandler.BuildVBNetValidator(host, settings);
 1484                break;
 485            default:
 0486                if (opts.Logger.IsEnabled(LogEventLevel.Warning))
 487                {
 0488                    opts.Logger.Warning("{language} is not supported for API Basic authentication", settings.Language);
 489                }
 0490                throw new NotSupportedException("Unsupported language");
 491        }
 492    }
 493
 494    /// <summary>
 495    /// Configures the API Key issue claims.
 496    /// </summary>
 497    /// <param name="host">The Kestrun host instance.</param>
 498    /// <param name="opts">The options to configure.</param>
 499    /// <exception cref="NotSupportedException">Thrown when the language is not supported.</exception>
 500    private static void ConfigureApiKeyIssueClaims(KestrunHost host, ApiKeyAuthenticationOptions opts)
 501    {
 6502        var settings = opts.IssueClaimsCodeSettings;
 6503        if (string.IsNullOrWhiteSpace(settings.Code))
 504        {
 3505            return;
 506        }
 507
 3508        switch (settings.Language)
 509        {
 510            case ScriptLanguage.PowerShell:
 1511                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 512                {
 1513                    opts.Logger.Debug("Building PowerShell Issue Claims for API Key authentication");
 514                }
 515
 1516                opts.IssueClaims = IAuthHandler.BuildPsIssueClaims(host, settings);
 1517                break;
 518            case ScriptLanguage.CSharp:
 1519                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 520                {
 1521                    opts.Logger.Debug("Building C# Issue Claims for API Key authentication");
 522                }
 523
 1524                opts.IssueClaims = IAuthHandler.BuildCsIssueClaims(host, settings);
 1525                break;
 526            case ScriptLanguage.VBNet:
 1527                if (opts.Logger.IsEnabled(LogEventLevel.Debug))
 528                {
 1529                    opts.Logger.Debug("Building VB.NET Issue Claims for API Key authentication");
 530                }
 531
 1532                opts.IssueClaims = IAuthHandler.BuildVBNetIssueClaims(host, settings);
 1533                break;
 534            default:
 0535                if (opts.Logger.IsEnabled(LogEventLevel.Warning))
 536                {
 0537                    opts.Logger.Warning("{language} is not supported for API Basic authentication", settings.Language);
 538                }
 0539                throw new NotSupportedException("Unsupported language");
 540        }
 541    }
 542
 543
 544    /// <summary>
 545    /// Adds API Key Authentication to the Kestrun host using the provided options object.
 546    /// </summary>
 547    /// <param name="host">The Kestrun host instance.</param>
 548    /// <param name="scheme">The authentication scheme name.</param>
 549    /// <param name="configure">The ApiKeyAuthenticationOptions object to configure the authentication.</param>
 550    /// <returns>The configured KestrunHost instance.</returns>
 551    public static KestrunHost AddApiKeyAuthentication(
 552    this KestrunHost host,
 553    string scheme,
 554    ApiKeyAuthenticationOptions configure)
 555    {
 1556        if (host.Logger.IsEnabled(LogEventLevel.Debug))
 557        {
 1558            host.Logger.Debug("Adding API Key Authentication with scheme: {Scheme}", scheme);
 559        }
 560
 1561        ArgumentNullException.ThrowIfNull(host);
 1562        ArgumentNullException.ThrowIfNull(scheme);
 1563        ArgumentNullException.ThrowIfNull(configure);
 1564        return host.AddApiKeyAuthentication(
 1565            scheme: scheme,
 1566            configure: opts =>
 1567            {
 1568                // let caller mutate everything first
 2569                opts.ExpectedKey = configure.ExpectedKey;
 2570                opts.HeaderName = configure.HeaderName;
 2571                opts.AdditionalHeaderNames = configure.AdditionalHeaderNames;
 2572                opts.AllowQueryStringFallback = configure.AllowQueryStringFallback;
 1573                // Logger configuration
 2574                opts.Logger = configure.Logger == Log.ForContext<ApiKeyAuthenticationOptions>() ?
 2575                        host.Logger.ForContext<ApiKeyAuthenticationOptions>() : configure.Logger;
 1576
 2577                opts.RequireHttps = configure.RequireHttps;
 2578                opts.EmitChallengeHeader = configure.EmitChallengeHeader;
 2579                opts.ChallengeHeaderFormat = configure.ChallengeHeaderFormat;
 2580                opts.ValidateCodeSettings = configure.ValidateCodeSettings;
 1581                // IssueClaimsCodeSettings
 2582                opts.IssueClaimsCodeSettings = configure.IssueClaimsCodeSettings;
 1583                // Claims policy configuration
 2584                opts.ClaimPolicyConfig = configure.ClaimPolicyConfig;
 2585            }
 1586        );
 587    }
 588
 589    /// <summary>
 590    /// Adds OpenID Connect authentication to the Kestrun host.
 591    /// <para>Use this for applications that require OpenID Connect authentication.</para>
 592    /// </summary>
 593    /// <param name="host">The Kestrun host instance.</param>
 594    /// <param name="scheme">The authentication scheme name.</param>
 595    /// <param name="clientId">The client ID for the OpenID Connect application.</param>
 596    /// <param name="clientSecret">The client secret for the OpenID Connect application.</param>
 597    /// <param name="authority">The authority URL for the OpenID Connect provider.</param>
 598    /// <param name="configure">An optional action to configure the OpenID Connect options.</param>
 599    /// <param name="configureAuthz">An optional action to configure the authorization options.</param>
 600    /// <returns>The configured KestrunHost instance.</returns>
 601    public static KestrunHost AddOpenIdConnectAuthentication(
 602        this KestrunHost host,
 603        string scheme,
 604        string clientId,
 605        string clientSecret,
 606        string authority,
 607        Action<OpenIdConnectOptions>? configure = null,
 608        Action<AuthorizationOptions>? configureAuthz = null)
 609    {
 2610        return host.AddAuthentication(
 2611            defaultScheme: scheme,
 2612            buildSchemes: ab =>
 2613            {
 2614                _ = ab.AddOpenIdConnect(
 2615                    authenticationScheme: scheme,
 2616                    displayName: "OIDC",
 2617                    configureOptions: opts =>
 2618                    {
 0619                        opts.ClientId = clientId;
 0620                        opts.ClientSecret = clientSecret;
 0621                        opts.Authority = authority;
 0622                        opts.ResponseType = "code";
 0623                        opts.SaveTokens = true;
 0624                        configure?.Invoke(opts);
 2625                    });
 2626            },
 2627            configureAuthz: configureAuthz
 2628        );
 629    }
 630
 631
 632    /// <summary>
 633    /// Adds authentication and authorization middleware to the Kestrun host.
 634    /// </summary>
 635    /// <param name="host">The Kestrun host instance.</param>
 636    /// <param name="buildSchemes">A delegate to configure authentication schemes.</param>
 637    /// <param name="defaultScheme">The default authentication scheme (default is JwtBearer).</param>
 638    /// <param name="configureAuthz">Optional authorization policy configuration.</param>
 639    /// <returns>The configured KestrunHost instance.</returns>
 640    internal static KestrunHost AddAuthentication(this KestrunHost host,
 641    Action<AuthenticationBuilder> buildSchemes,            // ← unchanged
 642    string defaultScheme = JwtBearerDefaults.AuthenticationScheme,
 643    Action<AuthorizationOptions>? configureAuthz = null)
 644    {
 22645        _ = host.AddService(services =>
 22646        {
 22647            var ab = services.AddAuthentication(defaultScheme);
 22648            buildSchemes(ab);                                  // Basic + JWT here
 22649
 22650            // make sure UseAuthorization() can find its services
 22651            _ = configureAuthz is null ? services.AddAuthorization() : services.AddAuthorization(configureAuthz);
 41652        });
 653
 22654        return host.Use(app =>
 22655        {
 22656            const string Key = "__kr.authmw";
 22657            if (!app.Properties.ContainsKey(Key))
 22658            {
 22659                _ = app.UseAuthentication();
 22660                _ = app.UseAuthorization();
 22661                app.Properties[Key] = true;
 22662                Log.Information("Kestrun: Authentication & Authorization middleware added.");
 22663            }
 44664        });
 665    }
 666
 667    /// <summary>
 668    /// Checks if the specified authentication scheme is registered in the Kestrun host.
 669    /// </summary>
 670    /// <param name="host">The Kestrun host instance.</param>
 671    /// <param name="schemeName">The name of the authentication scheme to check.</param>
 672    /// <returns>True if the scheme is registered; otherwise, false.</returns>
 673    public static bool HasAuthScheme(this KestrunHost host, string schemeName)
 674    {
 15675        var schemeProvider = host.App.Services.GetRequiredService<IAuthenticationSchemeProvider>();
 15676        var scheme = schemeProvider.GetSchemeAsync(schemeName).GetAwaiter().GetResult();
 15677        return scheme != null;
 678    }
 679
 680    /// <summary>
 681    /// Adds authorization services to the Kestrun host.
 682    /// </summary>
 683    /// <param name="host">The Kestrun host instance.</param>
 684    /// <param name="cfg">Optional configuration for authorization options.</param>
 685    /// <returns>The configured KestrunHost instance.</returns>
 686    public static KestrunHost AddAuthorization(this KestrunHost host, Action<AuthorizationOptions>? cfg = null)
 687    {
 1688        return host.AddService(services =>
 1689        {
 1690            _ = cfg == null ? services.AddAuthorization() : services.AddAuthorization(cfg);
 2691        });
 692    }
 693
 694
 695
 696    /// <summary>
 697    /// Checks if the specified authorization policy is registered in the Kestrun host.
 698    /// </summary>
 699    /// <param name="host">The Kestrun host instance.</param>
 700    /// <param name="policyName">The name of the authorization policy to check.</param>
 701    /// <returns>True if the policy is registered; otherwise, false.</returns>
 702    public static bool HasAuthPolicy(this KestrunHost host, string policyName)
 703    {
 14704        var policyProvider = host.App.Services.GetRequiredService<IAuthorizationPolicyProvider>();
 14705        var policy = policyProvider.GetPolicyAsync(policyName).GetAwaiter().GetResult();
 14706        return policy != null;
 707    }
 708
 709    /// <summary>
 710    /// Helper to copy values from a user-supplied CookieAuthenticationOptions instance to the instance
 711    /// created by the framework inside AddCookie(). Reassigning the local variable (opts = source) would
 712    /// not work because only the local reference changes – the framework keeps the original instance.
 713    /// </summary>
 714    /// <param name="source">The source options to copy from.</param>
 715    /// <param name="target">The target options to copy to.</param>
 716    /// <exception cref="ArgumentNullException">Thrown when source or target is null.</exception>
 717    /// <remarks>
 718    /// Only copies primitive properties and references. Does not clone complex objects like CookieBuilder.
 719    /// </remarks>
 720    private static void CopyCookieAuthenticationOptions(CookieAuthenticationOptions source, CookieAuthenticationOptions 
 721    {
 722        // Paths & return URL
 0723        target.LoginPath = source.LoginPath;
 0724        target.LogoutPath = source.LogoutPath;
 0725        target.AccessDeniedPath = source.AccessDeniedPath;
 0726        target.ReturnUrlParameter = source.ReturnUrlParameter;
 727
 728        // Expiration & sliding behavior
 0729        target.ExpireTimeSpan = source.ExpireTimeSpan;
 0730        target.SlidingExpiration = source.SlidingExpiration;
 731
 732        // Cookie builder settings
 733        // (Cookie is always non-null; copy primitive settings)
 0734        target.Cookie.Name = source.Cookie.Name;
 0735        target.Cookie.Path = source.Cookie.Path;
 0736        target.Cookie.Domain = source.Cookie.Domain;
 0737        target.Cookie.HttpOnly = source.Cookie.HttpOnly;
 0738        target.Cookie.SameSite = source.Cookie.SameSite;
 0739        target.Cookie.SecurePolicy = source.Cookie.SecurePolicy;
 0740        target.Cookie.IsEssential = source.Cookie.IsEssential;
 0741        target.Cookie.MaxAge = source.Cookie.MaxAge;
 742
 743        // Forwarding
 0744        target.ForwardAuthenticate = source.ForwardAuthenticate;
 0745        target.ForwardChallenge = source.ForwardChallenge;
 0746        target.ForwardDefault = source.ForwardDefault;
 0747        target.ForwardDefaultSelector = source.ForwardDefaultSelector;
 0748        target.ForwardForbid = source.ForwardForbid;
 0749        target.ForwardSignIn = source.ForwardSignIn;
 0750        target.ForwardSignOut = source.ForwardSignOut;
 751
 752        // Data protection / ticket / session
 0753        target.TicketDataFormat = source.TicketDataFormat;
 0754        target.DataProtectionProvider = source.DataProtectionProvider;
 0755        target.SessionStore = source.SessionStore;
 756
 757        // Events & issuer
 0758        if (source.Events is not null)
 759        {
 0760            target.Events = source.Events;
 761        }
 0762        target.EventsType = source.EventsType;
 0763        target.ClaimsIssuer = source.ClaimsIssuer;
 0764    }
 765}

Methods/Properties

AddBasicAuthentication(Kestrun.Hosting.KestrunHost,System.String,System.Action`1<Kestrun.Authentication.BasicAuthenticationOptions>)
AddBasicAuthentication(Kestrun.Hosting.KestrunHost,System.String,Kestrun.Authentication.BasicAuthenticationOptions)
AddJwtBearerAuthentication(Kestrun.Hosting.KestrunHost,System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters,System.Action`1<Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions>,Kestrun.Claims.ClaimPolicyConfig)
AddCookieAuthentication(Kestrun.Hosting.KestrunHost,System.String,System.Action`1<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>,Kestrun.Claims.ClaimPolicyConfig)
AddCookieAuthentication(Kestrun.Hosting.KestrunHost,System.String,Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions,Kestrun.Claims.ClaimPolicyConfig)
AddWindowsAuthentication(Kestrun.Hosting.KestrunHost)
AddApiKeyAuthentication(Kestrun.Hosting.KestrunHost,System.String,System.Action`1<Kestrun.Authentication.ApiKeyAuthenticationOptions>)
ConfigureBasicAuthValidators(Kestrun.Hosting.KestrunHost,Kestrun.Authentication.BasicAuthenticationOptions)
ConfigureBasicIssueClaims(Kestrun.Hosting.KestrunHost,Kestrun.Authentication.BasicAuthenticationOptions)
ConfigureApiKeyValidators(Kestrun.Hosting.KestrunHost,Kestrun.Authentication.ApiKeyAuthenticationOptions)
ConfigureApiKeyIssueClaims(Kestrun.Hosting.KestrunHost,Kestrun.Authentication.ApiKeyAuthenticationOptions)
AddApiKeyAuthentication(Kestrun.Hosting.KestrunHost,System.String,Kestrun.Authentication.ApiKeyAuthenticationOptions)
AddOpenIdConnectAuthentication(Kestrun.Hosting.KestrunHost,System.String,System.String,System.String,System.String,System.Action`1<Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions>,System.Action`1<Microsoft.AspNetCore.Authorization.AuthorizationOptions>)
AddAuthentication(Kestrun.Hosting.KestrunHost,System.Action`1<Microsoft.AspNetCore.Authentication.AuthenticationBuilder>,System.String,System.Action`1<Microsoft.AspNetCore.Authorization.AuthorizationOptions>)
HasAuthScheme(Kestrun.Hosting.KestrunHost,System.String)
AddAuthorization(Kestrun.Hosting.KestrunHost,System.Action`1<Microsoft.AspNetCore.Authorization.AuthorizationOptions>)
HasAuthPolicy(Kestrun.Hosting.KestrunHost,System.String)
CopyCookieAuthenticationOptions(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions,Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions)