172 lines
5.0 KiB
C#
172 lines
5.0 KiB
C#
using System.Text;
|
|
using JWT.Algorithms;
|
|
using JWT.Builder;
|
|
using JWT.Exceptions;
|
|
using Logging.Net;
|
|
using Moonlight.App.Database.Entities;
|
|
using Moonlight.App.Helpers;
|
|
using Moonlight.App.Models.Misc;
|
|
using Moonlight.App.Repositories;
|
|
using Moonlight.App.Services.LogServices;
|
|
using UAParser;
|
|
|
|
namespace Moonlight.App.Services.Sessions;
|
|
|
|
public class IdentityService
|
|
{
|
|
private readonly UserRepository UserRepository;
|
|
private readonly CookieService CookieService;
|
|
private readonly SecurityLogService SecurityLogService;
|
|
private readonly ErrorLogService ErrorLogService;
|
|
private readonly IHttpContextAccessor HttpContextAccessor;
|
|
private readonly string Secret;
|
|
|
|
private User? UserCache;
|
|
|
|
public IdentityService(
|
|
CookieService cookieService,
|
|
UserRepository userRepository,
|
|
IHttpContextAccessor httpContextAccessor,
|
|
ConfigService configService,
|
|
SecurityLogService securityLogService,
|
|
ErrorLogService errorLogService)
|
|
{
|
|
CookieService = cookieService;
|
|
UserRepository = userRepository;
|
|
HttpContextAccessor = httpContextAccessor;
|
|
SecurityLogService = securityLogService;
|
|
ErrorLogService = errorLogService;
|
|
|
|
Secret = configService
|
|
.GetSection("Moonlight")
|
|
.GetSection("Security")
|
|
.GetValue<string>("Token");
|
|
}
|
|
|
|
public async Task<User?> Get()
|
|
{
|
|
try
|
|
{
|
|
if (UserCache != null)
|
|
return UserCache;
|
|
|
|
var token = "none";
|
|
|
|
// Load token via http context if available
|
|
if (HttpContextAccessor.HttpContext != null)
|
|
{
|
|
var request = HttpContextAccessor.HttpContext.Request;
|
|
|
|
if (request.Cookies.ContainsKey("token"))
|
|
{
|
|
token = request.Cookies["token"];
|
|
}
|
|
}
|
|
else // if not we check the cookies manually via js. this may not often work
|
|
{
|
|
token = await CookieService.GetValue("token", "none");
|
|
}
|
|
|
|
if (token == "none")
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(token))
|
|
return null;
|
|
|
|
var json = "";
|
|
|
|
try
|
|
{
|
|
json = JwtBuilder.Create()
|
|
.WithAlgorithm(new HMACSHA256Algorithm())
|
|
.WithSecret(Secret)
|
|
.Decode(token);
|
|
}
|
|
catch (TokenExpiredException)
|
|
{
|
|
return null;
|
|
}
|
|
catch (SignatureVerificationException)
|
|
{
|
|
await SecurityLogService.Log(SecurityLogType.ManipulatedJwt, x =>
|
|
{
|
|
x.Add<string>(token);
|
|
});
|
|
return null;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
await ErrorLogService.Log(e, x => {});
|
|
return null;
|
|
}
|
|
|
|
// To make it easier to use the json data
|
|
var data = new ConfigurationBuilder().AddJsonStream(
|
|
new MemoryStream(Encoding.ASCII.GetBytes(json))
|
|
).Build();
|
|
|
|
var userid = data.GetValue<int>("userid");
|
|
var user = UserRepository.Get().FirstOrDefault(y => y.Id == userid);
|
|
|
|
if (user == null)
|
|
{
|
|
Logger.Warn($"Cannot find user with the id '{userid}' in the database. Maybe the user has been deleted or a token has been successfully faked by a hacker");
|
|
return null;
|
|
}
|
|
|
|
var iat = data.GetValue<long>("iat", -1);
|
|
|
|
if (iat == -1)
|
|
{
|
|
Logger.Debug("Legacy token found (without the time the token has been issued at)");
|
|
return null;
|
|
}
|
|
|
|
var iatD = DateTimeOffset.FromUnixTimeSeconds(iat).ToUniversalTime().DateTime;
|
|
|
|
if (iatD < user.TokenValidTime)
|
|
return null;
|
|
|
|
UserCache = user;
|
|
return UserCache;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
await ErrorLogService.Log(e, x => {});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public string GetIp()
|
|
{
|
|
if (HttpContextAccessor.HttpContext == null)
|
|
return "N/A";
|
|
|
|
if(HttpContextAccessor.HttpContext.Request.Headers.ContainsKey("X-Real-IP"))
|
|
{
|
|
return HttpContextAccessor.HttpContext.Request.Headers["X-Real-IP"]!;
|
|
}
|
|
|
|
return HttpContextAccessor.HttpContext.Connection.RemoteIpAddress!.ToString();
|
|
}
|
|
|
|
public string GetDevice()
|
|
{
|
|
if (HttpContextAccessor.HttpContext == null)
|
|
return "N/A";
|
|
|
|
try
|
|
{
|
|
var uaParser = Parser.GetDefault();
|
|
var info = uaParser.Parse(HttpContextAccessor.HttpContext.Request.Headers.UserAgent);
|
|
|
|
return $"{info.OS} - {info.Device}";
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
return "UserAgent not present";
|
|
}
|
|
}
|
|
} |