Strongly-Typed Configuration

Reading config with Configuration["JWT:Key"] scattered across the codebase means every typo is a silent null discovered at runtime. The Options pattern binds a config section to a real class once, validates it at startup, and injects it wherever it's needed.

Define the shape

Options/JwtOptions.cs
public class JwtOptions
{
    public const string SectionName = "JWT";

    public required string Key { get; set; }
    public required string Issuer { get; set; }
    public double ExpiryMinutes { get; set; } = 60;
}
appsettings.json
{
  "JWT": {
    "Key": "replace-with-a-real-secret",
    "Issuer": "YourApp",
    "ExpiryMinutes": 60
  }
}

Bind and validate at startup

ValidateDataAnnotations() plus ValidateOnStart() means a missing or malformed section fails the app immediately on boot โ€” not the first time a request happens to hit the code path that reads it.

Dependencies.cs
services.AddOptions<JwtOptions>()
    .Bind(configuration.GetSection(JwtOptions.SectionName))
    .ValidateDataAnnotations()
    .ValidateOnStart();

Inject it โ€” no config lookups at the call site

IOptions<T> for values that never change during the app's lifetime; IOptionsSnapshot<T> (scoped) if you want it to re-read on each request in a reloadable-config scenario.

TokenService.cs
public class TokenService : ITokenService
{
    private readonly JwtOptions _options;

    public TokenService(IOptions<JwtOptions> options)
    {
        _options = options.Value;
    }

    public Token GenerateToken(User user)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Key));
        var expiresOn = DateTime.UtcNow.AddMinutes(_options.ExpiryMinutes);
        // ... _options.Issuer, no ["JWT:Issuer"] string lookup anywhere
    }
}
Compare to the manual Configuration["JWT:Key"] ?? throw ... checks in JWT Authentication โ€” those work, but the validation happens lazily on first use. Binding to a class with ValidateOnStart() moves that same check to app startup, which is worth it once a project has more than one or two config values.