diff --git a/src/AnimeFeedManager.Features/AnimeFeedManager.Features.csproj b/src/AnimeFeedManager.Features/AnimeFeedManager.Features.csproj index 00bbc7a3..4a06aeee 100644 --- a/src/AnimeFeedManager.Features/AnimeFeedManager.Features.csproj +++ b/src/AnimeFeedManager.Features/AnimeFeedManager.Features.csproj @@ -10,11 +10,13 @@ + + diff --git a/src/AnimeFeedManager.Features/Images/IO/AzureImagesBlobStore.cs b/src/AnimeFeedManager.Features/Images/IO/AzureImagesBlobStore.cs index 2191f5f3..62a44305 100644 --- a/src/AnimeFeedManager.Features/Images/IO/AzureImagesBlobStore.cs +++ b/src/AnimeFeedManager.Features/Images/IO/AzureImagesBlobStore.cs @@ -1,26 +1,33 @@ using AnimeFeedManager.Features.Infrastructure.Messaging; +using Azure.Identity; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; -using Microsoft.Extensions.Options; namespace AnimeFeedManager.Features.Images.IO; -public class AzureImagesBlobStore : IImagesBlobStore, IDisposable +public class AzureImagesBlobStore : IImagesBlobStore { private const string Container = "images"; private BlobContainerClient _containerClient; - private readonly IDisposable? _optionsReference; - public AzureImagesBlobStore(IOptionsMonitor blobStorageOptions) + + public AzureImagesBlobStore(AzureStorageSettings storageOptions) { - _containerClient = new BlobContainerClient(blobStorageOptions.CurrentValue.StorageConnectionString, Container); + _containerClient = GetClient(storageOptions); Initialize(); + } - _optionsReference = blobStorageOptions.OnChange(options => + private static BlobContainerClient GetClient(AzureStorageSettings azureSettings) + { + return azureSettings switch { - _containerClient = new BlobContainerClient(options.StorageConnectionString, Container); - Initialize(); - }); - + ConnectionStringSettings connectionStringOptions => new BlobContainerClient( + connectionStringOptions.StorageConnectionString, Container), + TokenCredentialSettings tokenCredentialOptions => new BlobContainerClient( + new Uri(tokenCredentialOptions.BlobUri, Container), new DefaultAzureCredential()), + _ => throw new ArgumentException( + "Provided Table Storage configuration is not valid. Make sure Configurations for Azure table Storage is correct for either connection string or managed identities", + nameof(TableClientOptions)) + }; } private void Initialize() @@ -41,10 +48,4 @@ public async Task Upload(string fileName, string path, Stream data) await blob.UploadAsync(data, new BlobUploadOptions {HttpHeaders = blobHttpHeader}); return blob.Uri; } - - - public void Dispose() - { - _optionsReference?.Dispose(); - } } \ No newline at end of file diff --git a/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureBlobStorageOptions.cs b/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureBlobStorageOptions.cs deleted file mode 100644 index c51dabda..00000000 --- a/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureBlobStorageOptions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace AnimeFeedManager.Features.Infrastructure.Messaging; - -public sealed class AzureBlobStorageOptions -{ - public string? StorageConnectionString { get; set; } -} \ No newline at end of file diff --git a/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureQueueMessages.cs b/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureQueueMessages.cs index db59b4de..7a314716 100644 --- a/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureQueueMessages.cs +++ b/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureQueueMessages.cs @@ -1,7 +1,7 @@ using System.Text.Json; using AnimeFeedManager.Common.Domain.Errors; +using Azure.Identity; using Azure.Storage.Queues; -using Microsoft.Extensions.Options; namespace AnimeFeedManager.Features.Infrastructure.Messaging; @@ -30,28 +30,21 @@ Task> SendDelayedMessage(T message, Box destiny, Mi CancellationToken cancellationToken = default); } -public class AzureQueueMessages : IDomainPostman, IDisposable +public class AzureQueueMessages : IDomainPostman { - private AzureBlobStorageOptions _blobStorageOptions; + private AzureStorageSettings _azureSettings; private readonly JsonSerializerOptions _jsonOptions; - private readonly QueueClientOptions _queueClientOptions; - private readonly IDisposable? _optionsReference; + private readonly QueueClientOptions _queueClientOptions; - public AzureQueueMessages( - IOptionsMonitor blobStorageOptions) + public AzureQueueMessages(AzureStorageSettings tableStorageSettings) { - _blobStorageOptions = blobStorageOptions.CurrentValue; + _azureSettings = tableStorageSettings; _jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions - { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + {PropertyNamingPolicy = JsonNamingPolicy.CamelCase}); _queueClientOptions = new QueueClientOptions { MessageEncoding = QueueMessageEncoding.Base64 }; - _optionsReference = blobStorageOptions.OnChange(options => - { - _blobStorageOptions = options; - }); - } public async Task> SendMessage(T message, Box destiny, @@ -85,19 +78,28 @@ public async Task> SendDelayedMessage(T message, Bo private async Task SendMessage(T message, string destiny, TimeSpan? delay = default, CancellationToken cancellationToken = default) { - var queue = new QueueClient(_blobStorageOptions?.StorageConnectionString ?? string.Empty, destiny, - _queueClientOptions); + var queue = GetClient(destiny); await queue.CreateIfNotExistsAsync(cancellationToken: cancellationToken); await queue.SendMessageAsync(AsBinary(message), cancellationToken: cancellationToken, visibilityTimeout: delay); } - private BinaryData AsBinary(T data) + private QueueClient GetClient(string destiny) { - return BinaryData.FromObjectAsJson(data, _jsonOptions); + return _azureSettings switch + { + ConnectionStringSettings connectionStringOptions => new QueueClient( + connectionStringOptions.StorageConnectionString, destiny, + _queueClientOptions), + TokenCredentialSettings tokenCredentialOptions => new QueueClient( + tokenCredentialOptions.QueueUri, new DefaultAzureCredential(), _queueClientOptions), + _ => throw new ArgumentException( + "Provided Table Storage configuration is not valid. Make sure Configurations for Azure table Storage is correct for either connection string or managed identities", + nameof(TableClientOptions)) + }; } - public void Dispose() + private BinaryData AsBinary(T data) { - _optionsReference?.Dispose(); + return BinaryData.FromObjectAsJson(data, _jsonOptions); } } \ No newline at end of file diff --git a/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureStorageSettings.cs b/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureStorageSettings.cs new file mode 100644 index 00000000..d5acc259 --- /dev/null +++ b/src/AnimeFeedManager.Features/Infrastructure/Messaging/AzureStorageSettings.cs @@ -0,0 +1,17 @@ +namespace AnimeFeedManager.Features.Infrastructure.Messaging; + +public abstract record AzureStorageSettings; + +public sealed record ConnectionStringSettings(string StorageConnectionString) : AzureStorageSettings; + +public sealed record TokenCredentialSettings(QueueUri QueueUri, BlobUri BlobUri) : AzureStorageSettings; + +public readonly record struct QueueUri(Uri Uri) +{ + public static implicit operator Uri(QueueUri queueUri) => queueUri.Uri; +} + +public readonly record struct BlobUri(Uri Uri) +{ + public static implicit operator Uri(BlobUri blobUri) => blobUri.Uri; +} \ No newline at end of file diff --git a/src/AnimeFeedManager.Features/Infrastructure/Registration.cs b/src/AnimeFeedManager.Features/Infrastructure/Registration.cs index e172f92e..677e1783 100644 --- a/src/AnimeFeedManager.Features/Infrastructure/Registration.cs +++ b/src/AnimeFeedManager.Features/Infrastructure/Registration.cs @@ -1,22 +1,73 @@ -using AnimeFeedManager.Features.Infrastructure.Messaging; +using System.Diagnostics.CodeAnalysis; +using AnimeFeedManager.Features.Infrastructure.Messaging; +using Azure.Identity; +using Microsoft.Extensions.Configuration; namespace AnimeFeedManager.Features.Infrastructure; public static class InfrastructureRegistration { - public static IServiceCollection RegisterStorage(this IServiceCollection services, string connectionString) + private const string TableBaseUrl = "https://{0}.table.core.windows.net"; + private const string QueueBaseUrl = "https://{0}.queue.core.windows.net"; + private const string BlobBaseUrl = "https://{0}.blob.core.windows.net"; + + + public static IServiceCollection RegisterStorage(this IServiceCollection services, + string connectionString) + { + RegisterWithConnectionString(services, connectionString); + services.RegisterCommonServices(); + return services; + } + + public static IServiceCollection RegisterStorage(this IServiceCollection services, + IConfigurationManager configuration) { - - services.Configure(options => + var storageAccountName = configuration["StorageAccountName"]; + if (!string.IsNullOrEmpty(storageAccountName)) { - options.StorageConnectionString = connectionString; - }); + RegisterWithAzureIdentity(services, storageAccountName); + } + else + { + RegisterWithConnectionString(services, configuration.GetConnectionString("AzureStorage") ?? string.Empty); + } + + services.RegisterCommonServices(); + + return services; + } - var tableClient = new TableServiceClient(connectionString); + private static void RegisterCommonServices(this IServiceCollection services) + { services.TryAddSingleton(); services.TryAddSingleton(typeof(ITableClientFactory<>), typeof(TableClientFactory<>)); - services.TryAddSingleton(tableClient); + } - return services; + private static void RegisterWithAzureIdentity(IServiceCollection services, string storageAccountName) + { + if (CreateUri(TableBaseUrl, storageAccountName, out var tableUri) && + CreateUri(QueueBaseUrl, storageAccountName, out var queueUri) && + CreateUri(BlobBaseUrl, storageAccountName, out var blobUri)) + { + services.TryAddSingleton( + new TokenCredentialSettings(new QueueUri(queueUri), new BlobUri(blobUri))); + services.TryAddSingleton(new TableServiceClient(tableUri, new DefaultAzureCredential())); + } + else + { + throw new ArgumentException("Azure Storage resources are malformed."); + } + } + + private static void RegisterWithConnectionString(IServiceCollection services, string connectionString) + { + services.TryAddSingleton(new ConnectionStringSettings(connectionString)); + services.TryAddSingleton(new TableServiceClient(connectionString)); + } + + private static bool CreateUri(string baseUrl, string storageAccountName, [NotNullWhen(true)] out Uri? tableUri) + { + return Uri.TryCreate($"{baseUrl}{storageAccountName}", UriKind.Absolute, out tableUri); } } \ No newline at end of file diff --git a/src/AnimeFeedManager.Features/Movies/Registration.cs b/src/AnimeFeedManager.Features/Movies/Registration.cs index 0a063af4..fd57185c 100644 --- a/src/AnimeFeedManager.Features/Movies/Registration.cs +++ b/src/AnimeFeedManager.Features/Movies/Registration.cs @@ -3,7 +3,6 @@ using AnimeFeedManager.Features.Movies.Scrapping; using AnimeFeedManager.Features.Movies.Scrapping.IO; using AnimeFeedManager.Features.Movies.Subscriptions.IO; -using AnimeFeedManager.Features.Tv.Subscriptions.IO; namespace AnimeFeedManager.Features.Movies; diff --git a/src/AnimeFeedManager.Features/Seasons/IO/LatestSeasonsGetter.cs b/src/AnimeFeedManager.Features/Seasons/IO/LatestSeasonsGetter.cs index 822271f6..423be82c 100644 --- a/src/AnimeFeedManager.Features/Seasons/IO/LatestSeasonsGetter.cs +++ b/src/AnimeFeedManager.Features/Seasons/IO/LatestSeasonsGetter.cs @@ -1,5 +1,4 @@ -using System.Collections.Frozen; -using System.Text.Json; +using System.Text.Json; using AnimeFeedManager.Common.Domain.Errors; namespace AnimeFeedManager.Features.Seasons.IO; diff --git a/src/AnimeFeedManager.Functions/AnimeFeedManager.Functions.csproj b/src/AnimeFeedManager.Functions/AnimeFeedManager.Functions.csproj index c68f31e5..d0d47478 100644 --- a/src/AnimeFeedManager.Functions/AnimeFeedManager.Functions.csproj +++ b/src/AnimeFeedManager.Functions/AnimeFeedManager.Functions.csproj @@ -8,12 +8,12 @@ - - - + + + - + diff --git a/src/AnimeFeedManager.Web/AnimeFeedManager.Web.csproj b/src/AnimeFeedManager.Web/AnimeFeedManager.Web.csproj index f9c4677c..a08e7933 100644 --- a/src/AnimeFeedManager.Web/AnimeFeedManager.Web.csproj +++ b/src/AnimeFeedManager.Web/AnimeFeedManager.Web.csproj @@ -12,6 +12,9 @@ + + + diff --git a/src/AnimeFeedManager.Web/Bootstrapping/Registration.cs b/src/AnimeFeedManager.Web/Bootstrapping/Registration.cs index 0f37c56b..013bd4d2 100644 --- a/src/AnimeFeedManager.Web/Bootstrapping/Registration.cs +++ b/src/AnimeFeedManager.Web/Bootstrapping/Registration.cs @@ -1,4 +1,5 @@ -using AnimeFeedManager.Features.Images; +using AnimeFeedManager.Common.Domain.Types; +using AnimeFeedManager.Features.Images; using AnimeFeedManager.Features.Infrastructure; using AnimeFeedManager.Features.Maintenance; using AnimeFeedManager.Features.Migration; @@ -9,18 +10,63 @@ using AnimeFeedManager.Features.State; using AnimeFeedManager.Features.Tv; using AnimeFeedManager.Features.Users; +using AnimeFeedManager.Web.Features.Security; +using Azure.Identity; using MediatR.NotificationPublishers; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server; +using Passwordless.Net; namespace AnimeFeedManager.Web.Bootstrapping; internal static class Registration { - internal static IServiceCollection RegisterAppDependencies(this IServiceCollection services, IConfigurationManager configuration) + internal static void TryAddVault(this IConfigurationManager configuration) + { + if (Uri.TryCreate(configuration["VaultUri"], UriKind.Absolute, out var vaultUri)) + { + configuration.AddAzureKeyVault(vaultUri, new DefaultAzureCredential()); + } + } + + internal static IServiceCollection RegisterSecurityServices(this IServiceCollection services, + IConfigurationManager configuration) + { + services.AddCascadingAuthenticationState(); + services.AddScoped(); + services.AddAuthorization(); + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.LoginPath = "/login"; + options.LogoutPath = "/logout"; + options.ExpireTimeSpan = TimeSpan.FromMinutes(30); + options.Cookie.MaxAge = options.ExpireTimeSpan; + options.SlidingExpiration = true; + }); + + + services.AddAuthorizationBuilder() + .AddPolicy(Policies.AdminRequired, policy => policy.RequireRole(RoleNames.Admin)); + + // bind section Passwordless to the object PassworlessOptions + services.Configure(configuration.GetSection("Passwordless")); + // Add Passworless + services.AddPasswordlessSdk(options => { configuration.GetRequiredSection("Passwordless").Bind(options); }); + + services.AddScoped(); + + return services; + } + + internal static IServiceCollection RegisterAppDependencies(this IServiceCollection services, + IConfigurationManager configuration) { // MediatR services.RegisterMediatR(); // Storage - services.RegisterStorage(configuration.GetConnectionString("AzureStorage") ?? string.Empty); + services.RegisterStorage(configuration); // App services.RegisterSeasonsServices(); services.RegisterImageServices(); @@ -47,5 +93,4 @@ private static IServiceCollection RegisterMediatR(this IServiceCollection servic return services; } - } \ No newline at end of file diff --git a/src/AnimeFeedManager.Web/Program.cs b/src/AnimeFeedManager.Web/Program.cs index bcfa88ee..02ef1aad 100644 --- a/src/AnimeFeedManager.Web/Program.cs +++ b/src/AnimeFeedManager.Web/Program.cs @@ -1,46 +1,22 @@ -using AnimeFeedManager.Common.Domain.Types; using AnimeFeedManager.Web.Bootstrapping; using AnimeFeedManager.Web.Features; using AnimeFeedManager.Web.Features.Common; -using AnimeFeedManager.Web.Features.Security; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Server; using Microsoft.AspNetCore.Components.Web; using TvEndpoints = AnimeFeedManager.Web.Features.Tv.Endpoints; using AdminEndpoints = AnimeFeedManager.Web.Features.Admin.Endpoints; using SecurityEndpoints = AnimeFeedManager.Web.Features.Security.Endpoints; -using Passwordless.Net; var builder = WebApplication.CreateBuilder(args); +builder.Configuration.TryAddVault(); + // Add services to the container. builder.Services.AddRazorComponents(); - builder.Services.AddHttpContextAccessor(); -builder.Services.AddCascadingAuthenticationState(); -builder.Services.AddScoped(); - -builder.Services.AddAuthorization(); -builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(options => - { - options.LoginPath = "/login"; - options.LogoutPath = "/logout"; - }); -builder.Services.AddAuthorizationBuilder() - .AddPolicy(Policies.AdminRequired, policy => policy.RequireRole(RoleNames.Admin)); - -// bind section Passwordless to the object PassworlessOptions -builder.Services.Configure(builder.Configuration.GetSection("Passwordless")); -// Add Passworless -builder.Services.AddPasswordlessSdk(options => -{ - builder.Configuration.GetRequiredSection("Passwordless").Bind(options); -}); +// Register Authentication and Authorization services +builder.Services.RegisterSecurityServices(builder.Configuration); -builder.Services.AddScoped(); // Add the renderer and wrapper to services builder.Services.AddScoped(); @@ -48,6 +24,7 @@ // Application dependencies builder.Services.RegisterAppDependencies(builder.Configuration); +builder.Services.AddApplicationInsightsTelemetry(); var app = builder.Build(); diff --git a/src/AnimeFeedManager.Web/Properties/serviceDependencies.json b/src/AnimeFeedManager.Web/Properties/serviceDependencies.json new file mode 100644 index 00000000..5820ddc3 --- /dev/null +++ b/src/AnimeFeedManager.Web/Properties/serviceDependencies.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "appInsights": { + "type": "appInsights", + "dynamicId": null + } + } +} \ No newline at end of file diff --git a/src/AnimeFeedManager.Web/Properties/serviceDependencies.local.json b/src/AnimeFeedManager.Web/Properties/serviceDependencies.local.json new file mode 100644 index 00000000..0d71519b --- /dev/null +++ b/src/AnimeFeedManager.Web/Properties/serviceDependencies.local.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "appInsights": { + "type": "appInsights.sdk", + "dynamicId": null + } + } +} \ No newline at end of file diff --git a/src/AnimeFeedManager.Web/appsettings.json b/src/AnimeFeedManager.Web/appsettings.json index 10f68b8c..a5e6e59e 100644 --- a/src/AnimeFeedManager.Web/appsettings.json +++ b/src/AnimeFeedManager.Web/appsettings.json @@ -5,5 +5,10 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "Passwordless": { + "Register": { + "Discoverable": true + } + } } diff --git a/src/AnimeFeedManager.Web/package-lock.json b/src/AnimeFeedManager.Web/package-lock.json index 8fcbc865..458c499a 100644 --- a/src/AnimeFeedManager.Web/package-lock.json +++ b/src/AnimeFeedManager.Web/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "devDependencies": { "@tailwindcss/typography": "^0.5.10", - "daisyui": "^4.6.0", + "daisyui": "^4.6.1", "tailwindcss": "^3.4.1" } }, @@ -281,9 +281,9 @@ } }, "node_modules/daisyui": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.6.0.tgz", - "integrity": "sha512-B5ZB/sczXpp4LMdo/SZrtYY/U2hq+Vr9I15QawuWZ0VwgtSAbuZpAZUftKVryEsPuv3BM0yVlBED0nAmtis/dw==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.6.1.tgz", + "integrity": "sha512-IXI8ypN/hkl1AKsag1XPlWt0wfvL4NedTUtUkv/VFP5q/xDbBZrZthq3/9M2yU1egcbbLhp01rluIz0GICUc+g==", "dev": true, "dependencies": { "css-selector-tokenizer": "^0.8", diff --git a/src/AnimeFeedManager.Web/package.json b/src/AnimeFeedManager.Web/package.json index 1f52f091..1f3bae15 100644 --- a/src/AnimeFeedManager.Web/package.json +++ b/src/AnimeFeedManager.Web/package.json @@ -10,7 +10,7 @@ "license": "ISC", "devDependencies": { "@tailwindcss/typography": "^0.5.10", - "daisyui": "^4.6.0", + "daisyui": "^4.6.1", "tailwindcss": "^3.4.1" } } diff --git a/src/AnimeFeedManager.Web/wwwroot/app.css b/src/AnimeFeedManager.Web/wwwroot/app.css index b0315074..4bd47d32 100644 --- a/src/AnimeFeedManager.Web/wwwroot/app.css +++ b/src/AnimeFeedManager.Web/wwwroot/app.css @@ -2388,6 +2388,7 @@ html { border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); --tw-bg-opacity: 1; background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); --tw-text-opacity: 0.2; } diff --git a/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthMemoryStorage.cs b/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthMemoryStorage.cs index e6b746f2..c4182829 100644 --- a/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthMemoryStorage.cs +++ b/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthMemoryStorage.cs @@ -6,7 +6,7 @@ namespace AnimeFeedManager.WebApp.Authentication; // A simple in-memory storage model for caching auth data -class AppServiceAuthMemoryStorage +internal class AppServiceAuthMemoryStorage { public AuthenticationData AuthenticationData { get; private set; } diff --git a/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticationService.cs b/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticationService.cs index 8e4e9996..ae6c1f0f 100644 --- a/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticationService.cs +++ b/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticationService.cs @@ -13,7 +13,7 @@ namespace AnimeFeedManager.WebApp.Authentication; -class AppServiceAuthRemoteAuthenticationService( +internal class AppServiceAuthRemoteAuthenticationService( IOptions> options, NavigationManager navigationManager, IJSRuntime jsRuntime, @@ -21,8 +21,8 @@ class AppServiceAuthRemoteAuthenticationService( : AuthenticationStateProvider, IRemoteAuthenticationService where TAuthenticationState : RemoteAuthenticationState { - const string BrowserStorageType = "sessionStorage"; - const string StorageKeyPrefix = "Blazor.AppServiceAuth"; + private const string BrowserStorageType = "sessionStorage"; + private const string StorageKeyPrefix = "Blazor.AppServiceAuth"; public RemoteAuthenticationOptions Options { get; } = options.Value; public HttpClient HttpClient { get; } = new() { BaseAddress = new Uri(navigationManager.BaseUri) }; @@ -105,7 +105,7 @@ public Task> SignOutAsync(Remot return Task.FromResult(new RemoteAuthenticationResult { Status = RemoteAuthenticationStatus.Redirect }); } - string BuildRedirectUri(string path) + private string BuildRedirectUri(string path) { return new Uri(new Uri(Navigation.BaseUri), path).ToString(); } diff --git a/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticatorView.cs b/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticatorView.cs index 6daf977b..18a7e7eb 100644 --- a/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticatorView.cs +++ b/src/AnimeFeedManager.WebApp.Authentication/AppServiceAuthRemoteAuthenticatorView.cs @@ -18,15 +18,15 @@ public class AppServiceAuthRemoteAuthenticatorViewCore : RemoteAuthenticatorViewCore< TAuthenticationState> where TAuthenticationState : RemoteAuthenticationState { - string _message; + private string _message; [Parameter] public string SelectedOption { get; set; } - [Inject] NavigationManager Navigation { get; set; } + [Inject] private NavigationManager Navigation { get; set; } - [Inject] IJSRuntime Js { get; set; } + [Inject] private IJSRuntime Js { get; set; } - [Inject] IRemoteAuthenticationService AuthenticationService { get; set; } + [Inject] private IRemoteAuthenticationService AuthenticationService { get; set; } protected override async Task OnParametersSetAsync() { @@ -88,7 +88,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) } } - async Task ProcessLogin(string returnUrl) + private async Task ProcessLogin(string returnUrl) { AuthenticationState.ReturnUrl = returnUrl; RemoteAuthenticationResult result = @@ -118,12 +118,12 @@ await AuthenticationService.SignInAsync( } } - ValueTask NavigateToReturnUrl(string returnUrl) + private ValueTask NavigateToReturnUrl(string returnUrl) { return Js.InvokeVoidAsync("Blazor.navigateTo", returnUrl, false, true); } - string GetReturnUrl(RemoteAuthenticationState state, string defaultReturnUrl = null) + private string GetReturnUrl(RemoteAuthenticationState state, string defaultReturnUrl = null) { if (state?.ReturnUrl != null) { diff --git a/src/AnimeFeedManager.WebApp.Authentication/StaticWebAppsAuthenticationStateProvider.cs b/src/AnimeFeedManager.WebApp.Authentication/StaticWebAppsAuthenticationStateProvider.cs index d8738953..1f487ec0 100644 --- a/src/AnimeFeedManager.WebApp.Authentication/StaticWebAppsAuthenticationStateProvider.cs +++ b/src/AnimeFeedManager.WebApp.Authentication/StaticWebAppsAuthenticationStateProvider.cs @@ -13,7 +13,7 @@ namespace AnimeFeedManager.WebApp.Authentication; public class StaticWebAppsAuthenticationStateProvider(IConfiguration config, IWebAssemblyHostEnvironment environment) : AuthenticationStateProvider { - readonly HttpClient _http = new() { BaseAddress = new Uri(environment.BaseAddress) }; + private readonly HttpClient _http = new() { BaseAddress = new Uri(environment.BaseAddress) }; public override async Task GetAuthenticationStateAsync() { diff --git a/src/AnimeFeedManager.WebApp/AnimeFeedManager.WebApp.csproj b/src/AnimeFeedManager.WebApp/AnimeFeedManager.WebApp.csproj index e9dd0a4c..e00618d0 100644 --- a/src/AnimeFeedManager.WebApp/AnimeFeedManager.WebApp.csproj +++ b/src/AnimeFeedManager.WebApp/AnimeFeedManager.WebApp.csproj @@ -16,7 +16,7 @@ - +