feat(auth): replace TOTP with phone-first login

This commit is contained in:
2026-07-28 17:39:29 +08:00
parent e7d350ec3d
commit c7f9a4e3c9
43 changed files with 18386 additions and 882 deletions

View File

@@ -150,7 +150,7 @@ public sealed class AuthSessionLifecycleTests
}
[Fact]
public async Task Backend_session_and_refresh_fail_immediately_after_the_last_permission_is_revoked()
public async Task Tenant_session_remains_valid_after_a_backend_permission_is_revoked()
{
await using var factory = new ApiTestFactory();
var seed = await SeedActiveMemberAsync(factory);
@@ -182,7 +182,7 @@ public sealed class AuthSessionLifecycleTests
UserId = seed.UserId,
RoleId = role.Id
});
var tokens = await IssueAsync(factory, seed, mfaSatisfied: true);
var tokens = await IssueAsync(factory, seed);
Assert.True(TryLocate(factory, tokens.RefreshToken, out var locator));
using (var scope = factory.CreateSystemScope("Revoke final backend permission"))
@@ -194,21 +194,12 @@ public sealed class AuthSessionLifecycleTests
await dbContext.SaveChangesAsync();
}
using (var scope = factory.CreateSystemScope("Validate revoked backend session"))
using (var scope = factory.CreateSystemScope("Validate tenant session"))
{
var store = scope.ServiceProvider.GetRequiredService<IAuthSessionStore>();
Assert.Null(await store.ValidateAccessSessionAsync(
Assert.NotNull(await store.ValidateAccessSessionAsync(
locator.SessionId, seed.UserId, AuthRealm.Tenant, seed.TenantId));
await Assert.ThrowsAsync<SessionRevokedException>(() =>
store.RotateAsync(tokens.RefreshToken, null, null));
}
using (var scope = factory.CreateSystemScope("Verify revoked backend family"))
{
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
var session = await dbContext.AuthSessions.SingleAsync(item => item.Id == locator.SessionId);
Assert.NotNull(session.RevokedAt);
Assert.Equal("realm_access_revoked", session.RevokedReason);
Assert.NotNull(await store.RotateAsync(tokens.RefreshToken, null, null));
}
}
@@ -236,8 +227,7 @@ public sealed class AuthSessionLifecycleTests
private static async Task<AuthTokenPair> IssueAsync(
ApiTestFactory factory,
SessionSeed seed,
bool mfaSatisfied = false)
SessionSeed seed)
{
using var scope = factory.CreateSystemScope("Issue authentication session");
return await scope.ServiceProvider.GetRequiredService<IAuthSessionStore>().IssueAsync(
@@ -249,7 +239,6 @@ public sealed class AuthSessionLifecycleTests
AuthRealm.Tenant,
seed.TenantId,
"integration-test",
mfaSatisfied,
"127.0.0.1",
"integration-test"));
}