Thursday, July 18, 2024

Setting Up and Configuring Authentication and Authorization in Blazor WASM with .NET API Backend on Azure

Blazor WebAssembly (WASM) is a powerful framework for building interactive client-side web apps with .NET. In this post, we’ll explore how to set up and configure authentication and authorization in a Blazor WASM application with a .NET API backend on Azure.

1. Setting Up the Blazor WASM Application

First, let’s create a new Blazor WASM application. You can do this using the .NET CLI:

dotnet new blazorwasm -n MyBlazorApp -au Individual

The -au Individual flag sets up the application to use individual user accounts, which is necessary for authentication.

2. Configuring Azure AD Authentication

Next, we need to configure Azure Active Directory (AD) for authentication. Here’s how to do it:

  1. In the Azure portal, create a new App Registration.
  2. Set the Redirect URI to the address of your Blazor app (e.g., https://localhost:5001/authentication/login-callback).
  3. Note down the Application (client) ID and Directory (tenant) ID.

Then, in the appsettings.json file of your Blazor app, add the following:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{Directory (tenant) ID}",
    "ClientId": "{Application (client) ID}",
    "ValidateAuthority": true
  }
}

3. Setting Up the .NET API Backend

Now, let’s set up the .NET API backend. You can create a new Web API project using the .NET CLI:

dotnet new webapi -n MyApi

4. Configuring Azure AD Authentication in the API

In the API project, install the Microsoft.Identity.Web NuGet package. Then, in the Startup.cs file, add the following:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
}

And in the appsettings.json file, add the following:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "{Your Azure AD domain}",
    "TenantId": "{Directory (tenant) ID}",
    "ClientId": "{Application (client) ID}"
  }
}

5. Configuring Authorization

Finally, let’s configure authorization. In the Startup.cs file of your API project, add the following:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ApiAccess", policy =>
            policy.RequireClaim("http://schemas.microsoft.com/identity/claims/scope", "Api.Access"));
    });
}

And in your controllers, use the [Authorize] attribute to enforce the policy:

[Authorize(Policy = "ApiAccess")]
public class MyController : ControllerBase
{
    // Controller methods here
}

That’s it! You’ve now set up and configured authentication and authorization in a Blazor WASM application with a .NET API backend on Azure. Happy coding!

Creating Authorization Policies in .NET Core 8.0

Authorization in .NET Core is a process that determines what a user is able to do. It uses policies to determine these permissions. Let’s dive into how to create authorization policies in .NET Core 8.0.

1. Defining Policies

Policies are defined in the Startup.cs file in the ConfigureServices method. Here’s an example of how to define a policy:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
    });
}

In this example, we’ve created a policy named “AdminOnly” that only allows users in the “Admin” role to access certain resources.

2. Applying Policies

Once you’ve defined your policies, you can apply them to your controllers or action methods using the [Authorize] attribute. Here’s how:

[Authorize(Policy = "AdminOnly")]
public class AdminController : Controller
{
    // Controller methods here
}

In this example, only users who fulfill the “AdminOnly” policy (i.e., users in the “Admin” role) can access the methods in the AdminController.

3. Policy Requirements

Policies can also have multiple requirements. Here’s an example:

services.AddAuthorization(options =>
{
    options.AddPolicy("AgeRequirement", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));
});

In this example, we’ve added an age requirement to our policy. We would also need to create a MinimumAgeRequirement class and a handler for this requirement.

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        var dateOfBirth = DateTime.Parse(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);

        var age = DateTime.Today.Year - dateOfBirth.Year;

        if (age >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

In this example, the MinimumAgeRequirement specifies the age requirement, and the MinimumAgeHandler handles the requirement.

Remember, authorization policies are a powerful way to control access to your resources. Use them wisely to secure your application. 

You could add different kinds of policies based on Claim Types. Here are more examples.

1. Policy Based on Claim Type

You can create a policy based on a specific claim type. For example, you can create a policy that only allows users with a specific claim to access certain resources:

services.AddAuthorization(options =>
{
    options.AddPolicy("EmailClaimPolicy", policy =>
        policy.RequireClaim(ClaimTypes.Email));
});

In this example, only users with an email claim can access the resources protected by the “EmailClaimPolicy”.

2. Policy with Multiple Requirements

Policies can have multiple requirements. Here’s an example:

services.AddAuthorization(options =>
{
    options.AddPolicy("EmployeeOnly", policy =>
        policy.RequireRole("Employee")
              .RequireClaim("Department", "Sales", "Marketing"));
});

In this example, the “EmployeeOnly” policy requires the user to be in the “Employee” role and have a “Department” claim of either “Sales” or “Marketing”.

3. Custom Requirements

You can also create custom requirements. Here’s an example:

public class WeekendRequirement : IAuthorizationRequirement { }

public class WeekendRequirementHandler : AuthorizationHandler<WeekendRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   WeekendRequirement requirement)
    {
        if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday ||
            DateTime.Now.DayOfWeek == DayOfWeek.Sunday)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

services.AddAuthorization(options =>
{
    options.AddPolicy("WeekendPolicy", policy =>
        policy.Requirements.Add(new WeekendRequirement()));
});

In this example, the “WeekendPolicy” only allows access on weekends.

Remember, the key to effective authorization is to create policies that accurately represent your business requirements. Happy coding!

Caching Strategies in .NET Core 8.0

Caching is a technique used to store frequently accessed data in memory, so future requests for that data can be served faster. In .NET Core 8.0, there are several caching strategies that you can use to optimize your applications. Let’s explore some of them.

1. In-Memory Caching

In-memory caching stores data in the memory of the web server. It’s fast and easy to implement. Here’s an example:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMemoryCache();
    }
}

public class MyController : Controller
{
    private IMemoryCache _cache;

    public MyController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

    public IActionResult Index()
    {
        DateTime cacheEntry;

        if (!_cache.TryGetValue("MyKey", out cacheEntry))
        {
            cacheEntry = DateTime.Now;

            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .SetSlidingExpiration(TimeSpan.FromSeconds(60));

            _cache.Set("MyKey", cacheEntry, cacheEntryOptions);
        }

        return View(cacheEntry);
    }
}

2. Distributed Caching

Distributed caching involves storing data across multiple nodes, typically in a cache cluster. This is useful for web farm scenarios where you have multiple web servers. Here’s how you can use it:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddStackExchangeRedisCache(options =>
        {
            options.Configuration = "localhost";
            options.InstanceName = "SampleInstance";
        });
    }
}

public class MyController : Controller
{
    private IDistributedCache _cache;

    public MyController(IDistributedCache distributedCache)
    {
        _cache = distributedCache;
    }

    public async Task<IActionResult> Index()
    {
        var cacheKey = "TheTime";
        var existingTime = _cache.GetString(cacheKey);

        if (existingTime == null)
        {
            existingTime = DateTime.UtcNow.ToString();
            _cache.SetString(cacheKey, existingTime);
        }

        return View(existingTime);
    }
}

3. Response Caching

Response caching adds cache-related headers to responses. Browsers and proxy servers use these headers to store responses. Here’s an example:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCaching();
    }
}

[ResponseCache(Duration = 60)]
public class MyController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

Remember, each caching strategy has its own use cases and trade-offs. Choose the one that best fits your application’s needs. Happy coding!

Monday, July 15, 2024

Generate Adobe Refresh Tokens and Updating Access Tokens Programmatically (Adobe Acrobat Sign)

Here is C#.NET code to generate Adobe Sign Refresh token and then update the Access token using the refresh token programmatically in C#.NET Core 8.0.

public static List<KeyValuePair<string, string>> GenerateNewRefreshToken()

{

    var client = new HttpClient();

    var request = new HttpRequestMessage(HttpMethod.Post, "https://<yourcompany>.na2.adobesign.com/oauth/v2/token");

    var collection = new List<KeyValuePair<string, string>>();

    collection.Add(new KeyValuePair<string, string>("code", code));

    collection.Add(new KeyValuePair<string, string>("client_id", "<Your Adobe Client ID>"));

    collection.Add(new KeyValuePair<string, string>("client_secret", "<Your Adobe Client Secret>"));

    collection.Add(new KeyValuePair<string, string>("redirect_uri", "<Your Website URL>"));

    collection.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));


    var content = new FormUrlEncodedContent((IEnumerable<KeyValuePair<string,string>>)collection);

    request.Content = content;


    var response = client.Send(request);

    Console.WriteLine(response.Content.ReadAsStringAsync());


    return collection;

}

C#.NET code to update Adobe Access Refresh token based on the above generated refresh token.

public static List<KeyValuePair<string, string>> UpdateAccessToken(string refreshToken)

{

    var client = new HttpClient();

    var request = new HttpRequestMessage(HttpMethod.Post,       "https://<yourcompany>.na2.adobesign.com/oauth/v2/refresh");

    var collection = new List<KeyValuePair<string, string>>();

    collection.Add(new("refresh_token", refreshToken));

    collection.Add(new("client_id", <your client idvalue>));

    collection.Add(new("client_secret", <your client secret value>));

    collection.Add(new("grant_type", "refresh_token"));

    var content = new FormUrlEncodedContent(collection);

    request.Content = content;


    var response = client.Send(request);

    Console.WriteLine(response.Content.ReadAsStringAsync());


    return collection;

}

Source: https://opensource.adobe.com/acrobat-sign/developer_guide/oauth.html