Pass Google Traffic through WARP with V2ray in Ubuntu

Install warp-cli

curl https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
sudo apt update
sudo apt install cloudflare-warp

Run warp-cli in proxy mode

warp-cli --accept-tos register
warp-cli --accept-tos set-mode proxy
warp-cli --accept-tos set-proxy-port 40040
warp-cli --accept-tos connect
warp-cli --accept-tos enable-always-on

Configure xray

nano /usr/local/etc/xray/config.json
"outbounds": [
    {
        "protocol": "socks", 
        "settings": { 
            "servers":[
                {
                    "address":"127.0.0.1",
                    "port":40040
                }
            ]
        }, 
        "tag": "warp"
    }
],
"routing": {
    "domainStrategy": "AsIs",
    "rules": [
        {
            "type":"field",
            "domain":[
                "domain:google.com"
            ],
            "outboundTag": "warp"
        }
    ]
},

References
https://developers.cloudflare.com/warp-client/get-started/linux
https://pkg.cloudflareclient.com/install

Install chrony on Ubuntu 22.04

Chrony is an implementation of the Network Time Protocol (NTP) that runs on Unix-like operating systems (including Linux and macOS) and is released under the GNU GPL v2. It can synchronize the system clock with NTP servers, reference clocks (e.g. GPS receiver), and manual input using wristwatch and keyboard. It can also operate as an NTPv4 (RFC 5905) server and peer to provide a time service to other computers in the network.

apt install -y chrony
timedatectl set-ntp true
systemctl enable chrony && systemctl restart chrony
timedatectl set-timezone Asia/Tehran
chronyc sourcestats -v
chronyc tracking -v
date

References
https://installati.one/install-chrony-ubuntu-22-04/

Create Refresh Token on ASP.NET Web API

[HttpPost("refresh-token")]
public async Task<ActionResult<string>> RefreshToken()
{
    var refreshToken = Request.Cookies["refreshToken"];

    if (!user.RefreshToken.Equals(refreshToken))
    {
        return Unauthorized("Invalid Refresh Token.");
    }
    else if(user.TokenExpires < DateTime.Now)
    {
        return Unauthorized("Token expired.");
    }

    string token = CreateToken(user);
    var newRefreshToken = GenerateRefreshToken();
    SetRefreshToken(newRefreshToken);

    return Ok(token);
}

private RefreshToken GenerateRefreshToken()
{
    var refreshToken = new RefreshToken
    {
        Token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64)),
        Expires = DateTime.Now.AddDays(7),
        Created = DateTime.Now
    };

    return refreshToken;
}

private void SetRefreshToken(RefreshToken newRefreshToken)
{
    var cookieOptions = new CookieOptions
    {
        HttpOnly = true,
        Expires = newRefreshToken.Expires
    };
    Response.Cookies.Append("refreshToken", newRefreshToken.Token, cookieOptions);

    user.RefreshToken = newRefreshToken.Token;
    user.TokenCreated = newRefreshToken.Created;
    user.TokenExpires = newRefreshToken.Expires;
}

References
https://www.youtube.com/watch?v=HGIdAn2h8BA
https://www.youtube.com/watch?v=LowJMwa7LCU
https://passage.id/post/how-refresh-tokens-work-a-complete-guide-for-beginners?utm_source=pocket_reader
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/?utm_source=pocket_reader

Decode JWT Token in ASP.NET Web API

var stream = "[encoded jwt]";  
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
var tokenS = jsonToken as JwtSecurityToken;

Or, without the cast:

var token = "[encoded jwt]";  
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);

I can get Claims using:

var jti = tokenS.Claims.First(claim => claim.Type == "jti").Value;

References
https://stackoverflow.com/questions/38340078/how-to-decode-jwt-token?utm_source=pocket_reader

Run Code Once at Application Startup in ASP.NET Core

ASP.NET Core interface IHostApplicationLifetime allows developers to subscribe their handlers to ApplicationStarted, ApplicationStopping and ApplicationStopped events

ASP.NET Core provides developers with an IHostedService interface that has StartAsync and StopAsync methods that are executed when the application starts and stops. This interface is typically used to trigger long running background tasks, but StartAsync itself must return quickly so as not to block other hosted services, if any.

public class EInvoiceSenderService: IHostedService {
    private readonly ILogger logger;
    private readonly IHostApplicationLifetime appLifetime;

    public EInvoiceSenderService(
        ILogger<LifetimeEventsHostedService> logger, 
        IHostApplicationLifetime appLifetime) { //<--- INJECTED DEPENDENCY
        this.logger = logger;
        this.appLifetime = appLifetime;
    }

    public Task StartAsync(CancellationToken cancellationToken) {
        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken) {
        return Task.CompletedTask;
    }


    private void OnStarted() {
        logger.LogInformation("OnStarted has been called.");

        // Perform post-startup activities here
    }

    private void OnStopping() {
        logger.LogInformation("OnStopping has been called.");

        // Perform on-stopping activities here
    }

    private void OnStopped() {
        logger.LogInformation("OnStopped has been called.");

        // Perform post-stopped activities here
    }    
}

Program.cs

builder.Services.AddHostedService<EInvoiceSenderService>();

References
https://levelup.gitconnected.com/3-ways-to-run-code-once-at-application-startup-in-asp-net-core-bcf45a6b6605
https://stackoverflow.com/questions/59650230/how-to-get-and-inject-the-ihostapplicationlifetime-in-my-service-to-the-containe

Access JWT Token from Controller in ASP.NET Web API

[HttpGet]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<ActionResult<string>> Get()
{
    var token = await HttpContext.GetTokenAsync("access_token");
    return token;
}
var token = Request.Headers["Authorization"];
var token2 = await HttpContext.GetTokenAsync(JwtBearerDefaults.AuthenticationScheme, "access_token");

References
https://stackoverflow.com/questions/58887151/how-to-access-token-data-from-controller-method
https://stackoverflow.com/questions/52793488/jwt-cannot-be-retrieved-by-httpcontext-gettokenasync-in-net-core-2-1

Get User in ASP.NET Web API

[HttpGet("me"), Authorize]
public async Task<ActionResult<ResponseDto<GetMeResponseDto>>> GetMe()
{
    try
    {
        ResponseDto<GetMeResponseDto> result = new();
        var cts = new CancellationTokenSource();
        var context = await _dbFactory.CreateDbContextAsync(cts.Token);

        var userName = User?.Identity?.Name;

        if (!string.IsNullOrEmpty(userName))
        {
            var user = await context.Users.FirstOrDefaultAsync(x => x.Username == userName,
                cancellationToken: cts.Token);

            if (user != null)
            {
                var role = User!.FindFirstValue(ClaimTypes.Role) ?? string.Empty;

                result.Data = new GetMeResponseDto()
                {
                    Id = user.Id,
                    Username = userName,
                    Role = role,
                };
                result.HasError = false;
            }
        }

        return Ok(result);
    }
    catch (Exception e)
    {
        _logger.LogErrorEx(e, e.Message);
    }

    return BadRequest();
}

Or you can use HttpContext to access it.

References
https://www.youtube.com/watch?v=fhWIkbF18lM
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-7.0
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/use-http-context?view=aspnetcore-7.0
https://www.youtube.com/watch?v=7vqAHD9DlIA

JWT Token Creation, Authentication And Authorization In ASP.NET

Add Nuget Packages

Microsoft.AspNetCore.Authentication.JwtBearer
Microsoft.IdentityModel.Tokens
System.IdentityModel.Tokens.Jwt

Add setting in appsetting.json

"Jwt": {
    "Key": "ACDt1vR3lXToPQ1g3MyN", //Generate random String from https://www.random.org/strings
    "Issuer": "http://localhost:28747/", //Project Property-> Debug-> IIS-->App URL (you can local host url as well)
    "Audience": "http://localhost:28747/"
  },

Register JWT token for Authentication in Program.cs file

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//JWT Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
    options.TokenValidationParameters = new TokenValidationParameters {
        ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
    };
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

Create Models (UserLogin, UserModel and UserConstant)

namespace JWTLoginAuthenticationAuthorization.Models
{
    public class UserModel
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string Role { get; set; }
    }
}
namespace JWTLoginAuthenticationAuthorization.Models
{
    public class UserLogin
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
}
namespace JWTLoginAuthenticationAuthorization.Models
{
    // We are not taking data from data base so we get data from constant
    public class UserConstants
    {
        public static List<UserModel> Users = new()
            {
                    new UserModel(){ Username="naeem",Password="naeem_admin",Role="Admin"}
            };
    }
}

Create LoginAPI Controller (Authenticate user and generate token)

using JWTLoginAuthenticationAuthorization.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JWTLoginAuthenticationAuthorization.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class LoginController : ControllerBase
    {
        private readonly IConfiguration _config;
        public LoginController(IConfiguration config)
        {
            _config = config;
        }

        [AllowAnonymous]
        [HttpPost]
        public ActionResult Login([FromBody] UserLogin userLogin)
        {
            var user = Authenticate(userLogin);
            if (user != null)
            {
                var token = GenerateToken(user);
                return Ok(token);
            }

            return NotFound("user not found");
        }

        // To generate token
        private string GenerateToken(UserModel user)
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier,user.Username),
                new Claim(ClaimTypes.Role,user.Role)
            };
            var token = new JwtSecurityToken(_config["Jwt:Issuer"],
                _config["Jwt:Audience"],
                claims,
                expires: DateTime.Now.AddMinutes(15),
                signingCredentials: credentials);


            return new JwtSecurityTokenHandler().WriteToken(token);

        }

        //To authenticate user
        private UserModel Authenticate(UserLogin userLogin)
        {
            var currentUser = UserConstants.Users.FirstOrDefault(x => x.Username.ToLower() ==
                userLogin.Username.ToLower() && x.Password == userLogin.Password);
            if (currentUser != null)
            {
                return currentUser;
            }
            return null;
        }
    }
}

Create User API Controller to authorize user role

namespace JWTLoginAuthenticationAuthorization.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        //For admin Only
        [HttpGet]
        [Route("Admins")]
        [Authorize(Roles = "Admin")]
        public IActionResult AdminEndPoint()
        {
            var currentUser = GetCurrentUser();
            return Ok($"Hi you are an {currentUser.Role}");
        }
        private UserModel GetCurrentUser()
        {
            var identity = HttpContext.User.Identity as ClaimsIdentity;
            if (identity != null)
            {
                var userClaims = identity.Claims;
                return new UserModel
                {
                    Username = userClaims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value,
                    Role = userClaims.FirstOrDefault(x => x.Type == ClaimTypes.Role)?.Value
                };
            }
            return null;
        }
    }
}

References
https://www.c-sharpcorner.com/article/jwt-token-creation-authentication-and-authorization-in-asp-net-core-6-0-with-po/
https://www.youtube.com/watch?v=UwruwHl3BlU
https://www.youtube.com/watch?v=6sMPvucWNRE