Why: provide service-side canonical login/refresh orchestration for session-based web auth. What: add session contracts, refresh token codec with provider-agnostic secret boundary, grpc session methods, DI wiring, tests, and docs. Rule: preserve thalos identity ownership and keep transport adapters at service edge.
79 lines
3.1 KiB
C#
79 lines
3.1 KiB
C#
using BuildingBlock.Identity.Contracts.Conventions;
|
|
using BuildingBlock.Identity.Contracts.Requests;
|
|
using BuildingBlock.Identity.Contracts.Responses;
|
|
using Thalos.Service.Application.Sessions;
|
|
using Thalos.Service.Application.UseCases;
|
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
|
using IdentityIssueRequest = BuildingBlock.Identity.Contracts.Requests.IssueIdentityTokenRequest;
|
|
using IdentityIssueResponse = BuildingBlock.Identity.Contracts.Responses.IssueIdentityTokenResponse;
|
|
using SessionRefreshRequest = Thalos.Service.Identity.Abstractions.Contracts.RefreshIdentitySessionRequest;
|
|
|
|
namespace Thalos.Service.Application.UnitTests;
|
|
|
|
public class RefreshIdentitySessionUseCaseTests
|
|
{
|
|
[Fact]
|
|
public async Task HandleAsync_WhenRefreshTokenValid_ReissuesSessionTokens()
|
|
{
|
|
var useCase = new RefreshIdentitySessionUseCase(new FakeIssueUseCase(), new FakeSessionTokenCodec());
|
|
|
|
var response = await useCase.HandleAsync(new SessionRefreshRequest("refresh-token", "corr-1", IdentityAuthProvider.Google));
|
|
|
|
Assert.Equal("token-new", response.AccessToken);
|
|
Assert.Equal(3000, response.ExpiresInSeconds);
|
|
Assert.Equal("google-sub-1", response.SubjectId);
|
|
Assert.Equal("tenant-2", response.TenantId);
|
|
Assert.Equal("refresh-google-sub-1-tenant-2", response.RefreshToken);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HandleAsync_WhenRefreshTokenInvalid_ReturnsEmptyPayload()
|
|
{
|
|
var useCase = new RefreshIdentitySessionUseCase(new FakeIssueUseCase(), new InvalidSessionTokenCodec());
|
|
|
|
var response = await useCase.HandleAsync(new SessionRefreshRequest("bad-token", "corr-2"));
|
|
|
|
Assert.Equal(string.Empty, response.AccessToken);
|
|
Assert.Equal(0, response.ExpiresInSeconds);
|
|
Assert.Equal(string.Empty, response.RefreshToken);
|
|
}
|
|
|
|
private sealed class FakeIssueUseCase : IIssueIdentityTokenUseCase
|
|
{
|
|
public Task<IdentityIssueResponse> HandleAsync(IdentityIssueRequest request)
|
|
{
|
|
return Task.FromResult(new IdentityIssueResponse("token-new", 3000));
|
|
}
|
|
}
|
|
|
|
private sealed class FakeSessionTokenCodec : IIdentitySessionTokenCodec
|
|
{
|
|
public string Encode(IdentitySessionDescriptor descriptor)
|
|
{
|
|
return $"refresh-{descriptor.SubjectId}-{descriptor.TenantId}";
|
|
}
|
|
|
|
public bool TryDecode(string token, out IdentitySessionDescriptor descriptor)
|
|
{
|
|
descriptor = new IdentitySessionDescriptor(
|
|
"google-sub-1",
|
|
"tenant-2",
|
|
IdentityAuthProvider.Google,
|
|
DateTimeOffset.UtcNow.AddHours(1));
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private sealed class InvalidSessionTokenCodec : IIdentitySessionTokenCodec
|
|
{
|
|
public string Encode(IdentitySessionDescriptor descriptor) => string.Empty;
|
|
|
|
public bool TryDecode(string token, out IdentitySessionDescriptor descriptor)
|
|
{
|
|
descriptor = new IdentitySessionDescriptor(string.Empty, string.Empty, IdentityAuthProvider.InternalJwt, DateTimeOffset.MinValue);
|
|
return false;
|
|
}
|
|
}
|
|
}
|