namespace MyOffice.Web.Auth; using DbContext; using Infrastructure; using Microsoft.Extensions.Options; using OpenIddict.Abstractions; using static OpenIddict.Abstractions.OpenIddictConstants; public sealed class OpenIddictSeeder : IHostedService { private readonly IServiceProvider _serviceProvider; private readonly IConfiguration _configuration; private readonly ILogger _logger; public OpenIddictSeeder( IServiceProvider serviceProvider, IConfiguration configuration, ILogger logger ) { _serviceProvider = serviceProvider; _configuration = configuration; _logger = logger; } public async Task StartAsync(CancellationToken cancellationToken) { await using var scope = _serviceProvider.CreateAsyncScope(); await RegisterScopesAsync(scope.ServiceProvider, cancellationToken); await RegisterClientAsync(scope.ServiceProvider, cancellationToken); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; private async Task RegisterScopesAsync(IServiceProvider provider, CancellationToken cancellationToken) { var manager = provider.GetRequiredService(); if (await manager.FindByNameAsync(OpenIddictAuthConstants.ApiScope, cancellationToken) is null) { await manager.CreateAsync(new OpenIddictScopeDescriptor { Name = OpenIddictAuthConstants.ApiScope, DisplayName = OpenIddictAuthConstants.ApiFriendlyName, Resources = { OpenIddictAuthConstants.ApiScope } }, cancellationToken); _logger.LogInformation("Created OpenIddict scope {Scope}.", OpenIddictAuthConstants.ApiScope); } if (await manager.FindByNameAsync(OpenIddictAuthConstants.RolesScope, cancellationToken) is null) { await manager.CreateAsync(new OpenIddictScopeDescriptor { Name = OpenIddictAuthConstants.RolesScope, DisplayName = "User roles" }, cancellationToken); _logger.LogInformation("Created OpenIddict scope {Scope}.", OpenIddictAuthConstants.RolesScope); } } private async Task RegisterClientAsync(IServiceProvider provider, CancellationToken cancellationToken) { var manager = provider.GetRequiredService(); var globalSettings = provider.GetRequiredService(); var redirectUri = BuildRedirectUri(globalSettings.Host); var existing = await manager.FindByClientIdAsync(OpenIddictAuthConstants.SpaClientId, cancellationToken); var descriptor = CreateSpaClientDescriptor(redirectUri); if (existing is null) { await manager.CreateAsync(descriptor, cancellationToken); _logger.LogInformation("Created OpenIddict client {ClientId}.", OpenIddictAuthConstants.SpaClientId); return; } var currentRedirectUris = await manager.GetRedirectUrisAsync(existing, cancellationToken); foreach (var uri in currentRedirectUris) { if (!string.Equals(uri, redirectUri.ToString(), StringComparison.Ordinal)) descriptor.RedirectUris.Add(new Uri(uri, UriKind.Absolute)); } await manager.UpdateAsync(existing, descriptor, cancellationToken); _logger.LogInformation("Updated OpenIddict client {ClientId}.", OpenIddictAuthConstants.SpaClientId); } internal static OpenIddictApplicationDescriptor CreateSpaClientDescriptor(Uri redirectUri) { var descriptor = new OpenIddictApplicationDescriptor { ClientId = OpenIddictAuthConstants.SpaClientId, DisplayName = "MyOffice SPA", ClientType = ClientTypes.Public, ConsentType = ConsentTypes.Implicit, Permissions = { Permissions.Endpoints.Authorization, Permissions.Endpoints.Token, Permissions.Endpoints.EndSession, Permissions.GrantTypes.AuthorizationCode, Permissions.GrantTypes.Password, Permissions.GrantTypes.RefreshToken, Permissions.Prefixes.GrantType + OpenIddictAuthConstants.ExternalGrantType, Permissions.ResponseTypes.Code, Permissions.Scopes.Email, Permissions.Scopes.Profile, Permissions.Scopes.Roles, Permissions.Prefixes.Scope + Scopes.OpenId, Permissions.Prefixes.Scope + Scopes.OfflineAccess, Permissions.Prefixes.Scope + OpenIddictAuthConstants.ApiScope, Permissions.Prefixes.Scope + OpenIddictAuthConstants.RolesScope } }; descriptor.RedirectUris.Add(redirectUri); return descriptor; } internal static Uri BuildRedirectUri(string? host) { if (string.IsNullOrWhiteSpace(host)) return new Uri("http://localhost:4300/silent-refresh.html"); return new Uri($"{host.TrimEnd('/')}/silent-refresh.html"); } }