Showing posts with label .NET Core. Show all posts
Showing posts with label .NET Core. Show all posts

April 27, 2026

Deploying .NET Core Web API to IIS on Windows Server (Fix HTTP 500.19 Error)

Introduction

Deploying a .NET Core Web API to IIS on Windows Server is a standard requirement in enterprise environments. However, many developers encounter deployment failures such as HTTP Error 500.19 (Error Code: 0x8007000d) immediately after publishing.

This error is rarely complex. It is almost always caused by missing prerequisites, incorrect IIS configuration, or an improperly installed Hosting Bundle.

This guide walks you through the complete IIS deployment process step-by-step - and explains exactly how to diagnose and resolve HTTP 500.19 errors with confidence.

Prerequisites

Before beginning deployment, ensure the following components are installed and ready:

  • Windows Server installed and accessible
  • IIS (Internet Information Services) installed and running
  • .NET Core / .NET Hosting Bundle installed (version must match your project)
  • Visual Studio project built successfully in Release mode
  • Published output folder generated and accessible

Important: The Hosting Bundle version must precisely match your target framework (.NET 6, 7, 8, etc.). A version mismatch is one of the most common root causes of HTTP Error 500.19.

01 Install IIS

Open Server ManagerAdd Roles and Features → Select Role-based installation → Choose your server → Select Web Server (IIS).

During feature selection, ensure the following are included:

  • Web Server (IIS)
  • Application Development Features
  • .NET Extensibility
  • ASP.NET Core Module (if available)
  • Management Tools
  • IIS Management Console

Command - Verify IIS Manager

inetmgr

Run this command in the Run dialog (Win + R) to confirm IIS Manager opens successfully.

02 Install .NET Core Hosting Bundle

Download the Hosting Bundle from the official Microsoft .NET download page and install the version that precisely matches your project's target framework. Running a mismatched version is a leading cause of 500.19 errors and should be verified before any other troubleshooting.

Command - Restart IIS After Installation

iisreset

After installation, always restart IIS to ensure the .NET Core Module is properly registered.

03 Select Publish Target

  • Right-click your project in Solution Explorer → Click Publish
  • Select Folder as the publish target → Click Next
  • Provide a publish path, e.g.: C:\Users\YourName\Desktop\PublishedFolder
  • Click Finish to save the publish profile

04 Publish the Web API

  • Set Configuration to Release
  • Set Deployment Mode to Framework-dependent
  • Confirm Target Framework matches your project version
  • Enable Delete all existing files prior to publish to avoid stale artifacts
  • Click Publish and wait for the process to complete

Once published, copy the output folder to the IIS web root directory:

C:\inetpub\wwwroot\MyWebApi

05 Create Application Pool

  • Open IIS Manager → Click Application Pools
  • Click Add Application Pool in the Actions panel
  • Name: MyApiPool
  • .NET CLR Version: No Managed Code (required for all .NET Core apps)
  • Managed Pipeline Mode: Integrated

Setting the .NET CLR Version to No Managed Code is critical. .NET Core manages its own runtime independently and does not rely on the IIS CLR. Selecting a CLR version here will cause application pool startup errors.

06 Create Website in IIS

  • In IIS Manager, right-click Sites → Click Add Website
  • Site Name: MyWebApi
  • Physical Path: point to your published output folder
  • Application Pool: select MyApiPool (created in Step 05)
  • Binding Port: 80 (or a custom port such as 5000)
  • Click OK to create the site

07 Configure Windows Firewall (For Custom Ports)

If your IIS website is configured to use a custom port (e.g., 5000), you must create an inbound firewall rule to allow traffic on that port. Without this step, external requests will be blocked silently by Windows Firewall.

  • Press Win + R, type wf.msc, and press Enter
  • Click Inbound RulesNew Rule
  • Select Port as the rule type → Click Next
  • Choose TCP and enter 5000 under Specific local ports
  • Select Allow the connection → Click Next
  • Apply to profiles: Domain, Private, and Public
  • Name the rule MyWebApi Port 5000 → Click Finish

After the rule is created, verify connectivity by navigating to:

http://your-server-ip:5000

Common Issue: HTTP Error 500.19 - Internal Server Error

Error Code: 0x8007000d  |  HTTP Status: 500.19 – Internal Server Error

This error consistently points to one of the following root causes:

  • Hosting Bundle not installed - or the version does not match the target framework
  • Corrupted or invalid web.config - IIS cannot parse the configuration file
  • Missing AspNetCoreModuleV2 - module not registered after Hosting Bundle installation
  • Incorrect Application Pool configuration - CLR version not set to No Managed Code

Diagnosis Tip: After each corrective action, run iisreset from an elevated command prompt and re-test. Most 500.19 errors resolve after reinstalling the correct Hosting Bundle version followed by an IIS restart.

Conclusion

The majority of HTTP 500.19 errors are environment configuration issues - not application code problems. By correctly installing IIS, precisely matching the Hosting Bundle version to your target framework, setting the Application Pool to No Managed Code, and opening the required ports in Windows Firewall, you can deploy your .NET Core Web API reliably and predictably on any Windows Server environment.

Follow these steps in sequence, validate each stage before proceeding to the next, and run iisreset after any configuration change to ensure changes take effect immediately.

April 17, 2025

Automating Azure Service App Deployment with Azure DevOps Pipelines

Introduction

In modern software development, Continuous Integration and Continuous Deployment (CI/CD) are crucial in ensuring smooth, automated deployments. Azure DevOps provides robust pipeline capabilities that enable developers to automate the deployment of their applications, reducing manual effort and minimizing errors.

In this blog, we’ll walk through setting up an Azure DevOps pipeline for an Azure Service App, ensuring seamless deployment whenever changes are pushed to the repository.

Setting Up the Azure DevOps Pipeline


Before diving into the pipeline configuration, ensure you have:
  • An Azure Service App created in the Azure Portal.
  • service connection in Azure DevOps is linked to your Azure subscription.
  • Your Service App code is stored in a repository like Azure Repos, GitHub, or Bitbucket.

Pipeline Configuration

Below is a YAML-based Azure DevOps pipeline that automates the build and deployment of an Azure Service App.


trigger:
  branches:
    include:
    - main # Change this to your branch if needed
  paths:
    include:
    - ServiceAppCode/*

variables:
  azureSubscription: 'ServiceAppDeployment' # Azure service connection name
  serviceAppName: 'TestingApp' # Azure Service App name
  serviceAppPath: 'ServiceAppCode' # Path to Service App source code
  buildConfiguration: 'Release' # Build service app source code in release
  publishDirectory: '$(Build.ArtifactStagingDirectory)/publish'

pool:
  vmImage: 'ubuntu-22.04' # Also use ubuntu-latest

stages:
- stage: Build
  displayName: 'Build Stage'
  jobs:
  - job: Build
    displayName: 'Build Job'
    steps:
    - task: UseDotNet@2
      displayName: 'Install .NET SDK'
      inputs:
        packageType: 'sdk'
        version: '8.0.x'
        includePreviewVersions: false

    - script: |
        echo "Cleaning up existing publish directory..."
        rm -rf $(publishDirectory)
        mkdir -p $(publishDirectory)
      displayName: 'Ensure Clean Publish Directory'

    - task: DotNetCoreCLI@2
      displayName: 'Restore Dependencies'
      inputs:
        command: 'restore'
        projects: '$(serviceAppPath)/*.csproj'

    - task: DotNetCoreCLI@2
      displayName: 'Build Service App'
      inputs:
        command: 'build'
        projects: '$(serviceAppPath)/*.csproj'
        arguments: '--configuration $(buildConfiguration) /p:WarningLevel=0' # /p:WarningLevel=0 Remove the warning at the time of build service app

    - task: DotNetCoreCLI@2
      displayName: 'Publish Service App'
      inputs:
        command: 'publish'
        projects: '$(serviceAppPath)/*.csproj'
        publishWebProjects: false
        arguments: '--configuration $(buildConfiguration) --output $(publishDirectory)'
        zipAfterPublish: true

    - task: PublishBuildArtifacts@1
      displayName: 'Publish Artifacts'
      inputs:
        pathToPublish: '$(publishDirectory)'
        artifactName: 'drop'

- stage: Deploy
  displayName: 'Deploy Stage'
  dependsOn: Build
  condition: succeeded()
  jobs:
  - job: Deploy
    displayName: 'Deploy to Azure Service App'
    steps:
    - download: current
      displayName: 'Download Build Artifacts'
      artifact: 'drop'

    - task: AzureServiceApp@1
      displayName: 'Deploy to Azure Service App'
      inputs:
        azureSubscription: '$(azureSubscription)'
        appType: 'webApp' # functionApp for function app OR webApp for web app
        appName: '$(serviceAppName)'
        package: '$(Pipeline.Workspace)/drop/*.zip'

Understanding the Pipeline

1. Trigger Configuration

  • The pipeline is triggered when changes are pushed to the main branch.
  • It monitors specific folders where Service App updates are made.

2. Defining Variables

  • azureSubscription: The name of the Azure DevOps service connection.
  • serviceAppName: The name of the Service App in Azure.
  • serviceAppPath: The directory containing the Service App source code.
  • publishDirectory: The folder where the build output is stored.

3. Choosing the Right Agent Pool

  • The pipeline uses the Ubuntu 22.04 VM image for the build process

4. Build Stage

  • Installing .NET SDK: Ensures that the required version is available.
  • Cleaning the Publish Directory: Prevents old files from interfering with new builds.
  • Restoring Dependencies: Ensures that all NuGet dependencies are downloaded.
  • Building the Service App: Compiles the service app with the specified configuration.
  • Publishing the Service App: Packages the compiled app for deployment.
  • Publishing Artifacts: Stores the published files as build artifacts for the deployment stage.

5. Deploy Stage

  • Downloading Build Artifacts: Retrieves the published application files.
  • Deploying to Azure: Uses the AzureServiceApp@1 task to deploy the service app to Azure.

Conclusion

By setting up this Azure DevOps pipeline, you can automate the deployment of your Azure Service App, ensuring quick and error-free releases.

With automation in place, you can focus on developing new features while Azure DevOps handles the heavy lifting of deployments!


If you have any questions you can reach out our SharePoint Consulting team here.

November 14, 2024

Level-up your .NET Skills: Automate, Validate, and Secure your Code


In the .NET ecosystem, there are many libraries that simplify common tasks, making the development process smoother and more efficient. In this post, we will explore three libraries that are indispensable in modern .NET applications: Automapper, FluentValidation, and BCrypt.Net. These libraries help with data mapping, validation, and security, respectively.


1. Automapper: Simplifying Object Mappings

Automapper is a library that eliminates the need to manually map properties from one object to another. This is especially helpful when dealing with Data Transfer Objects (DTOs) or ViewModels, where the structure may differ from the domain entities.

Problem:

Consider a scenario where you have a User entity with a lot of fields, but you only need a subset of those fields to be sent in an API response. Manually copying each property from the entity to a DTO can become tedious and error-prone.

Solution with Automapper:

Automapper provides a streamlined approach to map these objects.

Step-by-Step Example:

1. Define your domain model (User) and DTO (UserDTO):

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public DateTime DateOfBirth { get; set; }
}

public class UserDTO
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
}


2. Create an Automapper Profile to define the mapping:

using AutoMapper;

public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<User, UserDTO>()
            .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} 
            {src.LastName}"));
    }
}
Here, CreateMap<User, UserDTO>() defines the mapping between the User entity and the UserDTO. The ForMember method maps the FullName property in UserDTO to the concatenation of FirstName and LastName from User.

3. Configure Automapper in your Startup class (or using Dependency Injection):
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAutoMapper(typeof(Startup));
    }
}

4. Use Automapper in your application:

public class UserController : ControllerBase
{
    private readonly IMapper _mapper;

    public UserController(IMapper mapper)
    {
        _mapper = mapper;
    }

    [HttpGet("{id}")]
    public ActionResult<UserDTO> GetUser(int id)
    {
        var user = _dbContext.Users.Find(id);
        if (user == null) return NotFound();

        // Map User to UserDTO
        var userDto = _mapper.Map<UserDTO>(user);
        return Ok(userDto);
    }
}

Explanation:

  • The User object is retrieved from the database.

  • The IMapper.Map method is used to convert the User object to a UserDTO object, with minimal effort.

2. FluentValidation: Clean and Readable Model Validation

FluentValidation simplifies model validation by allowing developers to write validation logic in a fluent, expressive syntax, keeping the validation logic separate from the model itself.

Problem:

Manually validating model fields (e.g., ensuring required fields are filled, data formats are correct, etc.) often leads to messy and repetitive code.

Solution with FluentValidation:

FluentValidation provides a cleaner way to handle validations with reusable, strongly-typed rules.

Step-by-Step Example:

1. Define your model (User):

public class User
{
    public string Email { get; set; }
    public string Password { get; set; }
    public DateTime DateOfBirth { get; set; }
}

2. Create a Validator class for the model:

using FluentValidation;

public class UserValidator : AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(user => user.Email)
            .NotEmpty().WithMessage("Email is required.")
            .EmailAddress().WithMessage("A valid email is required.");

        RuleFor(user => user.Password)
            .NotEmpty().WithMessage("Password is required.")
            .MinimumLength(8).WithMessage("Password must be at least 8 characters long.");

        RuleFor(user => user.DateOfBirth)
            .NotEmpty().WithMessage("Date of birth is required.")
            .Must(BeAtLeast18).WithMessage("You must be at least 18 years old.");
    }

    private bool BeAtLeast18(DateTime dateOfBirth)
    {
        return dateOfBirth <= DateTime.Now.AddYears(-18);
    }
}

3. Use FluentValidation in your Controller or Service:

public class UserController : ControllerBase
{
    private readonly IValidator<User> _validator;

    public UserController(IValidator<User> validator)
    {
        _validator = validator;
    }

    [HttpPost]
    public IActionResult Register(User user)
    {
        var validationResult = _validator.Validate(user);
        if (!validationResult.IsValid)
        {
            return BadRequest(validationResult.Errors);
        }

        // Proceed with registration logic
        return Ok();
    }
}

Explanation:

  • The UserValidator class defines validation rules for the User model.

  • The RuleFor method is used to apply specific validation rules for each property, with a custom rule for checking the age. The Validate method checks if the model is valid, and any errors are returned as a response.

3. BCrypt.Net: Securing User Passwords

BCrypt.Net is a library for hashing passwords securely. Passwords should never be stored in plain text, and BCrypt helps ensure password security with hashing and salting.

Problem:

Storing passwords as plain text in databases makes user accounts vulnerable to data breaches and attacks.

Solution with BCrypt.Net:

BCrypt is widely regarded as a secure way to hash passwords, incorporating salt to protect against rainbow table attacks.

Step-by-Step Example:

1. Install BCrypt.Net:

dotnet add package BCrypt.Net-Next

2. Hash a password before storing it:  

public class UserService
{
    public string HashPassword(string password)
    {
        return BCrypt.Net.BCrypt.HashPassword(password);
    }
    public bool VerifyPassword(string password, string hash)
    {
       return BCrypt.Net.BCrypt.Verify(password, hash);
    }
}

3. Use BCrypt in your registration and login logic:

public class UserController : ControllerBase
{
    private readonly UserService _userService;

    public UserController(UserService userService)
    {
        _userService = userService;
    }

    [HttpPost("register")]
    public IActionResult Register(string password)
    {
        var hashedPassword = _userService.HashPassword(password);

        // Save hashedPassword to the database (omitted for brevity)

        return Ok("User registered successfully.");
    }

    [HttpPost("login")]
    public IActionResult Login(string password, string storedHash)
    {
        if (_userService.VerifyPassword(password, storedHash))
        {
            return Ok("Login successful.");
        }

        return Unauthorized("Invalid password.");
    }
}

Explanation:

  • HashPassword is used to hash the user's password before storing it in the database.
  • During login, VerifyPassword checks whether the entered password matches the stored hash, ensuring secure authentication.

Conclusion

These libraries - Automapper, FluentValidation, and BCrypt.Net—offer solutions to common problems encountered in .NET development. By using them, you can focus on writing cleaner, more maintainable, and secure code while relying on well-tested solutions for routine tasks.

If you have any questions you can reach out our SharePoint Consulting team here.

December 30, 2021

Web Scrapping in C# using Scraper API and HTMLAgilityPack

Overview:

In this Article, we will explore C# and how to create real life web scrapper using Scraper API and HtmlAgilityPack.

Requirement:

Recently, we have implemented a Scraper API to get data from different pages and dump into either CSV file OR Database for wholesale sourcing and ordering processing of the hospitality company based out Richmond, VA, USA.

Introduction:

What is Scraper API?

Scraper API is used to extract data. Its special purpose is to download large amounts of raw data easily and quickly. It is easy to use. We can scrape by sending the URL you would like to scrape to the API along with your API key and the API will return the HTML responses from the URL you want to scrape.

Get API Key from Scraper API?

We need to pass the API key with each Scraper API request to authenticate requests. For that, you need to sign up for an account here. After signing up on Scraper API, you will get 5000 free requests for a trial.

WEB SCRAPING USING C#

We are going to perform scraping with HTML parsing. We are going to extract data from https://coinmarketcap.com.
This website holds the information of cryptocurrencies, like name, current price, the percentage change in the last 24hrs, 7 days, market capital, etc.

Step-1: Create Project

First, create a project, here we are choosing Console App (.NET Core). You can choose the project template based on your requirement. Right now, our focus is web scraping so, skipped project creation steps.

Step-2: Install NuGet Packages

We require to install the following NuGet packages:
  1. ScraperAPI: This is the official C# SDK for the Scraper Api.
  2. HtmlAgilityPack: It is a .NET code library that allows you to parse "out of the web" HTML files.
Open Package Manager Console and run the below command one by one,
Install-Package ScraperApi
Install-Package HtmlAgilityPack

Step-3: GetDataFromWebPage() method

In this example, we are going to use the HttpClient and ScraperApiClient.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static async Task GetDataFromWebPage() {
  string apiKey = "cb4b88b493a5d003efadb120698c1f14";
  HttpClient scraperApiHttpClient = ScraperApiClient.GetProxyHttpClient(apiKey);
  scraperApiHttpClient.BaseAddress = new Uri("https://coinmarketcap.com");

  var response = await scraperApiHttpClient.GetAsync("/");
  if (response.StatusCode == HttpStatusCode.OK) {
    var htmlData = await response.Content.ReadAsStringAsync();
    ParseHtml(htmlData);
  }
}

Replace your Scraper API key with “apiKey” variable.

GetProxyHttpClient() is used to create a HTTP client with the scraperapi.com.

GetAsync() will fetch the data from the website and store in local variable. proxy.

Step-4: ParseHtml() method

Once get html data from Webpage, parsing it using the HTMLdocument method. It comes from HtmlAgilityPack.

Next step, load html data and get the ‘tbody’ html tag from it. The tbody tag contains the rows of Cryptocurrency data.

To get more data, use selectSignleNode method. It will return the first HtmlNode that matches the XPath query, it will return a null reference if the matching node is not found. SelectNodes is a collection of Html Nodes.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
static void ParseHtml(string htmlData) {
  var coinData = new Dictionary < string,
    string > ();
  HtmlDocument htmlDoc = new HtmlDocument();
  htmlDoc.LoadHtml(htmlData);

  var theHTML = htmlDoc.DocumentNode.SelectSingleNode("html//body");
  var cmcTableBody = theHTML.SelectSingleNode("//tbody");
  var cmcTableRows = cmcTableBody.SelectNodes("tr");
  if (cmcTableRows != null) {
    foreach(HtmlNode row in cmcTableRows) {
      var cmcTableColumns = row.SelectNodes("td");
      string name = cmcTableColumns[2].InnerText;
      string price = cmcTableColumns[3].InnerText;
      coinData.Add(name, price);
    }
  }
  WriteDataToCSV(coinData);
}

Step-5: WriteDataToCSV() method

In this example, we have taken the currency name and its price from the scraped data, and store in CSV file.
1
2
3
4
5
6
7
8
9
static void WriteDataToCSV(Dictionary < string, string > cryptoCurrencyData) {
  var csvBuilder = new StringBuilder();

  csvBuilder.AppendLine("Name,Price");
  foreach(var item in cryptoCurrencyData) {
    csvBuilder.AppendLine(string.Format("{0},\"{1}\"", item.Key, item.Value));
  }
  File.WriteAllText("C:\\Kishan\\Webscraping.csv", csvBuilder.ToString());
}

Step-6: Main() method

Replace content of Main method with below code:
1
2
3
static async Task Main(string[] args) {
  await GetDataFromWebPage();
}

Output

We can see CSV file as output that contains two columns, 1) Currency Name and 2) Price. We can get required column and data based on our need.
Webscraping-csv










Conclusion

In this blog, with the help of ScraperAPI and HtmlAgility Nuget Packages, we can scrap the data from site, filter the require data and dump into CSV file.

If you have any questions you can reach out our SharePoint Consulting team here.

January 28, 2021

How to register an App only Principal in SharePoint Online?

Overview:

We implemented the Delivery Schedule application in .Net Core for a Seattle, Washington-based construction firm. The information on Delivery Sites was available in SharePoint Online, we had to query this information using REST API from SharePoint Online and display the same in the .Net Core application.

Now, to call REST API from the .Net Core application, we need to use Client ID & Client Secret (app-only authentication) as per best practices. To generate Client ID and Client Secret, we need to register the SharePoint App. In this article, we will check the registration steps for the SharePoint App. So, now let’s get started!
  1. Access following URL to open App Registration Page.
    https://<<Site Collection URL>>/_layouts/15/appregnew.aspx 
  2. This will open following screen. 
  3. Click on “Generate” for Client ID and Client Secret. 
  4. Enter the Title, App Domain and Redirect URL.
    1. Title = BRiteApp (a meaningful name)
    2. App Domain = www.localhost.com
    3. Redirect URL = https://localhost.com 
  5. Click on Create.
  6. This will give you a summary of the App you created. Copy this information for future reference.
  7. Now, access the following URL.
    https://<<Site Collection URL>>/_layouts/15/appinv.aspx 
  8. This will open the following screen.
  9. Enter the same Client ID in the App ID field that we registered in Step 3 and click on Lookup. This will auto-populate other information.
  10. Now, in “Permission Request XML" we need to provide the XML with the desired permission level information. Below is the example XML that grants Site Collection level Full Control permission to the app. For more details on the permission request options, please visit this article from Microsoft.
     <AppPermissionRequests AllowAppOnlyPolicy="true">  
     <AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />  
     </AppPermissionRequests>  
    

  11. Click on Create.
  12. Click on the “Trust it” button.

Conclusion:

This is how we register our App in SharePoint Online. The registered app (Client ID & Client Secret) can be used to call the SharePoint REST APIs from the other applications.

If you have any questions you can reach out our SharePoint Consulting team here.

November 12, 2020

How to perform Azure App Only Authentication with SharePoint in .Net Core (Azure Function)

Introduction:

In the .NET Framework, there were native classes like  "SharePointCredentials" and "AuthenticatinoManager" which were used for SharePoint Authentication, but now they are removed.

The major difference compared to the .NET Framework CSOM is that the authentication is completely independent of the CSOM library now.

Resolution:

.NET standard CSOM now uses the OAuth for authentication. So we need to get an access token and pass it along with the call to SharePoint Online.

So in this blog, we will learn how to perform Azure App-Only Authentication with SharePoint in new .NET Standard CSOM to get data from SharePoint Online.

When we are using App-Only Authentication, we will have two options:
1. Azure Active Directory App Registration with the client certificate.
2. Create SharePoint App using AppRegNew.aspx and AppInv.aspx.

Here we will discuss the Azure AD App Registration approach.

Step 1:

  • First, we need to register a new app in the Azure Active Directory.
  • Go to http://portal.azure.com/ and select "Azure Active Directory". Now from the "App registrations" option in the left panel, register a new app.


Step 2:

Now we will grant the required permission to the app we created in Step 1. Open the newly created app and click on "API permissions" and click on "Add a permission".

Now select "SharePoint permission" as per your requirement and grant the admin consent. Here we needed "Full Control" permission for SharePoint so we have added "Sites.FullControl.All" and "Sites.Manage.All" permissions.


Step 3:

    1. CertificateName.cert
    2. CertificateName.pfx
  • Consideration: Keep note of the password which you have used to generate a certificate.

Step 4:

  • Now we will configure this certificate with our Azure AD App. 
  • Go to your Azure AD App. Now click on "Certificates and client" from the left panel and click on the "Upload certificate" button.
  • On clicking the "Upload certificate", it will open a panel at the top and allows you to upload the file. Here you need to upload the ".cert" file.

Step 5:

  • Make sure you have selected all required permissions in API Permissions and admin consent has been granted for all required permissions.

Step 6:

  • Now we will create an Azure Function solution using Visual Studio 2019.


Step 7:


Step 8:

  • Now in the Azure Function solution, we need to read the ".pfx" file which is uploaded in our solution. To read the file from the Azure Function solution we need execution context.
  • We can get the execution context using the below parameter in the Azure Function:
          
 ExecutionContext executionContext  

  • Below is the code snippet for how to use execution context:
 [FunctionName("Function1")]  
 public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext)  

  • Now using this execution context, we can get the ".pfx" file as below:
 var Sfilepath = $"{ System.IO.Directory.GetParent(executionContext.FunctionDirectory).FullName}\\Certificate\\CertificateFilename.pfx";  

  • Here we have uploaded the file in the Certificate folder. So the Sfilepath variable we are reading a file from the Certificate folder.

Step 9:

  • Now we will get ClientContext using this Certificate File and Application ID.
  • To get the context, first, we need an access token.
  • To get the access token we need Application ID, Certificate File Path, Certificate File Password, Tenant ID, and Permission Scope.
  • Permission scope will be as below:
  public static string[] permissionscopes = { "https://tenantname.sharepoint.com/.default" };  

  • We will use the below method to get the access token:
 internal static async Task<string> GetApplicationAuthenticatedClient(string clientId, string Sfilepath, string certificatePassword, string[] scopes, string tenantId)  
     {  
       X509Certificate2 certificate = new X509Certificate2(certThumprint, certificatePassword", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);  
       IConfidentialClientApplication clientApp;  
       clientApp = ConfidentialClientApplicationBuilder  
       .Create(clientId)  
       .WithCertificate(certificate)  
       .WithTenantId(tenantId)  
       .Build();  
       AuthenticationResult authResult = await clientApp.AcquireTokenForClient(scopes).ExecuteAsync();  
       string accessToken = authResult.AccessToken;  
       return accessToken;  
     }  

  • We can call the above method as below:
 var accessToken = await GetApplicationAuthenticatedClient(clientId, Sfilepath, certificatePassword, permissionscopes, tenantId);  

  • Now the "accessToken" variable will contain the access token.

Step 10:

  • Now using the access token which we get in Step 8, we will generate client context.
  • We can use the below method, which will return the client context. We will need the Site URL for which we want the client context and the access token.
 public static ClientContext GetClientContextWithAccessToken(string targetUrl, string accessToken)  
     {  
       ClientContext clientContext = new ClientContext(targetUrl);  
       clientContext.ExecutingWebRequest +=  
       delegate (object oSender, WebRequestEventArgs webRequestEventArgs)  
       {  
         webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =  
   "Bearer " + accessToken;  
       };  
       return clientContext;  
     }  

  • We can call the above method as below:
 var clientCtx = GetClientContextWithAccessToken(siteUrl, accessToken);  

Step 11:

  • Now we have the client context of the Site URL which you have used in step 9.
  • So we can now use the client context for any operations we want to perform programmatically with CSOM.
 Web web = clientCtx.Web;  
 clientCtx.Load(web);  
 clientCtx.ExecuteQuery();  

Conclusion:

This is how we can authenticate to SharePoint from .Net Core with the use of Azure App-Only Authentication. 

If you have any questions you can reach out our SharePoint Consulting team here.