Extract and Access the Sharepoint data using a RefreshToken in C#

Darsini Ramu
BI3 Technologies
Published in
7 min readJul 11, 2024

Introduction:

In the realm of SharePoint development, authentication plays a pivotal role in ensuring secure access to resources. One key aspect that streamlines this process is the utilization of refresh tokens.

Power of Refresh Tokens:

  • Long-Term Access: Refresh tokens are used to obtain new access tokens without requiring the user to re-enter their credentials. This enables longer sessions and seamless access to resources over extended periods.
  • Reduced Credential Exposure: Since refresh tokens are long-lived and used to acquire short-lived access tokens, they mitigate the risk of exposing user credentials frequently. Access tokens, which are short-lived, are used for actual resource access.
  • Token Lifecycle Management: Refresh tokens typically have a longer lifespan compared to access tokens. They are used to obtain new access tokens when the current access token expires or becomes invalid, ensuring uninterrupted access to resources.

Achieving the goal of retrieving data from SharePoint using a refresh token involves several structured steps to facilitate secure and uninterrupted access to SharePoint resources.

Here is a step-by-step walkthrough guide:

Step 1: Generate an access token by utilizing the refresh token

  • This code snippet retrieves an access token from Microsoft Azure Active Directory using a refresh token. This token can then be used to authenticate requests to Microsoft services like SharePoint.
public static async Task<string> GetAccessToken()
{
// Configuration Setup
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");

var configuration = builder.Build();

// HTTP Client Setup
var httpClient = new HttpClient();

// Sending the Request
var request = new HttpRequestMessage(HttpMethod.Post, "https://login.microsoftonline.com/{Tenant ID}/oauth2/v2.0/token")
{
Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "refresh_token",
["refresh_token"] = configuration["RefreshToken"] ?? string.Empty,
["scope"] = "https://abc.sharepoint.com/Sites.ReadWrite.All"
})
};

var response = await httpClient.SendAsync(request);

// Response Handling
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
throw new Exception($"Request to token endpoint failed with status code {response.StatusCode} and content {errorContent}");
}

var responseContent = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(responseContent);
var root = doc.RootElement;
if (!root.TryGetProperty("access_token", out var accessTokenProperty))
{
throw new Exception("Access token not found in the response.");
}
var accessToken = accessTokenProperty.GetString();

// Logging Information
Logger?.LogInformation("Access Token generated successfully.");

// Return Value
if (string.IsNullOrEmpty(accessToken))
{
throw new Exception("Access token is empty.");
}
return accessToken;
}

Code Explanation:

Configuration Setup: The code initializes configuration management from an appsettings.json file located in the current directory. It retrieves necessary parameters like the refresh token from this configuration.

HTTP Client Setup: An HTTP client (HttpClient) is established to facilitate communication with external services over HTTP.

Sending the Request: It constructs a POST request to the Azure AD token endpoint (https://login.microsoftonline.com/{Tenant ID}/oauth2/v2.0/token). The request body includes parameters like grant_type (set to refresh_token), refresh_token (retrieved from configuration), and scope (defining permissions for SharePoint).

Handling the Response: After sending the request asynchronously, the code checks if the response indicates success (IsSuccessStatusCode). If not, it reads the error message from the response content and throws an exception with details about the failure.

Extracting the Access Token: If the response is successful, it parses the JSON response to extract the access_token. If the token is missing or empty, it throws an exception.

Logging and Return: Upon successfully obtaining the access token, it logs a success message. If any errors occur during the process (e.g., HTTP request failure or missing token), appropriate exceptions are thrown.

Note: Replace "Tenant ID" and "https://abc.sharepoint.com/Sites.ReadWrite.All" with actual values relevant to Azure AD tenant and SharePoint site permissions.

{  
RefreshToken: "Enter the Refresh Token here"
}

Step 2: Create a class to handle data operations for a specific SharePoint list

The SampleListData class represents a sample data structure for storing information about individuals. It includes properties such as ID, Name, Date of Birth (DOB), Date of Joining (DOJ), and an optional Description.

public class SampleListData
{
public string ID { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public DateTime DOB { get; set; }
public DateTime DOJ { get; set; }
public string? Description { get; set; }
}

Step 3: Create a method that extracts fields from SharePoint and stores them into a dictionary

The ExtractFields method is designed to parse a JSON string representing data, extract specific fields from it, and organize the extracted fields into a dictionary structure for further processing or analysis.

public static Dictionary<string, List<string>> ExtractFields(string jsonString)
{
// Column Filter Initialization
var columnFilter = new List<string> { "ODATA", "SERVER", "CONTENTTYPE", "ATTACHMENTS", "COMPLIANCEASSETID", "AUTHORID", "EDITORID", "GUID", "FILESYSTEMOBJECTTYPE", "TITLE" };

// Results Dictionary Initialization
using (JsonDocument doc = JsonDocument.Parse(jsonString))
{
// JSON Parsing
JsonElement root = doc.RootElement;
JsonElement valueArray = root.GetProperty("value");

// Accessing JSON Data
foreach (JsonElement item in valueArray.EnumerateArray())
{
var result = new Dictionary<string, string>();
foreach (JsonProperty prop in item.EnumerateObject())
{
if (!columnFilter.Any(filter => prop.Name.StartsWith(filter, StringComparison.OrdinalIgnoreCase)))
{
result[prop.Name] = prop.Value.ToString();
}
}

// Organizing Results
foreach (var pair in result)
{
if (results.TryGetValue(pair.Key, out List<string>? value))
{
value.Add(pair.Value);
}
else
{
results[pair.Key] = new List<string> { pair.Value };
}
}
}
}

// Return Value
return results;
}

Code Explanation:

Column Filter Initialization: Defines a list of field name prefixes (columnFilter) that determines which fields should be excluded from extraction. This filtering ensures that only relevant fields are processed.

Results Dictionary Initialization: Creates an empty dictionary (results) to store extracted fields. Each key in the dictionary represents a field name, and its corresponding value is a list of strings that contains all extracted values for that field.

JSON Parsing: Parses the input JSON string (jsonString) into a structured format (JsonDocument). This enables navigation and extraction of data from the JSON structure.

Accessing JSON Data: Accesses the "value" property within the JSON structure, which typically contains an array of JSON objects. These objects contain the actual data from which fields will be extracted.

Iterating Through JSON Objects: Iterates through each JSON object in the "value" array. For each object, it extracts specific fields based on the column filter criteria and stores them in a temporary dictionary.

Organizing Results: Adds the extracted fields to the results dictionary, ensuring that each field name maps to a list of strings containing all corresponding values extracted from the JSON data.

Return Value: Returns the populated results dictionary, which now contains all extracted fields organized by field name with associated lists of extracted values.

Step 4: Design a method to retrieve a SharePoint list based on its name

The GetSharePointList method retrieves items from a SharePoint list located at a specified URL using the SharePoint REST API. It requires an access token for authentication.

public static async Task<string> GetSharePointList(string siteUrl, string listTitle, string accessToken)
{
// Method Signature

try
{
// HTTP Client Setup
using var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

// HTTP GET Request
var response = await client.GetAsync($"{siteUrl}/_api/web/lists/getbytitle('{listTitle}')/items");
response.EnsureSuccessStatusCode();

// Response Handling
var content = await response.Content.ReadAsStringAsync();

// Logging Information
Logger?.LogInformation($"Sharepoint List: {listTitle} Fetched successfully.", listTitle);

// Return Value
return content;
}
catch (HttpRequestException e)
{
// Exception Handling
Console.WriteLine($"Request exception: {e.Message}");
throw;
}
}

Code Explanation:

HTTP Client Setup:

  • Initializes an HttpClient instance (client) using the using statement, ensuring proper disposal after use.
  • Configures the client to accept JSON responses (application/json) and sets the authorization header with the provided access token (Bearer scheme).

HTTP GET Request:

  • Performs an asynchronous HTTP GET request to the SharePoint REST API endpoint ({siteUrl}/_api/web/lists/getbytitle('{listTitle}')/items).
  • GetAsync method sends the request to fetch items from the specified SharePoint list.

Response Handling:

  • Checks if the HTTP response indicates success (EnsureSuccessStatusCode method). If not successful, it throws an exception.
  • Reads the response content asynchronously (ReadAsStringAsync method) to retrieve the JSON data representing SharePoint list items.

Logging Information: Logs a success message using a logging mechanism (Logger.LogInformation). It indicates that the SharePoint list with the specified title was fetched successfully.

Return Value: Returns the fetched content (content) as a string. This content typically contains JSON data representing items from the SharePoint list.

Exception Handling: Catches and handles HttpRequestException specifically. If an exception occurs during the HTTP request (e.g., network issues or server errors), it prints an error message to the console and rethrows the exception for higher-level handling.

Step 5: Utilize the above-mentioned classes and methods to create a list with the required data

The GetSampleListData method fetches data from a SharePoint list named "SampleListData" and converts it into a list of SampleListData objects. It requires an access token for authentication.

public async static Task<List<SampleListData>> GetSampleListData(string accessToken)
{
// Method Signature

// SharePoint List Data Retrieval
string siteUrl = "https://abc.sharepoint.com/";
var projects = ExtractFields(await GetSharePointList(siteUrl, "SampleListData", accessToken));

// Data Transformation
var data = new List<SampleListData>();
for (int i = 0; i < projects["ID"].Count; i++)
{
data.Add(new SampleListData
{
ID = projects["ID"][i],
Name = projects["Name"][i],
DOB = DateTime.Parse(projects["DOB"][i]),
DOJ = DateTime.Parse(projects["DOJ"][i]),
Description = projects["Description"][i],
});
}

// Return Value
return data;
}

Code Explanation:

Method Signature:

  • public async static Task<List<SampleListData>> GetSampleListData(string accessToken): Declares a public static asynchronous method GetSampleListData that returns a task of List<SampleListData>. It takes an accessToken parameter used to authenticate with SharePoint.

SharePoint List Data Retrieval:

  • string siteUrl = "https://abc.sharepoint.com/";: Specifies the SharePoint site URL.
  • await GetSharePointList(siteUrl, "SampleListData", accessToken): Calls an asynchronous method GetSharePointList to fetch data from the SharePoint list named "SampleListData" using the provided access token. The await keyword indicates that the method execution will await completion of this operation.

Data Transformation:

  • var projects = ExtractFields(await GetSharePointList(...));: Retrieves SharePoint list data and extracts fields into a dictionary-like structure (projects), where each field has a list of values.
  • var data = new List<SampleListData>();: Initializes an empty list to store transformed data.

Return Value: Returns the populated List<SampleListData> containing transformed SharePoint list data.

Step 6: Access the SharePoint data

  • The Main method serves as the entry point of the application.
  • It demonstrates the usage of the methods GetAccessToken and GetSampleListData from the Sharepoint class to obtain an access token and retrieve sample data from a SharePoint list.
  • After obtaining the access token and retrieving the data, it displays the retrieved data on the console.
static async Task Main(string[] args)
{
var accessToken = await Sharepoint.GetAccessToken();

var SampleListData = await Sharepoint.GetSampleListData(accessToken);
foreach (var item in SampleListData)
{
Console.WriteLine($"ID: {item.ID}, Name: {item.Name}, DOB: {item.DOB}, DOJ: {item.DOJ}, Description: {item.Description}");
}
}

Conclusion:

Using refresh tokens in SharePoint logins enhances the usability and safety of SharePoint applications by reducing the need for users to repeatedly enter passwords. This streamlined access not only improves user experience but also strengthens security measures, ensuring that SharePoint resources remain protected from unauthorized access.

About Us:

Bi3 has been recognized for being one of the fastest-growing companies in Australia. Our team has delivered substantial and complex projects for some of the largest organizations around the globe, and we’re quickly building a brand that is well-known for superior delivery.

Website: https://bi3technologies.com/

Follow us on,
LinkedIn: https://www.linkedin.com/company/bi3technologies
Instagram: https://www.instagram.com/bi3technologies/
Twitter: https://twitter.com/Bi3Technologies

--

--