Clear        


                
                    using CORE.Authentication.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

namespace CORE.Authentication.Services
{
    /// <summary>
    /// Provides concrete implementations for JWT (JSON Web Token) 
    /// based authentication operations, including generating JWT 
    /// response with JWT (access token) and refresh token,
    /// generating refresh token, and extracting claims from JWT.
    /// This service is responsible for securely creating and 
    /// validating JWT used in authentication flows.
    /// </summary>
    public class JwtAuthService : IJwtAuthService
    {
        /// <summary>
        /// Returns a JWT response including JWT (access token) and 
        /// refresh token.
        /// </summary>
        /// <param name="userId">The unique ID of the user.</param>
        /// <param name="userName">The username of the user.</param>
        /// <param name="userRoleNames">A collection of role names 
        /// assigned to the user.</param>
        /// <param name="expiration">The expiration date and time for 
        /// the JWT.</param>
        /// <param name="securityKey">The security key used to sign 
        /// the JWT.</param>
        /// <param name="issuer">The issuer of the JWT, generally the 
        /// server API application's domain.</param>
        /// <param name="audience">The intended audience for the JWT, 
        /// generally the client application's domain.</param>
        /// <param name="refreshToken">Refresh token to be included 
        /// in the returned JwtResponse object.</param>
        /// <returns>A JwtResponse object containing the created JWT 
        /// and provided refresh token.</returns>
        public JwtResponse GetJwtResponse(int userId, string userName,
            IEnumerable<string> userRoleNames, DateTime expiration,
            string securityKey, string issuer,
            string audience, string refreshToken)
        {
            // Create claims for user ID and username,
            // then add claims for each user role.
            var claims = new List<Claim>
            {
                new Claim("Id", userId.ToString()), 
                // custom claim with key Id and value user ID
                new Claim(ClaimTypes.Name, userName)
            };
            foreach (var userRoleName in userRoleNames)
            {
                claims.Add(new Claim(ClaimTypes.Role, userRoleName));
            }

            // Create signing credentials using the provided security key
            // and 256-bit hash.
            var signingKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(securityKey));
            var signingCredentials = new SigningCredentials(
                signingKey, SecurityAlgorithms.HmacSha256);

            // Build the JWT with claims, issuer, audience, and expiration.
            var jwtSecurityToken = new JwtSecurityToken(
                issuer, audience, claims, DateTime.Now, expiration, 
                signingCredentials);
            var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();

            // Serialize the JWT to a string.
            var jwt = jwtSecurityTokenHandler.WriteToken(jwtSecurityToken);

            // Return the JWT response with the serialized JWT value
            // and the refresh token parameter value.
            return new JwtResponse
            {
                Jwt = $"{JwtBearerDefaults.AuthenticationScheme} {jwt}", 
                // JwtBearerDefaults.AuthenticationScheme: "Bearer"
                RefreshToken = refreshToken
            };
        }

        /// <summary>
        /// Returns a new generated refresh token, which is a secure, 
        /// random string used to obtain new JWT without 
        /// re-authenticating the user.
        /// </summary>
        /// <returns>
        /// A string representing the newly generated refresh token.
        /// </returns>
        public string GetRefreshToken()
        {
            // Generate a cryptographically secure random
            // 32-byte refresh token.
            var bytes = new byte[32];
            using (var generator = RandomNumberGenerator.Create())
            {
                generator.GetBytes(bytes);
            }
            return Convert.ToBase64String(bytes);
        }

        /// <summary>
        /// Extracts and returns a collection of claims from the 
        /// specified access token (JWT) using the provided security key.
        /// </summary>
        /// <param name="jwt">The JWT containing encoded claims.</param>
        /// <param name="securityKey">The security key used to validate 
        /// and decode the JWT.</param>
        /// <returns>
        /// A claim collection containing the claims extracted from the JWT.
        /// </returns>
        public IEnumerable<Claim> GetClaims(string jwt, string securityKey)
        {
            // IEnumerable is an interface that the List class implements.
            // LINQ methods can also be used with IEnumerable.
            // An IEnumerable collection can be converted to a List collection
            // by invoking ToList method when needed, or ToArray method
            // to convert the collection to an array.

            // Remove the "Bearer" prefix if exists in the JWT.
            jwt = jwt.StartsWith(JwtBearerDefaults.AuthenticationScheme) ?
                jwt.Remove(0, JwtBearerDefaults.AuthenticationScheme.Length + 1) : 
                jwt;

            // Prepare the signing key and validation parameters.
            var signingKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(securityKey));
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateLifetime = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey
            };

            // Validate the JWT and extract claims then return the claims.
            var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
            SecurityToken securityToken;
            var principal = jwtSecurityTokenHandler.ValidateToken(
                jwt, tokenValidationParameters, out securityToken);
            // out and ref are used to pass arguments by reference,
            // allowing the method to modify the value of the argument
            // and return it to the caller through the variable (securityToken).
            // Mostly used with value types.
            return securityToken is null ? null : principal.Claims;
        }
    }
}