From 3b28b89f491b195ad7fdac88561cd6d0382dd4a1 Mon Sep 17 00:00:00 2001 From: Spencer Twaddle <7374698+stwaddle@users.noreply.github.com> Date: Sat, 2 May 2026 15:54:57 -0500 Subject: [PATCH] Add OIDC discovery retry loop on startup Blocks startup for up to 2 minutes retrying the OIDC discovery doc fetch, then proceeds anyway. Prevents JWT middleware from failing to initialize when the auth and app containers start simultaneously. Co-Authored-By: Claude Sonnet 4.6 --- src/Budget.Api/Program.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Budget.Api/Program.cs b/src/Budget.Api/Program.cs index 61327de..2c0dfc0 100644 --- a/src/Budget.Api/Program.cs +++ b/src/Budget.Api/Program.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; var builder = WebApplication.CreateBuilder(args); @@ -48,6 +49,32 @@ builder.Services.AddHealthChecks() var app = builder.Build(); +// Block startup until OIDC discovery succeeds (handles auth/app container race). +var jwtOpts = app.Services.GetRequiredService>() + .Get(JwtBearerDefaults.AuthenticationScheme); +var startupLogger = app.Services.GetRequiredService().CreateLogger("Startup"); +var deadline = DateTime.UtcNow + TimeSpan.FromMinutes(2); +while (true) +{ + try + { + jwtOpts.ConfigurationManager!.RequestRefresh(); + await jwtOpts.ConfigurationManager!.GetConfigurationAsync(CancellationToken.None); + startupLogger.LogInformation("OIDC discovery succeeded"); + break; + } + catch (Exception ex) + { + if (DateTime.UtcNow >= deadline) + { + startupLogger.LogWarning(ex, "OIDC discovery timed out after 2 min; continuing startup"); + break; + } + startupLogger.LogWarning(ex, "OIDC discovery failed; retrying in 5 s"); + Thread.Sleep(TimeSpan.FromSeconds(5)); + } +} + // Apply EF migrations automatically on startup using (var scope = app.Services.CreateScope()) {