Invitation process improvements (#6188)

* Server: Make sending email optional when adding user

Closes #6158.

* Generate custom invite token and store it in user blob

Closes btcpayserver/app/#46.

* QR code for user invite

Closes #6157.

* Text fix
This commit is contained in:
d11n
2024-09-12 05:31:57 +02:00
committed by GitHub
parent 3342122be2
commit f3d485da53
11 changed files with 134 additions and 34 deletions

View File

@@ -1,5 +1,7 @@
#nullable enable
using System;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Security;
using Microsoft.AspNetCore.Identity;
@@ -19,15 +21,35 @@ namespace BTCPayServer
return await userManager.FindByIdAsync(idOrEmail);
}
public static async Task<string> GenerateInvitationTokenAsync<TUser>(this UserManager<TUser> userManager, TUser user) where TUser : class
public static async Task<string?> GenerateInvitationTokenAsync<TUser>(this UserManager<ApplicationUser> userManager, string userId) where TUser : class
{
return await userManager.GenerateUserTokenAsync(user, InvitationTokenProviderOptions.ProviderName, InvitationPurpose);
var token = Guid.NewGuid().ToString("n")[..12];
return await userManager.SetInvitationTokenAsync<TUser>(userId, token) ? token : null;
}
public static async Task<TUser?> FindByInvitationTokenAsync<TUser>(this UserManager<TUser> userManager, string userId, string token) where TUser : class
public static async Task<bool> UnsetInvitationTokenAsync<TUser>(this UserManager<ApplicationUser> userManager, string userId) where TUser : class
{
return await userManager.SetInvitationTokenAsync<TUser>(userId, null);
}
private static async Task<bool> SetInvitationTokenAsync<TUser>(this UserManager<ApplicationUser> userManager, string userId, string? token) where TUser : class
{
var user = await userManager.FindByIdAsync(userId);
var isValid = user is not null && await userManager.VerifyUserTokenAsync(user, InvitationTokenProviderOptions.ProviderName, InvitationPurpose, token);
if (user == null) return false;
var blob = user.GetBlob() ?? new UserBlob();
blob.InvitationToken = token;
user.SetBlob(blob);
await userManager.UpdateAsync(user);
return true;
}
public static async Task<ApplicationUser?> FindByInvitationTokenAsync<TUser>(this UserManager<ApplicationUser> userManager, string userId, string token) where TUser : class
{
var user = await userManager.FindByIdAsync(userId);
var isValid = user is not null && (
user.GetBlob()?.InvitationToken == token ||
// backwards-compatibility with old tokens
await userManager.VerifyUserTokenAsync(user, InvitationTokenProviderOptions.ProviderName, InvitationPurpose, token));
return isValid ? user : null;
}
}