Compare commits
No commits in common. "25b6c7d69148c04d9a37abc4250fdceb6a2bc31a" and "470031af39fb36c4068a36d87b2f46822243e27a" have entirely different histories.
25b6c7d691
...
470031af39
@ -1,13 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Mirea.Api.Security.Common.Domain;
|
||||
|
||||
public class AuthToken
|
||||
{
|
||||
public required string RefreshToken { get; set; }
|
||||
public required string UserAgent { get; set; }
|
||||
public required string Ip { get; set; }
|
||||
public required string UserId { get; set; }
|
||||
public required string AccessToken { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Mirea.Api.Security.Common.Dto.Responses;
|
||||
|
||||
public class AuthTokenResponse
|
||||
{
|
||||
public required string AccessToken { get; set; }
|
||||
public required string RefreshToken { get; set; }
|
||||
public DateTime ExpiresIn { get; set; }
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Mirea.Api.Security.Common.Interfaces;
|
||||
|
||||
public interface IAccessToken
|
||||
{
|
||||
(string Token, DateTime ExpireIn) GenerateToken(string userId);
|
||||
DateTimeOffset GetExpireDateTime(string token);
|
||||
}
|
@ -6,11 +6,7 @@ namespace Mirea.Api.Security.Common.Interfaces;
|
||||
|
||||
public interface ICacheService
|
||||
{
|
||||
Task SetAsync<T>(string key, T value,
|
||||
TimeSpan? absoluteExpirationRelativeToNow = null,
|
||||
TimeSpan? slidingExpiration = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpirationRelativeToNow = null, CancellationToken cancellationToken = default);
|
||||
Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default);
|
||||
Task RemoveAsync(string key, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mirea.Api.Security.Common.Interfaces;
|
||||
|
||||
public interface IRevokedToken
|
||||
{
|
||||
Task AddTokenToRevokedAsync(string token, DateTimeOffset expiresIn);
|
||||
Task<bool> IsTokenRevokedAsync(string token);
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
using Mirea.Api.Security.Common.Domain;
|
||||
using Mirea.Api.Security.Common.Dto.Requests;
|
||||
using Mirea.Api.Security.Common.Dto.Responses;
|
||||
using Mirea.Api.Security.Common.Interfaces;
|
||||
using System;
|
||||
using System.Security;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mirea.Api.Security.Services;
|
||||
|
||||
public class AuthService(ICacheService cache, IAccessToken accessTokenService, IRevokedToken revokedToken)
|
||||
{
|
||||
public TimeSpan Lifetime { private get; init; }
|
||||
|
||||
private static string GenerateRefreshToken() => Guid.NewGuid().ToString().Replace("-", "") +
|
||||
GeneratorKey.GenerateString(32);
|
||||
private (string Token, DateTime ExpireIn) GenerateAccessToken(string userId) =>
|
||||
accessTokenService.GenerateToken(userId);
|
||||
|
||||
private static string GetAuthCacheKey(string fingerprint) => $"{fingerprint}_auth_token";
|
||||
|
||||
private Task SetAuthTokenDataToCache(string fingerprint, AuthToken data, CancellationToken cancellation) =>
|
||||
cache.SetAsync(
|
||||
GetAuthCacheKey(fingerprint),
|
||||
JsonSerializer.SerializeToUtf8Bytes(data),
|
||||
slidingExpiration: Lifetime,
|
||||
cancellationToken: cancellation);
|
||||
|
||||
private Task RevokeAccessToken(string token) =>
|
||||
revokedToken.AddTokenToRevokedAsync(token, accessTokenService.GetExpireDateTime(token));
|
||||
|
||||
public async Task<AuthTokenResponse> GenerateAuthTokensAsync(TokenRequest request, string userId, CancellationToken cancellation = default)
|
||||
{
|
||||
var refreshToken = GenerateRefreshToken();
|
||||
var accessToken = GenerateAccessToken(userId);
|
||||
|
||||
var authTokenStruct = new AuthToken
|
||||
{
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Ip = request.Ip,
|
||||
RefreshToken = refreshToken,
|
||||
UserAgent = request.UserAgent,
|
||||
UserId = userId,
|
||||
AccessToken = accessToken.Token
|
||||
};
|
||||
|
||||
await SetAuthTokenDataToCache(request.Fingerprint, authTokenStruct, cancellation);
|
||||
|
||||
return new AuthTokenResponse
|
||||
{
|
||||
AccessToken = accessToken.Token,
|
||||
ExpiresIn = accessToken.ExpireIn,
|
||||
RefreshToken = authTokenStruct.RefreshToken
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<AuthTokenResponse> GenerateAuthTokensWithPreAuthAsync(TokenRequest request, string preAuthToken,
|
||||
CancellationToken cancellation = default) =>
|
||||
await GenerateAuthTokensAsync(request,
|
||||
await new PreAuthService(cache).MatchToken(request, preAuthToken, cancellation),
|
||||
cancellation);
|
||||
|
||||
public async Task<AuthTokenResponse> RefreshTokenAsync(TokenRequest request, string refreshToken, CancellationToken cancellation = default)
|
||||
{
|
||||
var authToken = await cache.GetAsync<AuthToken>(GetAuthCacheKey(request.Fingerprint), cancellation)
|
||||
?? throw new SecurityException(request.Fingerprint);
|
||||
|
||||
if (authToken.RefreshToken != refreshToken ||
|
||||
authToken.UserAgent != request.UserAgent &&
|
||||
authToken.Ip != request.Ip)
|
||||
{
|
||||
await cache.RemoveAsync(request.Fingerprint, cancellation);
|
||||
await RevokeAccessToken(authToken.AccessToken);
|
||||
|
||||
throw new SecurityException(request.Fingerprint);
|
||||
}
|
||||
|
||||
var accessToken = GenerateAccessToken(authToken.UserId);
|
||||
await RevokeAccessToken(authToken.AccessToken);
|
||||
|
||||
authToken.AccessToken = accessToken.Token;
|
||||
await SetAuthTokenDataToCache(request.Fingerprint, authToken, cancellation);
|
||||
|
||||
return new AuthTokenResponse
|
||||
{
|
||||
AccessToken = accessToken.Token,
|
||||
ExpiresIn = accessToken.ExpireIn,
|
||||
RefreshToken = GenerateRefreshToken()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task LogoutAsync(string fingerprint, CancellationToken cancellation = default)
|
||||
{
|
||||
var authTokenStruct = await cache.GetAsync<AuthToken>(GetAuthCacheKey(fingerprint), cancellation);
|
||||
if (authTokenStruct == null) return;
|
||||
|
||||
await RevokeAccessToken(authTokenStruct.AccessToken);
|
||||
|
||||
await cache.RemoveAsync(fingerprint, cancellation);
|
||||
}
|
||||
}
|
@ -35,8 +35,8 @@ public class PreAuthService(ICacheService cache)
|
||||
await cache.SetAsync(
|
||||
GetPreAuthCacheKey(request.Fingerprint),
|
||||
JsonSerializer.SerializeToUtf8Bytes(preAuthTokenStruct),
|
||||
absoluteExpirationRelativeToNow: Lifetime,
|
||||
cancellationToken: cancellation);
|
||||
Lifetime,
|
||||
cancellation);
|
||||
|
||||
return new PreAuthTokenResponse
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user