fengling-console/docs/task-03-configure-openiddict.md

5.7 KiB

Task 3: Configure OpenIddict

Task Description

Files:

  • Create: src/Fengling.AuthService/Configuration/OpenIddictSetup.cs
  • Modify: src/Fengling.AuthService/Program.cs

Implementation Steps

Step 1: Create OpenIddict configuration

Create: src/Fengling.AuthService/Configuration/OpenIddictSetup.cs

using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Validation.AspNetCore;

namespace Fengling.AuthService.Configuration;

public static class OpenIddictSetup
{
    public static IServiceCollection AddOpenIddictConfiguration(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddOpenIddict()
            .AddCore(options =>
            {
                options.UseEntityFrameworkCore()
                    .UseDbContext<Data.ApplicationDbContext>();
            })
            .AddServer(options =>
            {
                options.SetIssuer(configuration["OpenIddict:Issuer"] ?? "https://auth.fengling.local");
                options.AddSigningKey(new SymmetricSecurityKey(
                    System.Text.Encoding.UTF8.GetBytes("fengling-super-secret-key-for-dev-only-change-in-prod-please!!!")));

                options.AllowAuthorizationCodeFlow()
                       .AllowPasswordFlow()
                       .AllowRefreshTokenFlow()
                       .RequireProofKeyForCodeExchange();

                options.RegisterScopes("api", "offline_access");

                options.AddDevelopmentEncryptionCertificate()
                       .AddDevelopmentSigningCertificate();

                options.UseAspNetCore()
                       .EnableAuthorizationEndpointPassThrough()
                       .EnableTokenEndpointPassThrough()
                       .EnableLogoutEndpointPassThrough();
            })
            .AddValidation(options =>
            {
                options.UseLocalServer();
                options.UseAspNetCore();
            });

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
        });

        return services;
    }
}

Step 2: Update Program.cs with OpenIddict and EF Core

Edit: src/Fengling.AuthService/Program.cs

using Fengling.AuthService.Configuration;
using Fengling.AuthService.Data;
using Fengling.AuthService.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Serilog;

var builder = WebApplication.CreateBuilder(args);

// Serilog
Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(builder.Configuration)
    .Enrich.FromLogContext()
    .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

builder.Host.UseSerilog();

// Database
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));

// Identity
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

// OpenIddict
builder.Services.AddOpenIddictConfiguration(builder.Configuration);

// OpenTelemetry
builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource =>
        resource.AddService("Fengling.AuthService"))
    .AddAspNetCoreInstrumentation()
    .AddHttpClientInstrumentation()
    .AddSource("OpenIddict.Server.AspNetCore")
    .AddOtlpExporter();

// Controllers
builder.Services.AddControllers();

// Swagger
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Fengling Auth Service",
        Version = "v1",
        Description = "Authentication and authorization service using OpenIddict"
    });

    options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new OpenApiOAuthFlows
        {
            Password = new OpenApiOAuthFlow
            {
                TokenUrl = "/connect/token"
            }
        }
    });
});

var app = builder.Build();

// Configure pipeline
app.UseSwagger();
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "Fengling Auth Service v1");
    options.OAuthClientId("swagger-ui");
    options.OAuthUsePkce();
});

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

app.MapControllers();

app.Run();

Step 3: Run to verify startup

Run:

dotnet run

Expected: Service starts without errors, Swagger UI available at http://localhost:5000/swagger

Step 4: Commit

git add src/Fengling.AuthService/Configuration/ src/Fengling.AuthService/Program.cs
git commit -m "feat(auth): configure OpenIddict with JWT and OAuth2 support"

Context

This task configures OpenIddict for OAuth2/OIDC authentication, including:

  • Server configuration with token endpoints
  • Password flow for user authentication
  • Development certificates for signing
  • Integration with ASP.NET Core Identity
  • Swagger UI with OAuth2 support

Tech Stack: OpenIddict 7.2.0, ASP.NET Core Identity, Swagger

Verification

  • OpenIddictSetup class created
  • Program.cs updated with OpenIddict configuration
  • Service starts without errors
  • Swagger UI accessible
  • Committed to git

Notes

  • Using symmetric key for token signing (dev only)
  • Password flow enabled for direct authentication
  • Development certificates used (replace in production)