Wednesday, 20 November 2024

Refresh Token : JWT Token Authentication WEBAPI

 To implement JWT (JSON Web Token) refresh token logic in a Web API, the general idea is to issue two tokens when a user successfully logs in:

  1. Access Token (short-lived): This token is used for authenticating requests and is typically valid for a short duration (e.g., 15 minutes).
  2. Refresh Token (long-lived): This token is used to get a new access token when the old one expires. It typically has a longer expiry time (e.g., 7 days or more).

Steps to Implement JWT Refresh Token in a Web API

1. Login Endpoint (Generate Access and Refresh Tokens)

When the user successfully logs in, generate both an access token and a refresh token. The access token will be used for authentication, and the refresh token will be stored securely (typically in an HttpOnly cookie or in the database).

public class AuthController : ControllerBase
{
    private readonly IConfiguration _configuration;

    public AuthController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginRequest request)
    {
        // Validate user credentials
        var user = AuthenticateUser(request.Username, request.Password);
        if (user == null) return Unauthorized();

        // Generate JWT Access Token
        var accessToken = GenerateAccessToken(user);

        // Generate Refresh Token
        var refreshToken = GenerateRefreshToken();

        // Save the refresh token in the database or cache associated with the user

        return Ok(new
        {
            AccessToken = accessToken,
            RefreshToken = refreshToken
        });
    }

    private string GenerateAccessToken(User user)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.Username),
            new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString())
            // Add other claims as necessary
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
_configuration["Jwt:SecretKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(15), // short-lived access token
            signingCredentials: creds
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    private string GenerateRefreshToken()
    {
        var randomNumber = new byte[32];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(randomNumber);
        }

        return Convert.ToBase64String(randomNumber);
    }
}

2. Refresh Token Endpoint

When the access token expires, the client sends the refresh token to this endpoint to get a new access token. The refresh token is validated, and if valid, a new access token is issued.

[HttpPost("refresh-token")]
public IActionResult RefreshToken([FromBody] RefreshTokenRequest request)
{
    // Validate the refresh token
    var user = ValidateRefreshToken(request.RefreshToken);
    if (user == null) return Unauthorized();

    // Generate new access token
    var accessToken = GenerateAccessToken(user);

    // Optionally, generate a new refresh token and save it (if you are rotating
// refresh tokens)
    var refreshToken = GenerateRefreshToken();
    SaveRefreshToken(user, refreshToken);

    return Ok(new
    {
        AccessToken = accessToken,
        RefreshToken = refreshToken
    });
}

private User ValidateRefreshToken(string refreshToken)
{
    // Validate the refresh token. Check if it's valid and matches what's stored
// in the database.
    // This could involve checking a database, cache, or other storage for a
// matching refresh token.
    // Implement this logic based on your storage method return GetUserByRefreshToken(refreshToken);
}

3. Refresh Token Storage

Refresh tokens should be securely stored. Options include:

  • HttpOnly Cookies: Secure and less vulnerable to XSS attacks.
  • Database: Store refresh tokens in a table or cache, mapping them to user accounts and ensuring the refresh token is revoked after use.

If you're storing the refresh token in a cookie, set the HttpOnly flag and Secure flag to ensure the token is sent only over HTTPS and cannot be accessed via JavaScript.

Example for setting refresh token in HttpOnly cookie:

Response.Cookies.Append("refresh_token", refreshToken, new CookieOptions
    {
        HttpOnly = true,
        Secure = true,
        SameSite = SameSiteMode.Strict,
        Expires = DateTime.Now.AddDays(7) // Set expiry to match your policy
    });
   

4. Token Expiry and Invalidating Refresh Tokens

Make sure that:

  • Access tokens expire quickly (e.g., in 15 minutes).
  • Refresh tokens can either have a long expiration time (e.g., 7 days) or a single-use (rotating refresh tokens) mechanism to increase security.
  • When a refresh token is used, it’s either revoked or replaced with a new refresh token to avoid token reuse.

5. Middleware for Token Validation

The access token is included in request headers (typically in the Authorization header) as a bearer token. This token is validated with each request to ensure it is still valid.

public class JwtMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IConfiguration _configuration;

    public JwtMiddleware(RequestDelegate next, IConfiguration configuration)
    {
        _next = next;
        _configuration = configuration;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var token = context.Request.Headers["Authorization"].FirstOrDefault()?
.Split(" ").Last();
        if (token != null)
        {
            AttachUserToContext(context, token);
        }

        await _next(context);
    }

    private void AttachUserToContext(HttpContext context, string token)
    {
        try
        {
            var claimsPrincipal = ValidateToken(token);
            context.User = claimsPrincipal;
        }
        catch
        {
            context.User = null;
        }
    }

    private ClaimsPrincipal ValidateToken(string token)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
_configuration["Jwt:SecretKey"]));
        var tokenHandler = new JwtSecurityTokenHandler();

        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            IssuerSigningKey = key,
            ClockSkew = TimeSpan.Zero,
            ValidIssuer = _configuration["Jwt:Issuer"],
            ValidAudience = _configuration["Jwt:Audience"]
        };

        return tokenHandler.ValidateToken(token, validationParameters, out _);
    }
}

Key Considerations:

  • Secure Storage: Always ensure that refresh tokens are stored securely, either in a database or HttpOnly cookies.
  • Token Rotation: Optionally, rotate refresh tokens to increase security (i.e., issue a new refresh token each time a new access token is issued).
  • Revocation: Implement a strategy for revoking refresh tokens if necessary (e.g., after logout or password change).

This setup should give you a basic implementation of JWT with access token and refresh token in your Web API.


Continue Reading →

Saturday, 16 November 2024

Dynamically passing method : Action, Func delegates

 In C#, you can dynamically pass a method or a delegate to another method by using either delegates or Action/Func types. These approaches allow you to abstract the invocation of methods in a flexible and reusable way.

Let’s look at how to pass methods dynamically using Action (for methods that return void) or Func<T> (for methods that return a value). Both Action and Func are predefined generic delegates in .NET.

Using Action Delegate

  static void Main(string[] args)
  {
    ExecuteAction(printMessage); //Passing method via Action delegate

// Passing an anonymous method (lambda) via Action delegate
    ExecuteAction(() => Console.WriteLine("Hello from Lamda expression"));
  }

// Method that accepts an Action delegate
  static void ExecuteAction(Action action)
  {
      action();
  }
 
// A method that matches the Action delegate signature (void method)
  static void printMessage()
  {
      Console.WriteLine("Hello world");
  }
OUTPUT
  //Hello world
  //Hello from Lamda expression

Using Func<T>Delegate

  static void Main(string[] args)
  {
    int result = ExecuteFunc(getSum, 4, 5);
    int result2 = ExecuteFunc((a, b) => a + b, 5, 6);
    Console.WriteLine(result);
    Console.WriteLine("Result from Lamda: "+result2);
  }

  static int ExecuteFunc(Func<int, int, int> func, int n1, int n2)
  {
      return func(n1, n2);
  }

  static int getSum(int a, int b)
  {
      return a + b;
  }

OUTPUT
  //9
  //Result from Lamda: 11

Key Points:

  • Action<T>: Used for methods that return void.
  • Func<T, TResult>: Used for methods that return a value.
  • You can pass both named methods and lambda expressions (anonymous methods) as delegates.
  • Delegates are type-safe, meaning that they require the method signature to match.

This flexibility lets you dynamically pass any method or lambda expression to another method in C#.


Continue Reading →

Coding Test: Extension method

Create an extension method to filter out odd and even numbers from a collection like a list or array. Below is an example of how you can write such extension methods.

1. Extension Method to Filter Odd Numbers

This method filters odd numbers from a collection of integers.

using System;
using System.Collections.Generic;

public static class NumberExtensions
{
    // Extension method to get odd numbers from an IEnumerable<int>
    public static IEnumerable<int> GetOddNumbers(this IEnumerable<int> numbers)
    {
        foreach (var number in numbers)
        {
            if (number % 2 != 0)
            {
                yield return number;
            }
        }
    }

    // Extension method to get even numbers from an IEnumerable<int>
    public static IEnumerable<int> GetEvenNumbers(this IEnumerable<int> numbers)
    {
        foreach (var number in numbers)
        {
            if (number % 2 == 0)
            {
                yield return number;
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        // Filter odd numbers using extension method
        var oddNumbers = numbers.GetOddNumbers();
        Console.WriteLine("Odd Numbers:");
        foreach (var number in oddNumbers)
        {
            Console.WriteLine(number);
        }

        // Filter even numbers using extension method
        var evenNumbers = numbers.GetEvenNumbers();
        Console.WriteLine("Even Numbers:");
        foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }
}

Output: 

Odd Numbers: 1 3 5 7 9 Even Numbers: 2 4 6 8 10

Summary:

  • The GetOddNumbers and GetEvenNumbers methods are simple extension methods that work on any collection implementing IEnumerable<int>.
  • yield return is used in the methods to lazily return the filtered numbers without creating a new list upfront, making it memory efficient.
Continue Reading →

Coding Test : find common character between two string

 Try to find common character with it's count between two strings and display it console.

Input:  string1: Dotnet , string2: Hotstar

Output:  o - 1, t - 2

  string str1 = "Dotnet";
  string str2 = "Hotstar";

  List<char> lst1 = str1.ToList();
  List<char> lst2 = str2.ToList();
  Dictionary<char, int> commonCharsDic = new Dictionary<char, int>();

  foreach (char c in lst1)
  {
      if (lst2.Contains(c))
      {
          int val;
          if (commonCharsDic.TryGetValue(c, out val))
              commonCharsDic[c] = val + 1;
          else
              commonCharsDic.Add(c, 1);

          lst2.Remove(c);
      }
  }

  foreach (var item in commonCharsDic)
      Console.WriteLine("{0}-{1}", item.Key, item.Value);

 // Output: o-1 , t-2

below is another similar logic


  string str1 = "Dotnet";
  string str2 = "Hotstar";

  List<char> lst1 = str1.ToList();
  List<char> lst2 = str2.ToList();
 
  var res2 = lst1.Intersect(lst2).ToList();
  Dictionary<char, int> dict = new Dictionary<char, int>();

  foreach (var item in res2)
      dict.Add(item, 0);

  foreach (var item in lst1)
  {
      if (lst2.Contains(item))
          dict[item] = dict[item] + 1;

      lst2.Remove(item);
  }

  foreach (var item in dict)
      Console.WriteLine("{0}-{1}", item.Key, item.Value);

 // Output: o-1 , t-2


Continue Reading →

Flags Attribute in C#

 In C#, the Flags Attribute is used to indicate that an enumeration (enum) can be treated as a bit field,  where individual bits represent different values that can be combined using bitwise operations. This allows you to represent multiple options or states in a single variable using a combination of flags.

Purpose of the Flags Attribute:

  • The Flags attribute provides a way to describe an enum where its values can be combined using bitwise operations like AND (&), OR (|), and XOR (^).

How to Use the Flags Attribute:

To use the Flags attribute in C#, you define an enum and then apply the [Flags] attribute to it. You also make sure that the enum values are powers of two (i.e., 1, 2, 4, 8, 16, etc.), so that each value corresponds to a single bit position.

  using System;

[Flags]
public enum Permissions
{
    None = 0,       // No permissions
    Read = 1,       // 0001 in binary
    Write = 2,      // 0010 in binary
    Execute = 4,    // 0100 in binary
    Delete = 8,     // 1000 in binary
    All = Read | Write | Execute | Delete  // Combine all flags
}

class Program
{
    static void Main()
    {
// Set My Permission
      Permissions myPerm = Permissions.Read | Permissions.Write;
  Console.WriteLine($"HasReadPermission:{myPerm.HasFlag(Permissions.Read)}");
  Console.WriteLine($"HasWritePermission:{myPerm.HasFlag(Permissions.Write)}");
Console.WriteLine($"HasExecutePermission:{myPerm.HasFlag(Permissions.Execute)}");

        // Combine multiple flags
        Console.WriteLine($"Permissions: {myPerm}");
    }
}

Explanation:

  • The [Flags] attribute indicates that the Permissions enum represents a set of flags.
  • Each flag (e.g., Read, Write, Execute, etc.) corresponds to a power of two.
  • You can combine multiple flags using the bitwise OR operator (|), as shown in myPermissions = Permissions.Read | Permissions.Write.
  • The HasFlag method checks if a specific flag is set in a variable.

Output:

Has Read Permission: True Has Write Permission: True Has Execute Permission: False Permissions: Read, Write

Key Points:

  1. Bitwise Operations: You can combine and check flags using bitwise operations like |, &, and ^.
    • | (OR) combines flags.
    • & (AND) checks if a flag is set.
    • ^ (XOR) can toggle a flag.
  2. HasFlag Method: The HasFlag method is a convenient way to check if a specific flag is set.
  3. Enum Values as Powers of Two: For bitwise operations to work properly, each flag should be a power of two.

Important Considerations:

  • 0 as Default: The value 0 is typically used for the "None" flag, representing no flags set.
  • Combining Flags: If you want to represent a combination of multiple flags, you can do so using the | operator, e.g., Permissions.Read | Permissions.Write.
  • Readability: Using the Flags attribute doesn't change the underlying behavior of enums, but it does enhance their clarity when printed or logged. If you print an enum without the Flags attribute, it will display the integer value instead of a comma-separated list of flags.

Example of Output with ToString():

If you print the myPermissions variable directly, you will get the readable flag names:

  Console.WriteLine(myPermissions); // Output: Read, Write

This approach is useful for scenarios like permissions, configurations, state management, and other cases where multiple independent options need to be combined.

2: Program 2

  public class Program
  {
      // Define an Enum with FlagsAttribute.
      [Flags]
      enum MultiHue
      {
          None = 0,
          Black = 1,
          Red = 2,
          Green = 4,
          Blue = 8
      };
 
      static void Main(string[] args)
      {
 
          var multihue1 = (int)MultiHue.Black | (int)MultiHue.Blue | (int)MultiHue.Red;
          var multihue2 = MultiHue.Black | MultiHue.Blue | MultiHue.Red;
          var multihue3 = (int)MultiHue.None;
 
          Console.WriteLine(multihue1);     // Output:11
          Console.WriteLine(multihue2);     // Output:Black, Red, Blue
          Console.WriteLine(multihue3);    // Output:0
          Console.WriteLine((MultiHue)11); // Output: Black, Red, Blue
 
          Console.Read();
      }
  }

3: Program 3

  public class Program
  {
      // Define an Enum with FlagsAttribute.
      [Flags]
      enum MultiHue
      {
          None = 0,
          Black = 1,
          Red = 2,
          Green = 4,
          Blue = 8
      };
 
      static void Main(string[] args)
      {
          // Display all combinations of values, and invalid values.
          Console.WriteLine(
               "\nAll possible combinations of values with FlagsAttribute:");
 
          for (int val = 0; val <= 16; val++)
          {
              Console.WriteLine("{0}-{1}", val, (MultiHue)val);
          }
 
          Console.Read();
      }
  }

Output

All possible combinations of values with FlagsAttribute:

0-None
1-Black
2-Red
3-Black, Red
4-Green
5-Black, Green
6-Red, Green
7-Black, Red, Green
8-Blue
9-Black, Blue
10-Red, Blue
11-Black, Red, Blue
12-Green, Blue
13-Black, Green, Blue
14-Red, Green, Blue
15-Black, Red, Green, Blue
16-16

  • & (AND) checks if a flag is set. See below code
//You can combine multiple flags using bitwise OR (|) and
//check if a flag is set using bitwise AND (&).
var multiHue = MultiHue.Black | MultiHue.Red;
 Console.WriteLine("multiHue: "+ multiHue); // Output: Black, Red

 bool hasBlack = (multiHue & MultiHue.Black) == MultiHue.Black;
 bool hasRed = (multiHue & MultiHue.Red) == MultiHue.Red;
 bool hasBlue = (multiHue & MultiHue.Blue) == MultiHue.Blue;

  Console.WriteLine("hasBlack: "+ hasBlack);  // Output: True
  Console.WriteLine("hasRed: " + hasRed);  // Output: True
  Console.WriteLine("hasBlue: " + hasBlue);  // Output: False


Continue Reading →

Thursday, 7 November 2024

Disadvantages of .NET Core

 Here are some disadvantages of .NET Core:

  • Limited libraries and tools

.NET Core doesn't have as many libraries and tools as .NET Framework. 

  • Less community support

.NET Core has a smaller community of developers than .NET Framework, so it may be harder to find answers to problems. 

  • No support for web forms

.NET Core doesn't support web forms, so if your applications rely on them, you'll need to use .NET Framework or look for alternatives. 

  • Some technologies are not available

Some .NET Framework technologies are not available in .NET Core, and some may never be available. 

  • Learning curve

.NET is extensive and has a large set of tools and technologies, which can make it challenging for new developers to learn. 

  • Legacy

Many CMS and eCommerce solutions are still based on a non-Core version of .NET. 

Continue Reading →

Disadvantages of MicroServices

 While microservices offer several advantages such as scalability, flexibility, and resilience, they also come with a set of challenges and disadvantages:

1. Increased Complexity

  • System Complexity: Microservices break a monolithic application into many smaller services, which can significantly increase the complexity of managing multiple services, especially as the number of services grows. You need to consider service discovery, load balancing, distributed tracing, and more.
  • Distributed System Issues: Communication between services introduces challenges like network latency, partial failures, and data consistency issues.

2. Data Management and Consistency

  • Distributed Data Management: Each microservice typically has its own database, which can lead to data duplication and consistency issues, especially when trying to maintain ACID (Atomicity, Consistency, Isolation, Durability) properties across multiple databases.
  • Eventual Consistency: Microservices often rely on eventual consistency models, which can complicate transactions and data synchronization between services.

3. Increased Overhead

  • Resource Overhead: Each microservice often runs in its own container or VM, requiring more resources for deployment and operation than a monolithic application. This can lead to higher infrastructure costs.
  • Management Overhead: Microservices require more tools and infrastructure for deployment, monitoring, logging, security, and scaling. This often translates to increased overhead in managing these services.

4. Communication Complexity

  • Inter-Service Communication: Microservices typically communicate over HTTP, gRPC, or messaging queues, which introduces latency and potential failure points in communication between services. Handling retries, timeouts, and backoff strategies becomes necessary.
  • Network Dependency: Since services are distributed, they rely on networks for communication, which can be unreliable or slow, adding additional points of failure.

5. Deployment Challenges

  • Deployment and Versioning: Managing deployment pipelines for multiple services can be more complicated than for a single monolithic application. Coordinating versions of services, ensuring backward compatibility, and managing dependencies between services can be complex.
  • Service Coordination: When deploying updates, you must ensure that services are updated in the correct order and that dependent services are compatible with new versions.

6. Testing Complexity

  • End-to-End Testing: Testing microservices can be more complex than testing monolithic applications because of the number of services involved and the interactions between them. Integration testing is harder, and mocking all the necessary services can be time-consuming.
  • Mocking Dependencies: Since microservices rely on each other, you must ensure that all dependencies are correctly mocked or tested, which can be difficult in distributed systems.

7. Security Considerations

  • Distributed Security: Microservices introduce multiple entry points for security breaches. Each service needs to be individually secured, requiring more detailed and granular security policies.
  • Inter-Service Authentication: Properly managing authentication and authorization between services can be more challenging than in a monolithic system.

8. Skill and Team Structure Requirements

  • Skillset: Microservices often require a more specialized skill set, including expertise in distributed systems, containerization (e.g., Docker, Kubernetes), continuous integration/continuous deployment (CI/CD), and service orchestration.
  • Organizational Challenges: Teams need to be organized around individual services (or domains), which can require a shift in how teams work and communicate, and may not be practical for all organizations.

9. Latency

  • Network Latency: Communication between services, especially over the network, can introduce latency that would not be present in a monolithic architecture where components are part of the same codebase. This latency can be amplified when services are hosted across different locations or cloud providers.

10. Difficulty in Tracing and Debugging

  • Distributed Tracing: In a microservices architecture, debugging issues that span multiple services can be more difficult. Identifying the root cause of an issue requires effective monitoring, logging, and tracing across multiple services, which can be harder to manage than in a monolithic system.
  • Debugging Complexity: Since the system is composed of multiple loosely coupled services, tracking down errors that occur due to service interactions or network failures can be time-consuming and error-prone.

11. Monolithic Legacy Systems Integration

  • Integration with Legacy Systems: If a company has existing monolithic systems, integrating microservices into the current architecture can be challenging and costly. You may need to carefully design the boundaries of microservices and how they interact with the legacy system.

Despite these disadvantages, microservices can still be a powerful architecture for certain use cases, especially for large-scale, distributed applications where flexibility, scalability, and fault tolerance are essential. However, it's important to weigh these challenges against the benefits before adopting microservices.

Continue Reading →

Topics

ADFS (1) ADO .Net (1) Ajax (1) Angular (47) Angular Js (15) ASP .Net (14) Authentication (4) Azure (3) Breeze.js (1) C# (55) CD (1) CI (2) CloudComputing (2) Coding (10) CQRS (1) CSS (2) Design_Pattern (7) DevOps (4) DI (3) Dotnet (10) DotnetCore (20) Entity Framework (5) ExpressJS (4) Html (4) IIS (1) Javascript (17) Jquery (8) jwtToken (4) Lamda (3) Linq (10) microservice (4) Mongodb (1) MVC (46) NodeJS (8) React (10) SDLC (1) Sql Server (32) SSIS (3) SSO (1) TypeScript (3) UI (1) UnitTest (2) WCF (14) Web Api (16) Web Service (1) XMl (1)

Dotnet Guru Archives