Files
tiku-backend.net/Tiku.Infrastructure/Auth/SessionService.cs

91 lines
2.8 KiB
C#

using System.Security.Cryptography;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Tiku.Application.Auth;
using Tiku.Application.Security;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
namespace Tiku.Infrastructure.Auth;
public sealed class SessionService(
TikuDbContext dbContext,
ITokenService tokenService,
IOptions<JwtOptions> options) : ISessionService
{
private readonly JwtOptions options = options.Value;
public string GenerateRefreshToken(Guid tenantId, Guid sessionId)
{
return $"v1.{tenantId:N}.{sessionId:N}.{Base64UrlEncoder.Encode(RandomNumberGenerator.GetBytes(64))}";
}
public bool TryParseRefreshToken(string refreshToken, out RefreshTokenLocator locator)
{
locator = default;
if (string.IsNullOrWhiteSpace(refreshToken))
{
return false;
}
var parts = refreshToken.Split('.', 4, StringSplitOptions.None);
if (parts.Length != 4 || parts[0] != "v1" || parts[3].Length < 32 ||
!Guid.TryParseExact(parts[1], "N", out var tenantId) ||
!Guid.TryParseExact(parts[2], "N", out var sessionId))
{
return false;
}
locator = new RefreshTokenLocator(tenantId, sessionId);
return true;
}
public string HashRefreshToken(string refreshToken)
{
var hash = SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(refreshToken));
return Convert.ToHexString(hash).ToLowerInvariant();
}
public async Task<AuthTokenPair> IssueAsync(
Guid userId,
string? phone,
string? email,
TenantMembership membership,
string provider,
string? ipAddress,
string? userAgent,
CancellationToken cancellationToken = default)
{
var session = new AuthSession
{
Id = Guid.NewGuid(),
TenantId = membership.TenantId,
UserId = userId,
TokenHash = string.Empty,
Provider = provider,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(options.RefreshTokenDays),
IpAddress = ipAddress,
UserAgent = userAgent
};
var refreshToken = GenerateRefreshToken(session.TenantId, session.Id);
session.TokenHash = HashRefreshToken(refreshToken);
dbContext.AuthSessions.Add(session);
await dbContext.SaveChangesAsync(cancellationToken);
var accessToken = tokenService.CreateAccessToken(
userId,
session.Id,
phone,
email,
membership);
return new AuthTokenPair(
accessToken.Token,
refreshToken,
accessToken.ExpiresAt,
session.ExpiresAt);
}
}