start (actual) email auth, add CancellationToken to most async methods

This commit is contained in:
sam 2024-09-09 14:37:59 +02:00
parent acc54a55bc
commit 344a0071e5
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
22 changed files with 325 additions and 152 deletions

View file

@ -14,12 +14,12 @@ public static class AvatarObjectExtensions
private static readonly string[] ValidContentTypes = ["image/png", "image/webp", "image/jpeg"];
public static async Task
DeleteMemberAvatarAsync(this ObjectStorageService objectStorage, Snowflake id, string hash) =>
await objectStorage.RemoveObjectAsync(MemberAvatarUpdateInvocable.Path(id, hash));
DeleteMemberAvatarAsync(this ObjectStorageService objectStorage, Snowflake id, string hash, CancellationToken ct = default) =>
await objectStorage.RemoveObjectAsync(MemberAvatarUpdateInvocable.Path(id, hash), ct);
public static async Task
DeleteUserAvatarAsync(this ObjectStorageService objectStorage, Snowflake id, string hash) =>
await objectStorage.RemoveObjectAsync(UserAvatarUpdateInvocable.Path(id, hash));
DeleteUserAvatarAsync(this ObjectStorageService objectStorage, Snowflake id, string hash, CancellationToken ct = default) =>
await objectStorage.RemoveObjectAsync(UserAvatarUpdateInvocable.Path(id, hash), ct);
public static async Task<Stream> ConvertBase64UriToAvatar(this string uri)
{

View file

@ -1,3 +1,4 @@
using Foxnouns.Backend.Database;
using Foxnouns.Backend.Services;
using Foxnouns.Backend.Utils;
using NodaTime;
@ -6,16 +7,31 @@ namespace Foxnouns.Backend.Extensions;
public static class KeyCacheExtensions
{
public static async Task<string> GenerateAuthStateAsync(this KeyCacheService keyCacheSvc)
public static async Task<string> GenerateAuthStateAsync(this KeyCacheService keyCacheSvc, CancellationToken ct = default)
{
var state = AuthUtils.RandomToken();
await keyCacheSvc.SetKeyAsync($"oauth_state:{state}", "", Duration.FromMinutes(10));
await keyCacheSvc.SetKeyAsync($"oauth_state:{state}", "", Duration.FromMinutes(10), ct);
return state;
}
public static async Task ValidateAuthStateAsync(this KeyCacheService keyCacheSvc, string state)
public static async Task ValidateAuthStateAsync(this KeyCacheService keyCacheSvc, string state, CancellationToken ct = default)
{
var val = await keyCacheSvc.GetKeyAsync($"oauth_state:{state}", delete: true);
var val = await keyCacheSvc.GetKeyAsync($"oauth_state:{state}", delete: true, ct);
if (val == null) throw new ApiError.BadRequest("Invalid OAuth state");
}
}
public static async Task<string> GenerateRegisterEmailStateAsync(this KeyCacheService keyCacheSvc, string email,
Snowflake? userId = null, CancellationToken ct = default)
{
var state = AuthUtils.RandomToken();
await keyCacheSvc.SetKeyAsync($"email_state:{state}", new RegisterEmailState(email, userId),
Duration.FromDays(1), ct);
return state;
}
public static async Task<RegisterEmailState?> GetRegisterEmailStateAsync(this KeyCacheService keyCacheSvc,
string state, CancellationToken ct = default) =>
await keyCacheSvc.GetKeyAsync<RegisterEmailState>($"email_state:{state}", delete: true, ct);
}
public record RegisterEmailState(string Email, Snowflake? ExistingUserId);

View file

@ -70,38 +70,43 @@ public static class WebApplicationExtensions
}
/// <summary>
/// Adds required services to the IServiceCollection.
/// Adds required services to the WebApplicationBuilder.
/// This should only add services that are not ASP.NET-related (i.e. no middleware).
/// </summary>
public static IServiceCollection AddServices(this IServiceCollection services, Config config)
public static IServiceCollection AddServices(this WebApplicationBuilder builder, Config config)
{
services
.AddQueue()
.AddDbContext<DatabaseContext>()
.AddMetricServer(o => o.Port = config.Logging.MetricsPort)
.AddMinio(c =>
c.WithEndpoint(config.Storage.Endpoint)
.WithCredentials(config.Storage.AccessKey, config.Storage.SecretKey)
.Build())
.AddSingleton<MetricsCollectionService>()
.AddSingleton<IClock>(SystemClock.Instance)
.AddSnowflakeGenerator()
.AddScoped<UserRendererService>()
.AddScoped<MemberRendererService>()
.AddScoped<AuthService>()
.AddScoped<KeyCacheService>()
.AddScoped<RemoteAuthService>()
.AddScoped<ObjectStorageService>()
// Background services
.AddHostedService<PeriodicTasksService>()
// Transient jobs
.AddTransient<MemberAvatarUpdateInvocable>()
.AddTransient<UserAvatarUpdateInvocable>();
if (!config.Logging.EnableMetrics)
services.AddHostedService<BackgroundMetricsCollectionService>();
return services;
builder.Host.ConfigureServices((ctx, services) =>
{
services
.AddQueue()
.AddMailer(ctx.Configuration)
.AddDbContext<DatabaseContext>()
.AddMetricServer(o => o.Port = config.Logging.MetricsPort)
.AddMinio(c =>
c.WithEndpoint(config.Storage.Endpoint)
.WithCredentials(config.Storage.AccessKey, config.Storage.SecretKey)
.Build())
.AddSingleton<MetricsCollectionService>()
.AddSingleton<IClock>(SystemClock.Instance)
.AddSnowflakeGenerator()
.AddSingleton<MailService>()
.AddScoped<UserRendererService>()
.AddScoped<MemberRendererService>()
.AddScoped<AuthService>()
.AddScoped<KeyCacheService>()
.AddScoped<RemoteAuthService>()
.AddScoped<ObjectStorageService>()
// Background services
.AddHostedService<PeriodicTasksService>()
// Transient jobs
.AddTransient<MemberAvatarUpdateInvocable>()
.AddTransient<UserAvatarUpdateInvocable>();
if (!config.Logging.EnableMetrics)
services.AddHostedService<BackgroundMetricsCollectionService>();
});
return builder.Services;
}
public static IServiceCollection AddCustomMiddleware(this IServiceCollection services) => services