ASP.NET Core 6.0 - API Authorization

This article describes restricting access to the member's image API endpoint to the user or an authorized administrator. I will assume you have downloaded the ASP.NET Core 6.0 - Users With Comments Project.

Users With Comments Project and Article Series

The ASP.NET Core 6.0 - Users With Comments Project (UWCP) implements public member profiles and a moderated comment workflow from user submission to admin publication. I started with the ASP.NET Core 6.0 - Users With Device 2FA Project (UWD2FAP) which implements WebAuthn, also known as FIDO2, instead of authenticator apps. The latest version of the UWCP is published at Preview. KenHaggerty. Com. I encourage you to register and submit a comment. Details, screenshots, and related articles can be found at ASP.NET Core 6.0 - Users With Comments Project. The details page includes the version change log.

Visual Studio 2022 (VS 2022) is required to develop .NET 6 and ASP.NET Core 6.0 applications. .NET 6 and ASP.NET Core 6.0 are Long Term Support (LTS) releases and will be supported until November 08, 2024. .NET 5 is a Current release and will be supported until May 08, 2022. .NET 3.1 is a LTS release and will be supported until December 3, 2022. See .NET and .NET Core release lifecycle.

I wanted to encourage users to use a profile image. I had already implemented Croppie to crop and resize a file image before it is uploaded and stored as a Data URL. I developed a Profile Image Control which adds options to use a webcam snapshot, or a stock image stored on the server.

New Register
New Register

The Profile Image Control implements the ProfileImageControler and a partial view. The partial implements the JavaScript that calls the ProfileImageControler. The _ProfileImagePostPartial is displayed on the Manage Account > Picture and the Admin > AppUsers > Edit page.

<partial name="_ProfileImagePostPartial" />

Many API endpoints need restricted access. Some need access by the user and/or an authorized administrator. The UWCP implements Authorization policies. I added MemberProfile and Comments Claims.

Authorization With MemberProfile and Comments.
Authorization With MemberProfile and Comments Mobile.

The ProfileImageControler's Get method has an appUserId parameter with a default value = 0. If the appUserId is not set, the current user is granted read write access to their picture. The Admin > Edit page sets the appUserId to the edit user's id. The ProfileImageControler injects the AuthorizationService to authorize the MemberProfilesRead and MemberProfilesWrite policies for the admin user.

private readonly IUserService _userService;
private readonly IAuthorizationService _authorizationService;
private readonly IWebHostEnvironment _environment;

public ProfileImageController(
    IUserService userService,
    IAuthorizationService authorizationService,
    IWebHostEnvironment environment)
{
    _userService = userService;
    _environment = environment;
    _authorizationService = authorizationService;
}

[HttpGet]
[Route("Get")]
public async Task<JsonResult> GetAsync(int appUserId = 0)
{
    if (!User.Identity?.IsAuthenticated ?? false) 
        return new JsonResult(new { Status = "Failed", Errors = "Not Authenticated", Id = appUserId });
    var currentAppUserId = int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier));
    var useStaffStock = User != null && 
        (User.Claims.Any(c => c.Type == AppClaimTypes.Comment && c.Value == AppClaimValues.Write) ||
        User.Claims.Any(c => c.Type == AppClaimTypes.Super));
    var canUpdate = false;
    if (appUserId == 0)
    {
        appUserId = currentAppUserId;
        canUpdate = true;
    }
    else
    {
        if (User == null || !(await _authorizationService.AuthorizeAsync(User, AuthorizationPolicies.MemberProfilesRead)).Succeeded)
            return new JsonResult(new { Status = "Failed", Errors = "Not Authorized", Id = currentAppUserId });
        var userClaims = await _userService.GetAppUserClaimsAsync(appUserId).ConfigureAwait(false);
        useStaffStock = userClaims.Any(c => c.Type == AppClaimTypes.Comment && c.Value == AppClaimValues.Write) ||
            userClaims.Any(c => c.Type == AppClaimTypes.Super);
        canUpdate = User != null && (await _authorizationService.AuthorizeAsync(User, AuthorizationPolicies.MemberProfilesWrite)).Succeeded;
    }

    var currentImageString = await _userService.GetMemberProfileImageStringAsync(appUserId).ConfigureAwait(false);
    if (string.IsNullOrEmpty(currentImageString)) currentImageString = AppSettings.AnonymousImage;
    else if (!currentImageString.StartsWith("data:image/"))
    {
        if (!System.IO.File.Exists(Path.Combine(_environment.WebRootPath, currentImageString[1..])))
            currentImageString = AppSettings.NotFoundImage;
    }
    ProfileImageInputModel profileImageInputModel = new();
    profileImageInputModel.Status = "Success";
    profileImageInputModel.CanUpdate = canUpdate;
    profileImageInputModel.CurrentImageString = currentImageString;
    profileImageInputModel.ImageTagWidth = AppSettings.MemberImageWidth;
    profileImageInputModel.ShowRemove = currentImageString != AppSettings.AnonymousImage;
    profileImageInputModel.StockEnabled = AppSettings.ProfileImageStockEnabled;
    profileImageInputModel.CropEnabled = AppSettings.ProfileImageCropEnabled;
    profileImageInputModel.CaptureEnabled = AppSettings.ProfileImageCaptureEnabled;
    if (AppSettings.ProfileImageStockEnabled)
    {
        var directoryPath = Path.Combine(_environment.WebRootPath, AppSettings.ProfileImageStockFolder[1..]);
        if (useStaffStock) directoryPath = Path.Combine(_environment.WebRootPath, AppSettings.ProfileImageStaffFolder[1..]);
        // Add image file name and relative path to StockImages.
        string[] fileNamePaths = Directory.GetFiles(directoryPath);
        foreach (string fileNamePath in fileNamePaths)
        {
            var fileName = Path.GetFileName(fileNamePath);
            var relativePath = Path.Combine(useStaffStock ? AppSettings.ProfileImageStaffFolder : AppSettings.ProfileImageStockFolder, fileName);
            profileImageInputModel.StockImages.Add(fileName, relativePath);
        }
    }
    return new JsonResult(profileImageInputModel);
}

References:

Ken Haggerty
Created 06/01/22
Updated 06/19/22 15:26 GMT

Log In or Reset Quota to read more.

Article Tags:

Authorization Claims
Successfully completed. Thank you for contributing.
Processing...
Something went wrong. Please try again.
Contribute to enjoy content without advertisments.
You can contribute without registering.

Comments(0)

Loading...
Loading...

Not accepting new comments.

Submit your comment. Comments are moderated.

User Image.
DisplayedName - Member Since ?