Sharepoint CSOM Authentication issue with .NET Core - c#

I'm new to Sharepoint online, and don't have an own account (yet), just an username/password from a client.
Need to build a service that gets the folder structure and archives from Sharepoint. And then allows to up/download archives.
Since the package
Microsoft.SharePointOnline.CSOM
is not compatible with .NET Core, I'm using this github solution that seems to cover the main functionality in an equal way: https://github.com/OneBitSoftware/NetCore.CSOM
I think there is nothing wrong with that so far - but when trying to connect using
SharePointOnlineCredentials
...I'm getting the error
PPCRL_REQUEST_E_PARTNER_HAS_NO_ASYMMETRIC_KEY
So I guess there's some account setting missing on the server side? Or am I following a wrong approach? I would have no problem implementing an OAuth access to get a Bearer token, but which API would that be, and how can I register an app for Sharepoint?
My research about API's and this particular error didn't result in anything yet, so I'm reaching out for help here.

Get NuGet package TTCUE.NetCore.SharepointOnline.CSOM.16.1.8029.1200.
You can also download an official package Microsoft.SharePointOnline.CSOM but it will attach wrong dlls to your project and you would need to change them according to the link from a different answer here - https://rajujoseph.com/getting-net-core-and-sharepoint-csom-play-nice/
Note - Your .NET Core project will compile, but it doesn't mean that it will work on, for example, linux. Those CSOM dlls are not finished and Microsoft is still working on them.(for a loooong time...)

Check the example below:
Create a .NET Core console app.
Add the references: Microsoft.SharePoint.Client.Portable.dll, Microsoft.SharePoint.Client.Runtime.Portable.dll, and Microsoft.SharePoint.Client.Runtime.Windows.dll.
Note: If the project has references to Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll, please remove them.
These references can be accessed by installing CSOM library into another project, and then navigating to installed nuget packages in the file directory:
c:\Users\user\\.nuget\packages\microsoft.sharepointonline.csom\\(version)\lib\netcore45
Add the code below to the .NET Core 2.0 console application:
using System;
using Microsoft.SharePoint.Client;
namespace ConsoleApp1 {
class Program {
static void Main(string[] args) {
string targetSiteURL = #"https://xxx.sharepoint.com/sites/xxx";
var login = "xxx#xx.onmicrosoft.com";
var password = "xxx";
SharePointOnlineCredentials onlineCredentials = new SharePointOnlineCredentials(login, password);
ClientContext ctx = new ClientContext(targetSiteURL);
ctx.Credentials = onlineCredentials;
WebCreationInformation wci = new WebCreationInformation();
wci.Url = "Site1"; // This url is relative to the url provided in the context
wci.Title = "Site 1";
wci.UseSamePermissionsAsParentSite = true;
wci.WebTemplate = "STS#0";
wci.Language = 1033;
var newWeb = ctx.Web.Webs.Add(wci);
ctx.Load(newWeb, w => w.Title);
ctx.ExecuteQueryAsync();
Console.WriteLine("Web title:" + newWeb.Title);
Console.ReadKey();
}
}
}
More information: Getting .NET Core and SharePoint CSOM Play Nice

Related

Service account is not authorized to manage project

I recently joined a team, and am adding Android Management Api to the already existing project. I added the management API, created service accounts with permissions, and am writing the .NET project to test it out.
I made a service account with Android Management User and Owner permissions. However, when I try to use the .NET library to make an enterprise, I get
The service androidmanagement has thrown an exception. HttpStatusCode is Forbidden. Caller is not authorized to manage project.
If it helps:
The API key I'm using is allowed to call any API, and the application name is a temporary one that does NOT match the project name. As for the service account with private key, I am using a FileStream to read a .json file downloaded when the service account was created.
This is my code, based on the sample app https://developers.google.com/android/management/sample-app
The error gets thrown on the createRequst.Execute()
string CreateEnterprise()
{
SignupUrlsResource.CreateRequest signupUrlRequest = managementService.SignupUrls.Create();
signupUrlRequest.ProjectId = cloud_project_id;
signupUrlRequest.CallbackUrl = "https://www.yahoo.com";
var signupUrl = signupUrlRequest.Execute();
string enterpriseToken = signupUrl.Url;
Console.WriteLine("Signup: " + enterpriseToken);
EnterprisesResource.CreateRequest createRequest = managementService.Enterprises.Create(new Enterprise());
createRequest.ProjectId = "Test Project";
createRequest.SignupUrlName = signupUrl.Name;
createRequest.EnterpriseToken = enterpriseToken;
var enterprise = createRequest.Execute();
return enterprise.Name;
}
Turns out the createRequest.ProjectId must match the project name that has the Android Management API, aka the project I'm working with.

Get Nuget credentials stored somewhere by Visual Studio in a VSIX project

I'm developing a Visual Studio extension (VSIX project) that needs to manage Nuget packages of a given project.
I'm already using the IVsPackageInstaller service as documented here but this is limited and I need more features (for example get the latest version number of a given package).
I searched but didn't find anything on how to programmatically interact with the Visual Studio Package Manager so I decided to go for the Nuget API directly.
I send HTTP requests to the Nuget API using the WebRequest class (because we can't use HttpClient in a VSIX project) but I'm hitting a problem: the requests are going to a private Nuget feed that needs authentication! (hosted on Azure DevOps)
I used Fiddler to check the HTTP requests sent to our Azure DevOps server. I see a POST request going to https://app.vssps.visualstudio.com/_apis/Token/SessionTokens with a token in response but this is not the Token I'm looking for.
The token passed to the Nuget API is a Basic token that comes from I don't know where. I couldn't find this token anywhere in the HTTP responses I caught.
I can also see that some responses to our Azure DevOps server contain some headers like this (I changed the GUID)
WWW-Authenticate: Bearer authorization_uri=https://login.windows.net/ce372fcc-5e17-490b-ad99-47565dac8a84
I can find this GUID back in the %userprofile%\AppData\Local\.IdentityService\AccountStore.json file, there is definitely something going on here. And the SessionTokens.json file in the same folder looks reeeaaally interesting too but it's encrypted...
I also tried to dig in the Registry to see if I can find interesting information for example at the path specified in Simon's comment but it seems VS2017 doesn't store the token there anymore.
I also loaded the privateregistry.bin file (aka the Visual Studio Settings Store) and searched everywhere but couldn't find anything.
So instead of trying to reverse engineer Visual Studio I wanted to access its Credential Provider directly. I tried to access to several services and classes
var componentModel = await ServiceProvider.GetGlobalServiceAsync(typeof(SComponentModel)) as IComponentModel;
var credentialProvider = componentModel.GetService<IVsCredentialProvider>();
var credentialServiceProvider = componentModel.GetService<ICredentialServiceProvider>();
var defaultCredentialServiceProvider = new DefaultVSCredentialServiceProvider();
But none of them are working (return null or Exception).
I wandered in the NuGet.PackageManagement.VisualStudio project on Github but couldn't find my answer.
There are also many Nuget packages like NuGet.PackageManagement.VisualStudio, Microsoft.VisualStudio.Services.Release.Client, Microsoft.VisualStudio.Services.ExtensionManagement.WebApi, Microsoft.VisualStudio.Services.InteractiveClient just to name a few but honestly I don't know if what I'm looking for is there...
So how to access the Nuget credentials used by Visual Studio?
I take any solution that gives me access to all the reading Nuget features, for example programmatically use the Visual Studio Package Management, or decrypt this SessionTokens.json file or access the Visual Studio Credential Provider.
The less hacky is the answer, the better it is of couse.
At this point you probably already guessed, I don't want to store the username and password somewhere myself. I need to create a user-friendly VS extension, that's why I want to retrieve and use the credentials already saved in Visual Studio by the users.
Thank you so much if you can solve this problem.
NuGet Client SDK
Thanks a lot to Simon who pointed me in the direction of NuGet.Client.
The only documentation from Microsoft is linking a 2016 blog post from Dave Glick but they also give a nice note:
These blog posts were written shortly after the 3.4.3 version of the NuGet client SDK packages were released. Newer versions of the packages may be incompatible with the information in the blog posts.
Alright, then I guess we will do with Dave's blog...
You should install two packages: NuGet.Client and Nuget.Protocol
Then here is the code for example to get the last version of a package:
using NuGet.Configuration;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MyProject
{
public class NugetHelper
{
public async Task<string> GetLatestVersionNumberFromNugetFeedAsync(NugetPackage package)
{
try
{
Logger logger = new Logger(); //Just a class implementing the Nuget.Common.ILogger interface
List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
providers.AddRange(Repository.Provider.GetCoreV3()); // Add v3 API support
PackageSource packageSource = new PackageSource(package.Source.ToString());
SourceRepository sourceRepository = new SourceRepository(packageSource, providers);
PackageMetadataResource packageMetadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>();
var searchMetadata = await packageMetadataResource.GetMetadataAsync(package.Name, false, false, new SourceCacheContext(), logger, new CancellationToken());
var versionNumber = searchMetadata.FirstOrDefault().Identity.Version.OriginalVersion;
return versionNumber;
}
catch (Exception ex)
{
return null;
}
}
}
public class NugetPackage
{
public string Name { get; set; }
public string Version { get; set; }
public string MinimumVersion { get; set; }
public Uri Source { get; set; }
}
}
Physical Token Storage Place
I tried to reverse engineer where Visual Studio was storing the token used in HTTP requests to the Nuget API.
I exported to text files all the different hives of the Registry including the Visual Studio Settings Store (privateregistry.bin).
Then added a brand new Nuget feed in Visual Studio, got the login popup as expected so I logged in.
Finally I exported all the hives again to text files and compared them all with the files before authentication.
I found nothing interesting in the VS Settings Store.
The only interesting changes were
[HKEY_CURRENT_USER\Software\Microsoft\VSCommon\ConnectedUser\IdeUserV2]
#="0746fb8e-4bc2-4ee5-b804-0084af725deb"
"AccountsRoaming_LastAccountsSettingVersion"=dword:0000025b
[HKEY_CURRENT_USER\Software\Microsoft\VsHub\ServiceModules\Settings\PerHubName\vshub\ConnectedUser\IdeUserV2\Cache]
"LastProfileVersion"=dword:10b8260a
and
[HKEY_USERS\S-1-5-21-1787888774-1556370510-3519259403-1001\Software\Microsoft\VSCommon\Keychain]
"TokenStorageNameSpace"="VisualStudio"
[HKEY_USERS\S-1-5-21-1787888774-1556370510-3519259403-1001\Software\Microsoft\VsHub\ServiceModules\Settings\PerHubName\vshub\ConnectedUser\IdeUserV2\Cache]
"LastProfileVersion"=dword:10b8260a
Maybe somewhere, there is the key to these encrypted SessionTokens.json and IdentityServiceAdalCache.cache files but having the data stored in hexadecimal makes things even harder.
I've to give up on this, almost no chance I could reverse engineer the authentication system.
Visual Studio credentials provider
The NuGet Client SDK solves my issue but doesn't actually answer to this SO question.
As I said, I tried to call
var componentModel = await ServiceProvider.GetGlobalServiceAsync(typeof(SComponentModel)) as IComponentModel;
componentModel.GetService<ICredentialServiceProvider>()
But this didn't work, so if anybody knows how to access the Visual Studio credentials provider, I would be really glad to know the answer.

List all AWS RDS instances using AWS .NET SDK

I'm trying to list all my RDS instances on AWS, using the .NET SDK for AWS.
I was expecting the SDK to offer something similar to the SDK's EC2 describe-instances, and sure enough, that is part of the CLI, but not so straight-forward in the SDK.
Does anyone know how to do this ?
Solution
The AWS .NET SDK (v3) contains a similar construct for RDS as for EC2. I missed that somehow. See my answer with source-code below.
Thanks in advance
I think you are looking for DescribeDBInstances. The DescribeDBInstancesResult has a list of DBInstances. That's where you'll find the information on each RDS instance.
Edit: The function and object names are the same but here's the link for V3.
So it turns out, that the procedure to get all RDS instances closely mimic the EC2 way of doing it.
You will need to install the AWSSDK.RDS nuget package
In Package Management Console in VS.NET
Install-Package AWSSDK.RDS
Once you have done that, you will need to add the necessary assemblies:
using Amazon.RDS;
using Amazon.RDS.Model;
And then you can do something like this:
public static void ListAllRDSInstances(RegionEndpoint region)
{
var c = new AmazonRDSClient(region);
var request = new DescribeDBInstancesRequest();
var response = c.DescribeDBInstances(request);
response.DBInstances
.ForEach(instance => {
//do stuff for each instance in region
});
}

Using Google Drive .NET API with UWP App

I am attempting to use Google Drive as a storage location in my UWP application. I started at the quickstart provided by Google. I copy the code into a blank UWP project, change some of the output code (Console.Writeline to a textbox.append method) and I try to build it. It fails to build and reports the error:
Cannot find type System.ComponentModel.ExpandableObjectConverter in module System.dll
I am running Windows 10 and VS 2015 and I have installed the sdk through NuGet. The example code in the quickstart does work in a console application. It is the UWP application that is having issues.
For the UWP application, I put the quickstart code in a button click method. This was because the API actually has an async method for the uwp apps which is a bit different then the code given in the quickstart.
Includes:
using System;
using System.Collections.Generic;
using System.IO;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Drive.v3.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.Threading;
The Button Method:
private async void button_Click(object sender, RoutedEventArgs e)
{
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = ""; //System.Environment.GetFolderPath(
//System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/drive-dotnet-quickstart.json");
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new Uri("ms-appx:///Assets/client_secrets.json"),
Scopes,
"user",
CancellationToken.None);
//Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.PageSize = 10;
listRequest.Fields = "nextPageToken, files(id, name)";
// List files.
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
.Files;
textBox.Text += "Files:\n";
if (files != null && files.Count > 0)
{
foreach (var file in files)
{
textBox.Text += (file.Name + file.Id + "\n");
}
}
else
{
textBox.Text += ("No files found.");
}
}
The test code will not work once the app is compiled as it is missing the code to load the client secret. Since I have not been able to test the code, this is all I can provide.
There is another post that is semi-related except that the answer is just that it wont work and the post has been dead for 4 years. I also wanted to create a new post that tags the google team specifically (like the quickstart says to do).
My specific question is: Is there a work around to this issue or am I just doing this wrong?
I agree with #Vincent, UWP apps use COM as a base and builds from there. Not all .Net API can be used in UWP apps, this SDK is based on .Net APIs, this is why your console app is OK, but your UWP app is down. For the differences between them, here is a great answer which explain this issue. But,
"You will need an UWP SDK from Google to build an UWP applications."
I just tried to search for this without any luck, but here is a suggestion, you can use JavaScript to make request to the Drive API. To do this, you can refer to JavaScript Quickstart. Then you can turn it to a web hosted UWP app, for more information, you can refer to Convert your web application to a Universal Windows Platform (UWP) app.
Another suggestion which can probably make the work easier is using Rest API to send HTTP requests, you can also refer to API Reference.
The final suggestion which is as #Vincent said, if you have access to the SDK code, you can also try to adapt it for UWP. It means you need to modify the source code of this SDK.
The .Net flavor used to build Windows Store/UWP apps has less features than the full .Net framework. Unfortunately, the ExpandableObjectConverter object is not available for UWP applications.
You will need an UWP SDK from Google to build an UWP applications.
If you have access to the SDK code, you can also try to adapt it for UWP.

How to authenticate to Visual Studio Team Services with the new basic authentication from a .Net Windows Service?

I am trying to access https://visualstudio.com (formerly known as https://tfs.visualstudio.com, http://www.tfspreview.com) from my Windows Service written on .NET.
I want to use the new basic authentication but I couldn't find a way to do it.
I found a lot of links to the blog post Team Foundation Service updates - Aug 27 but it is using the Team Explorer Everywhere Java client for TFS.
Is there a new version of the TFS .NET Object Model to support the basic authentication?
By the way I've successively logged in with the service account. This answer was very useful.
First of all, you need to have at least Visual Studio 2012 Update 1 installed on your machine. It includes an updated Microsoft.TeamFoundation.Client.dll assembly with the BasicAuthCredential class.
Here's the code to do it, from Buck's blog post How to connect to Team Foundation Service.
using System;
using System.Net;
using Microsoft.TeamFoundation.Client;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
NetworkCredential netCred = new NetworkCredential(
"yourbasicauthusername#live.com",
"yourbasicauthpassword");
BasicAuthCredential basicCred = new BasicAuthCredential(netCred);
TfsClientCredentials tfsCred = new TfsClientCredentials(basicCred);
tfsCred.AllowInteractive = false;
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(
new Uri("https://YourAccountName.visualstudio.com/DefaultCollection"),
tfsCred);
tpc.Authenticate();
Console.WriteLine(tpc.InstanceId);
}
}
}
There have been some updates to authentication. For .NET apps we now recommend using VSTS client libraries. Another option is to use Azure Active Directory Libraries (ADAL). For more information and samples check out VSTS's authentication documentation.

Categories