//-----------------------------------------------------------------------
//
// Copyright (c) lanedirt. All rights reserved.
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
//
//-----------------------------------------------------------------------
namespace AliasVault.Api.Controllers.Security;
using AliasServerDb;
using AliasVault.Api.Controllers.Abstracts;
using AliasVault.Shared.Models.Enums;
using AliasVault.Shared.Models.WebApi.Security;
using Asp.Versioning;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
///
/// Security controller for handling security related actions such as showing auth logs or revoking sessions for user.
///
/// AliasServerDbContext instance.
/// UserManager instance.
[Route("v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("1")]
public class SecurityController(IAliasServerDbContextFactory dbContextFactory, UserManager userManager) : AuthenticatedRequestController(userManager)
{
///
/// Returns list of active sessions (refresh tokens) for the current user.
///
/// Task with list of active refresh tokens.
[HttpGet("sessions")]
public async Task Sessions()
{
var user = await GetCurrentUserAsync();
if (user is null)
{
return Unauthorized("Not authenticated.");
}
await using var context = await dbContextFactory.CreateDbContextAsync();
var refreshTokenList = await context.AliasVaultUserRefreshTokens.Where(x => x.UserId == user.Id).Select(x => new RefreshTokenModel()
{
Id = x.Id,
DeviceIdentifier = x.DeviceIdentifier,
ExpireDate = x.ExpireDate,
CreatedAt = x.CreatedAt,
})
.Where(x => x.ExpireDate > DateTime.UtcNow)
.OrderByDescending(x => x.CreatedAt)
.ToListAsync();
return Ok(refreshTokenList);
}
///
/// Revokes a specific user session (refresh token).
///
/// The ID of the refresh token to revoke.
/// Http200 if success.
[HttpDelete("sessions/{id}")]
public async Task RevokeSession(Guid id)
{
var user = await GetCurrentUserAsync();
if (user is null)
{
return Unauthorized("Not authenticated.");
}
await using var context = await dbContextFactory.CreateDbContextAsync();
var refreshToken = await context.AliasVaultUserRefreshTokens
.FirstOrDefaultAsync(x => x.Id == id && x.UserId == user.Id);
if (refreshToken == null)
{
return NotFound("Session not found or does not belong to the current user.");
}
context.AliasVaultUserRefreshTokens.Remove(refreshToken);
await context.SaveChangesAsync();
return Ok();
}
///
/// Returns the last 50 authentication logs for the current user.
///
/// Task with a list of the last 50 authentication logs.
[HttpGet("authlogs")]
public async Task GetUserAuthLogs()
{
var user = await GetCurrentUserAsync();
if (user is null)
{
return Unauthorized("Not authenticated.");
}
await using var context = await dbContextFactory.CreateDbContextAsync();
var authLogs = await context.AuthLogs
.Where(x => x.Username == user.UserName)
.Where(x => x.EventType != AuthEventType.TokenRefresh)
.OrderByDescending(x => x.Timestamp)
.Take(50)
.Select(x => new AuthLogModel
{
Id = x.Id,
Timestamp = x.Timestamp,
EventType = x.EventType,
Username = x.Username,
IpAddress = x.IpAddress ?? string.Empty,
UserAgent = x.UserAgent ?? string.Empty,
Client = x.Client ?? string.Empty,
IsSuccess = x.IsSuccess,
})
.ToListAsync();
return Ok(authLogs);
}
}