Wednesday, May 24, 2023

.NET 6 minimal API hash password with salt

 In .NET 6, you can use the `Rfc2898DeriveBytes` class from the `System.Security.Cryptography` namespace to generate a hash-based password with a salt. Here's an example of how you can do this:



using System;

using System.Security.Cryptography;

using System.Text;


public class PasswordHasher

{

    private const int SaltSize = 16; // 128 bits

    private const int HashSize = 32; // 256 bits

    private const int Iterations = 10000;


    public static string HashPassword(string password)

    {

        byte[] salt = new byte[SaltSize];

        using (var rng = RandomNumberGenerator.Create())

        {

            rng.GetBytes(salt);

        }


        byte[] hash = HashPasswordWithSalt(password, salt);


        // Combine the salt and hash

        byte[] saltedHash = new byte[SaltSize + HashSize];

        Buffer.BlockCopy(salt, 0, saltedHash, 0, SaltSize);

        Buffer.BlockCopy(hash, 0, saltedHash, SaltSize, HashSize);


        return Convert.ToBase64String(saltedHash);

    }


    public static bool VerifyPassword(string password, string hashedPassword)

    {

        byte[] saltedHash = Convert.FromBase64String(hashedPassword);

        byte[] salt = new byte[SaltSize];

        byte[] hash = new byte[HashSize];


        // Extract the salt and hash from the combined bytes

        Buffer.BlockCopy(saltedHash, 0, salt, 0, SaltSize);

        Buffer.BlockCopy(saltedHash, SaltSize, hash, 0, HashSize);


        byte[] computedHash = HashPasswordWithSalt(password, salt);


        // Compare the computed hash with the stored hash

        return SlowEquals(hash, computedHash);

    }


    private static byte[] HashPasswordWithSalt(string password, byte[] salt)

    {

        using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))

        {

            return deriveBytes.GetBytes(HashSize);

        }

    }


    // Compares two byte arrays in a way that is resistant to timing attacks

    private static bool SlowEquals(byte[] a, byte[] b)

    {

        uint diff = (uint)a.Length ^ (uint)b.Length;

        for (int i = 0; i < a.Length && i < b.Length; i++)

        {

            diff |= (uint)(a[i] ^ b[i]);

        }

        return diff == 0;

    }

}



You can use the `HashPassword` method to hash a password and store it securely in your application's database. The `VerifyPassword` method can be used to compare a user-provided password with the stored hashed password to verify if they match.


Monday, May 22, 2023

The feature 'global using directive' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version

 The error message you're seeing indicates that the "global using directive" feature is currently unsupported in the version of C# you're using. To resolve this issue, you can follow these steps:


Step 1: Open the `.csproj` file of your project.


Step 2: Locate the `<LangVersion>` element within the `<PropertyGroup>` section.


Step 3: Modify the `<LangVersion>` element to include the "preview" language version. It should look like this:



<PropertyGroup>

  <LangVersion>preview</LangVersion>

</PropertyGroup>



Step 4: Save the changes to the `.csproj` file.


Step 5: Rebuild your project.


By setting the language version to "preview," you enable the use of preview features, including the "global using directive." However, please note that preview features are subject to change and may not be suitable for production environments. It's important to exercise caution when using preview features and to consider upgrading to a stable release once available.


After applying these changes, rebuild your project and verify if the error is resolved.

minimal api authentication JWT .NET 6

 To implement minimal API authentication with JWT (JSON Web Tokens) in .NET 6, you can follow these steps:


Step 1: Create a new .NET 6 Minimal API project.


Step 2: Install the required NuGet packages:


dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

dotnet add package System.IdentityModel.Tokens.Jwt



Step 3: Configure JWT authentication in the `Program.cs` file:


using Microsoft.AspNetCore.Authentication.JwtBearer;

using Microsoft.IdentityModel.Tokens;


var builder = WebApplication.CreateBuilder(args);


// JWT Configuration

var jwtSettings = builder.Configuration.GetSection("JwtSettings");

var key = Encoding.ASCII.GetBytes(jwtSettings["SecretKey"]);

var tokenValidationParameters = new TokenValidationParameters

{

    ValidateIssuerSigningKey = true,

    IssuerSigningKey = new SymmetricSecurityKey(key),

    ValidateIssuer = false,

    ValidateAudience = false

};


builder.Services.AddAuthentication(options =>

{

    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;

    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

})

.AddJwtBearer(options =>

{

    options.TokenValidationParameters = tokenValidationParameters;

});


builder.Services.AddSingleton(tokenValidationParameters);




Step 4: Configure JWT secret key and issuer in the `appsettings.json` file:

{

  "JwtSettings": {

    "SecretKey": "your_secret_key_here"

  }

}



Step 5: Protect your API endpoints with the `[Authorize]` attribute:


using Microsoft.AspNetCore.Authorization;



app.MapGet("/protected", () =>

{

    return "This is a protected endpoint.";

}).RequireAuthorization(); // Requires authentication for this endpoint


Step 6: Generate JWT tokens during the login process:


using System.IdentityModel.Tokens.Jwt;

using Microsoft.Extensions.Configuration;

using Microsoft.IdentityModel.Tokens;



app.MapPost("/login", async (LoginModel model, IConfiguration configuration) =>

{

    // Validate the user credentials and generate JWT token

    if (IsValidUser(model.Username, model.Password))

    {

        var tokenHandler = new JwtSecurityTokenHandler();

        var jwtSettings = configuration.GetSection("JwtSettings");

        var key = Encoding.ASCII.GetBytes(jwtSettings["SecretKey"]);

        var tokenDescriptor = new SecurityTokenDescriptor

        {

            Subject = new ClaimsIdentity(new[]

            {

                new Claim(ClaimTypes.Name, model.Username)

            }),

            Expires = DateTime.UtcNow.AddHours(1),

            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)

        };

        var token = tokenHandler.CreateToken(tokenDescriptor);

        var tokenString = tokenHandler.WriteToken(token);

        return Results.Ok(new { Token = tokenString });

    }

    else

    {

        return Results.Unauthorized();

    }

});



Step 7: Test the protected endpoints by including the JWT token in the `Authorization` header of the request:


GET /protected HTTP/1.1

Host: localhost:5000

Authorization: Bearer <your_token_here>



That's it! With these steps, you have implemented minimal API authentication with JWT in .NET 6 using the Minimal API approach. Remember to customize the authentication and authorization logic according to your requirements.

How cache can be enabled for embeded text as well for search query results in Azure AI ?

 Great question, Rahul! Caching in the context of Azure AI (especially when using **RAG pipelines with Azure OpenAI + Azure AI Search**) can...