Introduction
- Create Azure AD Instance with Delegate Permission.
- Create Console Application and Add Microsoft.SharePointOnline.CSOM Nuget Package.
- Add Authentication Manager Class to generate the access token.
- Get Access Token & access SharePoint Online List data
- Login to https://portal.azure.com
- Search and Select Azure Active Directory
- Select App registrations
- Select +New registration
- Name "spocsom-api", user can add as per project namespace or naming convention guidelines.
- Select "Account in this organization (single-tenant)
- Click on the Register button
- Select API Permission
- Click to Add Permission
- Search and Add SharePoint from Request API Permission
- Select Add a Permission
- Selection Delegate Permission
- Search and Select Sites
- Choose "All Site.Read" -> This is not site specific but quite secure , It will just come into use to generate token, not going to grant access to the site to access the content.
- Click to "Add Permission"
- Select "Grant admin consent for default directory and click ok to proceed.
- All added api will show granted admin consent on behalf of the user. so it will not prompt for consent.
- Select authentication and scroll down till the end
- Under advance settings -> select Allow Public client flows -> Select Yes -> Click on Save to Proceed.
- Login to Visual Studio 2019 and Create New Project
- Select ConsoleApp (.Net Core) and Ok to proceed.
- Microsoft.SharepointOnline.CSOM
- Newtonsoft.Json
- System.IdentityModel.Token.Jwt
- public class AuthManager : IDisposable
- {
- private static readonly HttpClient httpClient = new HttpClient();
- private const string tokenEndpoint = "https://login.microsoftonline.com/common/oauth2/token";
- // Replace with Azure AD Client ID -Generated in above Steps
- private const string defaultAADAppId = "Azure Active Director Client ID";
- // Token cache handling
- private static readonly SemaphoreSlim semaphoreSlimTokens = new SemaphoreSlim(1);
- private AutoResetEvent tokenResetEvent = null;
- private readonly ConcurrentDictionary<string, string> tokenCache = new ConcurrentDictionary<string, string>();
- private bool disposedValue;
- internal class TokenWaitInfo
- {
- public RegisteredWaitHandle Handle = null;
- }
- public ClientContext GetContext(Uri web, string userPrincipalName, SecureString userPassword)
- {
- var context = new ClientContext(web);
- context.ExecutingWebRequest += (sender, e) =>
- {
- string accessToken = EnsureAccessTokenAsync(new Uri($"{web.Scheme}://{web.DnsSafeHost}"), userPrincipalName, new System.Net.NetworkCredential(string.Empty, userPassword).Password).GetAwaiter().GetResult();
- e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken;
- };
- return context;
- }
- public async Task<string> EnsureAccessTokenAsync(Uri resourceUri, string userPrincipalName, string userPassword)
- {
- string accessTokenFromCache = TokenFromCache(resourceUri, tokenCache);
- if (accessTokenFromCache == null)
- {
- await semaphoreSlimTokens.WaitAsync().ConfigureAwait(false);
- try
- {
- // No async methods are allowed in a lock section
- string accessToken = await AcquireTokenAsync(resourceUri, userPrincipalName, userPassword).ConfigureAwait(false);
- Console.WriteLine($"Successfully requested new access token resource {resourceUri.DnsSafeHost} for user {userPrincipalName}");
- AddTokenToCache(resourceUri, tokenCache, accessToken);
- // Register a thread to invalidate the access token once's it's expired
- tokenResetEvent = new AutoResetEvent(false);
- TokenWaitInfo wi = new TokenWaitInfo();
- wi.Handle = ThreadPool.RegisterWaitForSingleObject(
- tokenResetEvent,
- async (state, timedOut) =>
- {
- if (!timedOut)
- {
- TokenWaitInfo internalWaitToken = (TokenWaitInfo)state;
- if (internalWaitToken.Handle != null)
- {
- internalWaitToken.Handle.Unregister(null);
- }
- }
- else
- {
- try
- {
- // Take a lock to ensure no other threads are updating the SharePoint Access token at this time
- await semaphoreSlimTokens.WaitAsync().ConfigureAwait(false);
- RemoveTokenFromCache(resourceUri, tokenCache);
- Console.WriteLine($"Cached token for resource {resourceUri.DnsSafeHost} and user {userPrincipalName} expired");
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Something went wrong during cache token invalidation: {ex.Message}");
- RemoveTokenFromCache(resourceUri, tokenCache);
- }
- finally
- {
- semaphoreSlimTokens.Release();
- }
- }
- },
- wi,
- (uint)CalculateThreadSleep(accessToken).TotalMilliseconds,
- true
- );
- return accessToken;
- }
- finally
- {
- semaphoreSlimTokens.Release();
- }
- }
- else
- {
- Console.WriteLine($"Returning token from cache for resource {resourceUri.DnsSafeHost} and user {userPrincipalName}");
- return accessTokenFromCache;
- }
- }
- public async Task<string> AcquireTokenAsync(Uri resourceUri, string username, string password)
- {
- string resource = $"{resourceUri.Scheme}://{resourceUri.DnsSafeHost}";
- var clientId = defaultAADAppId;
- var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
- using (var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"))
- {
- var result = await httpClient.PostAsync(tokenEndpoint, stringContent).ContinueWith((response) =>
- {
- return response.Result.Content.ReadAsStringAsync().Result;
- }).ConfigureAwait(false);
- var tokenResult = JsonSerializer.Deserialize<JsonElement>(result);
- var token = tokenResult.GetProperty("access_token").GetString();
- return token;
- }
- }
- private static string TokenFromCache(Uri web, ConcurrentDictionary<string, string> tokenCache)
- {
- if (tokenCache.TryGetValue(web.DnsSafeHost, out string accessToken))
- {
- return accessToken;
- }
- return null;
- }
- private static void AddTokenToCache(Uri web, ConcurrentDictionary<string, string> tokenCache, string newAccessToken)
- {
- if (tokenCache.TryGetValue(web.DnsSafeHost, out string currentAccessToken))
- {
- tokenCache.TryUpdate(web.DnsSafeHost, newAccessToken, currentAccessToken);
- }
- else
- {
- tokenCache.TryAdd(web.DnsSafeHost, newAccessToken);
- }
- }
- private static void RemoveTokenFromCache(Uri web, ConcurrentDictionary<string, string> tokenCache)
- {
- tokenCache.TryRemove(web.DnsSafeHost, out string currentAccessToken);
- }
- private static TimeSpan CalculateThreadSleep(string accessToken)
- {
- var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(accessToken);
- var lease = GetAccessTokenLease(token.ValidTo);
- lease = TimeSpan.FromSeconds(lease.TotalSeconds - TimeSpan.FromMinutes(5).TotalSeconds > 0 ? lease.TotalSeconds - TimeSpan.FromMinutes(5).TotalSeconds : lease.TotalSeconds);
- return lease;
- }
- private static TimeSpan GetAccessTokenLease(DateTime expiresOn)
- {
- DateTime now = DateTime.UtcNow;
- DateTime expires = expiresOn.Kind == DateTimeKind.Utc ? expiresOn : TimeZoneInfo.ConvertTimeToUtc(expiresOn);
- TimeSpan lease = expires - now;
- return lease;
- }
- protected virtual void Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (disposing)
- {
- if (tokenResetEvent != null)
- {
- tokenResetEvent.Set();
- tokenResetEvent.Dispose();
- }
- }
- disposedValue = true;
- }
- }
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose(disposing: true);
- GC.SuppressFinalize(this);
- }
- }
Get List Data function get data from SharePoint online List and Print out all result based on return data.
- static void Main(string[] args)
- {
- List<Data> getdata = GetListData();
- foreach (Data data in getdata)
- {
- Console.WriteLine("Employee Name " + data.Title);
- }
- Console.ReadLine();
- }
- public static List<Data> GetListData()
- {
- const string DataColumn = "ID,Title";
- const string DataAPIAllData = "{0}/_api/lists/getbytitle('{1}')/items?$top=10&$select=" + DataColumn + "&$orderby=Modified desc";
- try
- {
- var results = new List<Data>();
- string sharepointSiteUrl = Convert.ToString("https://mittal1201.sharepoint.com/sites/CommSiteHub");
- if (!string.IsNullOrEmpty(sharepointSiteUrl))
- {
- string listname = "Employee";
- string api = string.Format(DataAPIAllData, sharepointSiteUrl, listname);
- if (!string.IsNullOrEmpty(listname))
- {
- //Invoke REST Call
- string response = TokenHelper.GetAPIResponse(api);
- if (!String.IsNullOrEmpty(response))
- {
- JObject jobj = Utility.Deserialize(response);
- JArray jarr = (JArray)jobj["d"]["results"];
- //Write Response to Output
- foreach (JObject j in jarr)
- {
- Data data = new Data();
- data.Title = Convert.ToString(j["Title"]);
- results.Add(data);
- }
- }
- return results;
- }
- else
- {
- throw new Exception("Custom Message");
- }
- }
- else
- {
- throw new Exception("Custom Message");
- }
- }
- catch (Exception ex)
- {
- throw new Exception("Custom Message");
- }
- }
- }
- public class Data
- {
- public string Title { get; set; }
- }
- public static string GetAPIResponse(string url)
- {
- string response = String.Empty;
- try
- {
- //Call to get AccessToken
- string accessToken = GetSharePointAccessToken();
- //Call to get the REST API response from Sharepoint
- System.Net.HttpWebRequest endpointRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
- endpointRequest.Method = "GET";
- endpointRequest.Accept = "application/json;odata=verbose";
- endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);
- System.Net.WebResponse webResponse = endpointRequest.GetResponse();
- Stream webStream = webResponse.GetResponseStream();
- StreamReader responseReader = new StreamReader(webStream);
- response = responseReader.ReadToEnd();
- return response;
- }
- catch (Exception ex)
- {
- throw;
- }
- }
- public static string GetSharePointAccessToken()
- {
- Uri site = new Uri("https://mittal1201.sharepoint.com/sites/CommSiteHub");
- string user = "user email address";
- string pwd = "user password";
- string result;
- using (var authenticationManager = new AuthManager())
- {
- string accessTokenSP = authenticationManager.AcquireTokenAsync(site, user, pwd).Result;
- result = accessTokenSP;
- }
- return result;
- }