Search code examples
c#authentication.net-8.0project-online

Cannot get ProjectContext for Project Online from Sharepoint Office 365 and get a 403 in .NET 8


I'm experienced in .NET but pretty new to Sharepoint and Project Online. I am curious if it's MFA or what is going but I always get a

The remote server returned an error: (403) FORBIDDEN

For the code

public async Task<ProjectContext?> GetContextWithCredentialsAsync(string url, string username, string password)
{
    try
    {
        if (url.ToUpper().StartsWith("HTTP://") || url.ToUpper().StartsWith("HTTPS://"))
        {
            var securePassword = new SecureString();
            password.ToCharArray().ToList().ForEach(s => securePassword.AppendChar(s));

            var clientContext = new ProjectContext(url)
            {
                
                Credentials = new NetworkCredential(username, securePassword)
        };
            clientContext.Load(clientContext.Projects);
            clientContext.ExecuteQuery();

            return await System.Threading.Tasks.Task.Factory.StartNew(() => { return clientContext; });
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }

    return null;
}

I have seen other similar code online doing this and I think maybe it is MFA and I need to get an auth but other links I have seen were older .NET. I am using the library: 'Microsoft.SharePointOnline.CSOM' version 16.1.24810.12000 in a .NET 8 Web API calling the code above.

Edit: It seems you have to use Entra and something else but I want a better answer for all to use. It's pretty sad when MS docs are wrong. Someone has to know the answer we can write up for .NET 8 and NOT full framework.

The closest I have so far requires a clientId and tenantId but it was done through Azure. Can you do this for a VM directly without an Azure app?

public async System.Threading.Tasks.Task 
CreateConnectionProjectContext(string baseUrl, string url, string username, string password, string clientId, string tenantId)
{
        var pcaConfig = PublicClientApplicationBuilder.Create(clientId).WithTenantId(tenantId);

        var TokenResult = await pcaConfig.Build().AcquireTokenByUsernamePassword(new[] { baseUrl }, username, password).ExecuteAsync();

        // Load ps context
        var projectContext = new ProjectContext(url);
        projectContext.ExecutingWebRequest += (s, e) =>
        {
            e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + TokenResult.AccessToken;
        };

        projectContext.Load(projectContext.Projects);
        projectContext.ExecuteQuery();

        _context = projectContext;
}

}

The problem is the line of 'var pcaConfig' Can you make that up directly in Entra without an Azure Application?


Solution

  • .NET Framework

    You need to use the SharePointOnlineCredentials for Credentials prop. Also be careful to use the right pwa url (from documentation) :

    enter image description here

    In this example, the url should be equal to : "https://Contoso.sharepoint.com/sites/pwa"

    //URL should contain the pwa url ex: https://Contoso.sharepoint.com/sites/pwa
     var clientContext = new ProjectContext(url);
    
     var netcred = new NetworkCredential(username, password);
     var orgIDCredential = new SharePointOnlineCredentials(netcred.UserName, netcred.SecurePassword);
     clientContext.Credentials = orgIDCredential;
    
     var projects = clientContext.Load(clientContext.Projects);
     clientContext.ExecuteQuery();
    
     if(projects.Any())
       // use projects
    

    Complete example that create project and update fields : https://github.com/OfficeDev/Project-Samples/tree/main/Create-Update-Project-Samples/Create-Update-Project-Samples

    [EDIT] : Distinction .Net Core/Framework

    .NET Core / .NET 8

    As you mentionned in the comment there's no SharePointOnlineCredentials . You will need to obtain an OAuth access token, by creating an app registration and granting to it Sharepoint permission :

    1. In Microsoft Entra ID (Azure AD), create a new App registration.
    2. In the API permission, Add permission, and choose Sharepoint enter image description here
    3. Then configure Delegated permissions for Project (if you need only read, or only write, or both). You can also ask for AllSites.Manage but it will require an admin consent. enter image description here
    4. In Authentication, change Allow public client flows to Yes enter image description here
    5. From the Overview copy the Application ID that you will use in your code.

    Now that you have a client configured to get OAuth tokens, you can fetch the token using passwork flow from the Microsoft Entra ID token endpoint https://login.microsoftonline.com/common/oauth2/token.

    POST https://login.microsoftonline.com/common/oauth2/token Body : resource=https://...sites//pwa&client_id={appId}&grant_type=password&username={username}&password={userPass}

    You can use an HttpClient to do the post, or use any OAuth library to get the token.

    When you have a token you can use the ProjectContext to query Project :

    var context = new ProjectContext(web);
    
    context.ExecutingWebRequest += (sender, e) =>
    {
        var accessToken = // Get AccessToken
        e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken;
    };
    
    projectContext.Load(projectContext.Projects);
    projectContext.ExecuteQuery();
    

    A complete code example here (it uses ClientContext but it's similiar) : https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/using-csom-for-dotnet-standard