Clear        


                
                    using CORE.Authentication.Models;
using CORE.Authentication.Services;
using CORE.Services;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Users.APP.Domain;

namespace Users.APP.Features.Authentication
{
    /// <summary>
    /// Represents a token request to obtain a JWT response 
    /// including a JWT (access token) and refresh token.
    /// Inherits from JwtRequest and implements IRequest of 
    /// type JwtResponse for MediatR pipeline integration.
    /// </summary>
    public class TokenRequest : JwtRequest, IRequest<JwtResponse>
    {
        // Inherits user credentials (UserName and Password)
        // with SecurityKey, Issuer and Audience properties
        // from the JwtRequest base class.
    }

    /// <summary>
    /// Handles a TokenRequest by validating user credentials and 
    /// generating a JwtResponse including JWT and refresh token.
    /// </summary>
    public class TokenHandler : DbService<User>,
        IRequestHandler<TokenRequest, JwtResponse>
    {
        // The JWT authentication service that will provide JWT 
        // operations in the methods of this class.
        private readonly IJwtAuthService _jwtAuthService;

        /// <summary>
        /// Initializes a new instance of the TokenHandler class.
        /// </summary>
        /// <param name="db">The injected application's user 
        /// database context through the IoC Container.</param>
        /// <param name="jwtAuthService">The injected JWT 
        /// authentication service instance through the 
        /// IoC Container.</param>
        public TokenHandler(DbContext db,
            IJwtAuthService jwtAuthService) : base(db)
        {
            _jwtAuthService = jwtAuthService;
        }

        /// <summary>
        /// Returns a queryable collection of User entities with 
        /// their associated UserRole and Role navigation properties 
        /// eagerly included.
        /// Overrides the base method to apply eager loading.
        /// </summary>
        /// <returns>
        /// An IQueryable of type User with the UserRole and Role 
        /// navigation properties eagerly loaded.
        /// </returns>
        protected override IQueryable<User> DbQuery()
        {
            return base.DbQuery()
                .Include(u => u.UserRoles)
                .ThenInclude(ur => ur.Role);
            // u: User entity delegate, ur: UserRole entity delegate
        }

        /// <summary>
        /// Handles the token request by authenticating the user and 
        /// returning a JWT response including JWT and refresh token.
        /// </summary>
        /// <param name="request">The token request containing username, 
        /// password, security key, audience and issuer.</param>
        /// <param name="cancellationToken">Asynchronous method's token 
        /// to cancel the operation.</param>
        /// <returns>A JwtResponse including the JWT and refresh token 
        /// if successful, otherwise null.</returns>
        public async Task<JwtResponse> Handle(TokenRequest request, 
            CancellationToken cancellationToken)
        {
            // Attempt to get the active user by user name and password
            var userEntity = await DbSingleAsync(
                u => u.UserName == request.UserName && 
                u.Password == request.Password && u.StatusId == 1, 
                cancellationToken);
                // "Active" status ID value is 1 in the Statuses table.
                // Instead of u.StatusId == 1, u.Status.Title == "Active"
                // may also be written. 
            // u: User entity delegate, ur: UserRole entity delegate.

            // If user entity is not found, return null
            if (userEntity is null)
                return null;

            // Generate refresh token and save it to the Users table
            // for the retrieved user entity with expiration date and time,
            // Also update the user entity's online status
            userEntity.RefreshToken = _jwtAuthService.GetRefreshToken();
            userEntity.RefreshTokenExpiration = DateTime.Now.AddDays(7);
            userEntity.IsOnline = true;
            // the refresh token will expire after 7 days from
            // DateTime.Now's execution value
            await DbUpdateAsync(userEntity, cancellationToken);

            // Return a JWT response according to the expiration
            // including the JWT and refresh token
            var expiration = DateTime.Now.AddMinutes(5); 
            // the JWT will expire after 5 minutes from DateTime.Now's
            // execution value
            return _jwtAuthService.GetJwtResponse(userEntity.Id, 
                userEntity.UserName, 
                userEntity.UserRoles.Select(ur => ur.Role.Name),
                expiration, request.SecurityKey, request.Issuer, 
                request.Audience, userEntity.RefreshToken);
        }
    }
}