Skip to content

ASP.NET Core 8 Startup File Configuration

Problem

When migrating ASP.NET MVC applications from .NET Framework to .NET 8, developers encounter significant changes in the application startup pattern. Key questions arise:

  • Does ASP.NET Core 8 still use the OWIN-style Startup.cs file?
  • If not, how do you configure elements like authentication/authorization and middleware?
  • Where should services and request pipeline configuration go in modern ASP.NET Core?

This transition presents challenges for developers accustomed to the traditional startup class pattern.

Solution

ASP.NET Core 8 introduces two configuration approaches:

  1. Unified Program.cs (Recommended Default)
    All configuration happens in a single Program.cs file
  2. Traditional Startup.cs Class
    Maintains separation between service configuration and HTTP pipeline setup

Option 1: Modern Unified Configuration (Program.cs)

cs
var builder = WebApplication.CreateBuilder(args);

// Service Configuration
builder.Services.AddControllersWithViews();

// Configure Authentication (OAuth Example)
builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "Cookies";
        options.DefaultSignInScheme = "Cookies";
        options.DefaultChallengeScheme = "Google";
    })
    .AddCookie()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    });

var app = builder.Build();

// Middleware Pipeline Configuration
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

// Authentication & Authorization Middleware
app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Key aspects of this approach:

  • Services registered via builder.Services collection
  • Middleware configured through app object methods
  • No separate Startup class required
  • Clean flow from services → build → middleware pipeline

Option 2: Traditional Startup Class

cs
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}
cs
public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        
        // OAuth Configuration
        services.AddAuthentication(options => { /* ... */ })
            .AddCookie()
            .AddGoogle(options => 
            {
                options.ClientId = Configuration["Authentication:Google:ClientId"];
                options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
            });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (!env.IsDevelopment())
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

Key Differences

FeatureUnified Program.csStartup Class
File StructureSingle fileTwo files
Project TemplatesDefault in .NET 6-8Legacy format
Configuration Accessbuilder.ConfigurationConstructor injection
Middleware RegistrationDirectly on app objectIn Configure method
Service RegistrationBefore var app = builder.Build()In ConfigureServices method
Separation of ConcernsModerateHigh

Migration Recommendations

  1. New Projects
    Use unified Program.cs approach - it's streamlined and reflects current best practices

  2. Existing Applications

    • Gradually migrate from Startup.cs to unified approach
    • Create extension methods to maintain organization:
      cs
      // In Program.cs
      builder.Services.AddCustomAuthentication();
      builder.Services.AddDatabaseServices();
      
      // In separate files
      public static class ServiceExtensions
      {
          public static void AddCustomAuthentication(this IServiceCollection services)
          {
              services.AddAuthentication()...;
          }
      }
  3. Authentication Setup

    • Modern authentication uses AddAuthentication() + Add[Provider]() pattern
    • OWIN/Katana-specific code needs replacement
    • Configuration-based setup recommended:
    json
    "Authentication": {
      "Google": {
        "ClientId": "YOUR_CLIENT_ID",
        "ClientSecret": "YOUR_CLIENT_SECRET"
      }
    }

Important Considerations

Placement Matters

Ensure authentication middleware appears:

  • After UseRouting()
  • Before UseAuthorization()
  • Before endpoint mapping (MapControllers()/MapRazorPages())

Authorization Sequence

cs
app.UseRouting();
app.UseAuthentication(); // Must come first
app.UseAuthorization();  // Follows authentication

OAuth Flow Changes

ASP.NET Core handles OAuth differently than OWIN:

  • Different authentication middleware APIs
  • Revised authorization policy system
  • Token management changes

When to Use Each Approach

  • Choose Unified Program.cs When

    • Starting a new project
    • Building microservices
    • Creating small-to-medium applications
    • Wanting minimal ceremony
  • Choose Startup Class When

    • Migrating large existing applications
    • Requiring deep organization of configuration
    • Working with legacy packages requiring startup class
    • Needing explicit separation of service/pipeline concerns

Conclusion

ASP.NET Core 8 offers flexibility in startup configuration:

  1. Unified Program.cs is the modern default for new projects
  2. Traditional `Startup.cs remains supported for migration scenarios

For OAuth and service configuration, both patterns support:

  • Adding authentication via AddAuthentication()
  • Setting up providers like Google, Facebook, etc.
  • Configuring middleware ordering

The default project templates now generate the unified approach, reflecting Microsoft's recommendation for new development. Migrating applications should focus on porting startup logic while accounting for authentication model changes.