none
SharePointOnlineCredentials disappeared from CSOM. How do I now connect to SharePoint online to upload files in c# code? RRS feed

  • Question

  • SharePointOnlineCredentials disappeared from CSOM.  How do I now connect to SharePoint online to upload files in c# code now?

    This function below used to work when attached to Microsoft.SharePointOnline.CSOM.  But now I get the error message "Type or namespace name 'SharePointOnlineCredentials' could not be found (are you missing a using directive or an assembly reference?"  I had heard that Microsoft fixed CSOM to work with dot net core, so I upgraded to the newest CSOM and now I'm upset...

            public void Auth(String username, String pwd)
            {
                m_context = new ClientContext(m_site);
                Web web = m_context.Web;
                SecureString passWord = new SecureString();
                foreach (char c in pwd.ToCharArray()) passWord.AppendChar(c);
                m_context.Credentials = new SharePointOnlineCredentials(username, passWord);
                m_context.Load(web);
                m_context.ExecuteQuery();
            }

    Tuesday, July 7, 2020 2:01 AM

All replies

  • Hi

    How did you add the reference.

    You can search in the nuget manager 'Microsoft.SharePoint.Client', then you will find Microsoft.SharePointOnline.CSOM, install it. I think it is the latest SharePoint CSOM.


    Justin Liu Office Apps & Services MVP, MCSE
    Senior Software Engineer
    Learn Microsoft 365 from Microsoft DOCs now!
    Please Vote and Mark as Answer if it helps you.

    Tuesday, July 7, 2020 8:22 AM
  • Hi Alan,

    I tested with the latest SharePoint Online CSOM Package from Nuget installed by this command in Package Manage Console within Visual Studio:

    Install-Package Microsoft.SharePointOnline.CSOM -Version 16.1.20211.12000

    Microsoft.SharePointOnline.CSOM

    It turns out the SharePointOnlineCredentials class is still available, please check the following capture:

    Thanks

    Best Regards


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Wednesday, July 8, 2020 2:05 AM
  • Hi Alan,

    Would you please share some update on this question ? Did you solve it ?

    If my reply is helpful to this question, you could Mark as answer so that it could also help others in the forum who have the similiar question.

    Thanks

    Best Regards


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Friday, July 10, 2020 7:10 AM
  • Hi Jerry,

    I have the same problem with SharePointOnlineCredentials class. It works fine with .Net Framework.
    However, when I add
    Microsoft.SharePointOnline.CSOM package to my .Net Core project, SharePointOnlineCredentials class can not be found.

    Thanks,
    Friday, July 10, 2020 7:41 PM
  • Hi Vitali & Alan,

    If you are building .NET Core Solution with newest SharePoint Online CSOM Package, then SharePointOnlineCredentials Class won't be supported, please check out the official document below, this is a difference with .NET Framwork:

    The suggest way is register an Azure AD for authentication, please refer the detailed code demo in the documentation below:

    Using CSOM for .NET Standard instead of CSOM for .NET Framework

    Thanks

    Best Regards


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.


    Monday, July 13, 2020 2:09 AM
  • Thanks Jerry,

    Have you succeeded in using the information found in: Using CSOM for .NET Standard instead of CSOM for .NET Framework?I failed on every example.  Nothing compiles and there is no mention of which other nu-get packages I might need to make the calls work.  I just want to upload a file... Should it be this difficult?

    I hate the work-arounds I have to use to make .NET Standard talk to .NET Core, but it appears that is my only option at this point.  I'm literally creating a separate executable and calling it with parameters to log into SharePoint and update files.  It seems crazy to me that this is so difficult for a modern .NET CORE application.  If anyone has a better work-around, or a good example (that actually works) of how to "modern authenicate", I would appreciate it.

    Monday, July 13, 2020 3:58 PM
  • Thanks Jerry,

    Have you succeeded in using the information found in: Using CSOM for .NET Standard instead of CSOM for .NET Framework?I failed on every example.  Nothing compiles and there is no mention of which other nu-get packages I might need to make the calls work.  I just want to upload a file... Should it be this difficult?

    I hate the work-arounds I have to use to make .NET Standard talk to .NET Core, but it appears that is my only option at this point.  I'm literally creating a separate executable and calling it with parameters to log into SharePoint and update files.  It seems crazy to me that this is so difficult for a modern .NET CORE application.  If anyone has a better work-around, or a good example (that actually works) of how to "modern authenicate", I would appreciate it.

    Hi Alan,

    You can check a working video demo to use SharePoint Online CSOM in .NET Core Console App here:

    Preview on upcoming SharePoint Online CSOM .NET Standard Version

    And authentication with Azure AD Application is the suggested way by Microsoft.

    Thanks

    Best Regards


    "SharePoint" forums will be migrating to a new home on Microsoft Q&A !
    We invite you to post new questions in the "SharePoint" forums' new home on Microsoft Q&A !

    Tuesday, July 14, 2020 5:26 AM
  • Hi Alan,

    I followed the code demo from the official link above, it's successful to Connect SharePoint Online site using SharePoint Online CSOM within .NET Core Console App:

    Create a class named AuthenticationManager.cs:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.SharePoint.Client;
    using System.Collections.Concurrent;
    using System.Net.Http;
    using System.Security;
    using System.Text.Json;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web;
    
    
    namespace ConsoleApp2
    {
         public class AuthenticationManager : IDisposable
        {
            private static readonly HttpClient httpClient = new HttpClient();
            private const string tokenEndpoint = "https://login.microsoftonline.com/common/oauth2/token";
    
            private const string defaultAADAppId = "f23c703e-d3d6-496f-865a-961d8377eb50";
    
            // 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 static SecureString GetSecureString(string PassWord)
            {
                var securePassword = new SecureString();
                foreach (char c in PassWord)
                {
                    securePassword.AppendChar(c);
                }
                return securePassword;
    
            }
    
    
            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 wi1 = (TokenWaitInfo)state;
                                    if (wi1.Handle != null)
                                    {
                                        wi1.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;
                }
            }
    
            private 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);
            }
        }
    }
    

    Program.cs:

    using System;
    using System.Security;
    using System.Threading.Tasks;
    
    namespace ConsoleApp2
    {
        class Program
        {
            public static void  Main(string[] args)
            {
                Uri site = new Uri("https://Tenant.sharepoint.com");
                string user = "user@Tenant.onmicrosoft.com";
                SecureString password = AuthenticationManager.GetSecureString("yourpwd");
    
                // Note: The PnP Sites Core AuthenticationManager class also supports this
                using (var authenticationManager = new AuthenticationManager())
    
                using (var context = authenticationManager.GetContext(site, user, password))
                {
                    context.Load(context.Web);
                    context.ExecuteQuery();
                    Console.WriteLine($"Title: {context.Web.Title}");
                }
               
            }
        }
    }
    
    Note the defaultAADAppId should be replaced with the app id you registered in Azure AD.

    So it's actually working although it's a little complex than the Normal .NET Framework version :)

    Thanks

    Best Regards


    "SharePoint" forums will be migrating to a new home on Microsoft Q&A !
    We invite you to post new questions in the "SharePoint" forums' new home on Microsoft Q&A !

    Tuesday, July 14, 2020 8:52 AM
  • Hi Alan,

    Is there any updates ? If the reply above is helpful to this question. You could Mark the reply as answer so that it could also help othere who have the same question in the forum. :)

    Thanks

    Best Regards


    "SharePoint" forums will be migrating to a new home on Microsoft Q&A !
    We invite you to post new questions in the "SharePoint" forums' new home on Microsoft Q&A !

    Friday, July 17, 2020 5:22 AM