What is the best way to access SharePoint in C# - c#

I'm updating an Excel complement I made 2-3 years ago with C#. The goal is to get some files that are stored on a SharePoint site, copy them locally and then open them. I have permission to access the SharePoint site, but I have no admin right over the setting of the SharePoint (it is run by our IT service and the company Security is tight regarding data protection). We use MFA to log in to our Windows session and after that we can access the SharePoint and other services without need to input our password again. Until now I have used the code below, and it still work perfectly:
using Microsoft.SharePoint.Client;
using OfficeDevPnP.Core;
string tempFileName = System.IO.Path.GetTempPath() + "filename.xlsx";
AuthenticationManager mgr = new AuthenticationManager();
ClientContext context = mgr.GetWebLoginClientContext("https://xxx.sharepoint.com/teams/mypage");
FileInformation fileinfo = File.OpenBinaryDirect(context, "ServerRealtivePath");
context.ExecuteQuery();
System.IO.FileStream fStream = new System.IO.FileStream(tempFileName, System.IO.FileMode.Create);
await fileinfo.Stream.CopyToAsync(fStream);
fStream.Close();
fileinfo.Stream.Close();
So why try to fix something that is not broken… yet? The NuGet package SharePointPnPCoreOnline containing the OfficeDevPnP NameSpace is now marked as being retired and it is recommended to use PnPFramework instead. But the PnPFramework do not contained the AuthenticationManager.GetWebLoginClientContext() method. With the ever-growing need for data protection and new technology, I’m expecting the current method will stop working at some point. Do you have an equivalent method to connect to a sharepoint using a more modern way?
I don’t have any permission to register the app with Azure and I’m guessing it will be a big No from our IT service. I don’t mind asking the user to input his login at some point if needed. I never used REST or GRAPH API but if it can help, I can look into it. I want the right to access the files base on the current user permission. If the user doesn’t have permission to access the specified file, I don’t want the app to be able to download it.
I’m open to suggestions, Thanks

For my tool, I've just reworked the code from the GetWebLoginClientContext, it's neither big nor complicated. Here is also a newer version of it. So, what's inside: a simple form with a web browser control (that is based on Internet Explorer). When the browser control authenticates the user, the code gets the authentication cookies from the browser control using the platform web browser API, and uses that in the subsequent calls to SharePoint.
What can break here: Internet Explorer (and thus, the browser control) is deprecated, and the support for it ended last year. If the authentication window stops working when opened from a browser control, that would be a problem.
I've "fixed" that for myself by using the WebView2 instead of the browser control, and since it's evergreen, it should be fine. It also provides API to get cookies that we need to call the SharePoint.
I don't think the approach with cookie authentication is a problem by itself, but they may change cookies some day and then the app may need to be updated correspondingly then if that happens.
A more "robust" approach would be to register an application anyway in Azure AD (actually you don't absolutely have to ask your admins to register the app, you can register one for yourself without asking anyone, in your own "organization").
With this approach, the user must consent to use the app (to allow the app to access the data in the organization). The admin consent may be required, but it depends on organization settings (by default it's not required, user consent is good enough).
Please note that connecting using an "app" is actually more secure because when you grant access to an app, you only give it specific permissions (i.e. you get an intersection of the user permissions and the permissions that were granted to the app). When connecting as a user (i.e. using the "cookies" approach above), you get full access (i.e. you can do anything the user can).
Another point, for the app you don't really have to build anything on the web (no website is actually needed); the "callback url" to get the access token can be hosted in the application itself (localhost), or the app can be configured to use device code.

Related

C# login and upload to OneDrive without user interaction

I'm currently working on a Windows service that will check and update Excel files and upload them to selected cloud storage - SharePoint or OneDrive. The whole process should be fully automatic and without any user interaction - all required information (username, password etc.) are part of the config file.
All is going well except the OneDrive part. I'm unable to find a fully automatic solution to login and upload to this cloud storage. I know about Microsoft Live SDK, but 'its support for non-WinPhone and -WinStore apps is reduced and also, to my knowledge, it always requires user to enter username, password (webbrowser component).
The second option is SkyDriveClientAPI (link here), but this API doesn't work anymore (as mentioned in Issues).
Is there way to use Live SDK without user interaction or do you have any other suggestions for a different way?
This is partly achievable
As mentioned in the other answers, one drive requires user authentication , and this makes fully automatic solution impossible.
However an almost automatic solution is possible, meaning an app can be created that will require a single one time login (on first activation) and a following the login, an authorization by the user for the running app, from that point the app will be able to work automatically (
uploading, downloading, folder creation, direct link to uploaded files, etc....), this will continue working, even if application is shutdown, and machine is restarted.
the only way to disengage the application from the one drive connection is to sign out the app (can be achieved automatically).
This can achieved by using the LIVE sdk (desktop) and following the single sign-on guidelines,
you will most likely need access to the following scopes:
wl.basic
wl.signin
wl.skydrive_update
wl.offline_access
check one drive dev center:
window desktop apps
Single sign-on for apps and websites
In order to achieve this behavior i suggest following the next steps:
Download the live desktop source code
Explore the ApiExplorer sample (Live SDK Desktop Code + ApiExplorer code sample) -
this will show you how to login, logout ,download, and upload content.
After using the example to build a basic demo App:
enhance your App to use single sign-on capabilities,
enhance your App to refresh its authentication token (so session wont time out, etc)
You can find information on their API here. Microsoft's One Drive truly aims to be secure and respect the users privacy. Which is why so many permission controls exist in the platform. They truly emphasize the desire to not have orphaned files or applications abusing the automated / upload process without the user expressing permission. To clarify directly from their API, their explicit guidelines are as follows:
Guidelines for apps that interact with OneDrive
Apps that interact with OneDrive must conform to these principles: Upload files to
OneDrive only in response to an explicit user request or choice. Your
apps must always ensure that a user intentionally chooses to save any
new data to OneDrive. Apps must not upload files to OneDrive
automatically without a user making an explicit choice to upload those
files. Here are some examples of conforming apps:
Apps that display an "Upload to OneDrive" or "Share on OneDrive" button that a user must click before each upload of a photo, video, document, or other file.
Document-editing apps that require a user to click an "Upload to
OneDrive" button initially, so that the app can save that document
later without further user interaction.
Here are some examples of nonconforming apps:
Apps that automatically upload to OneDrive any file added to a specific location on a user’s devices.
Apps that automatically back up files or folders to OneDrive.
Use OneDrive for the things that it’s good at. OneDrive includes features both for
high-quality document viewing and editing, and for creating and
sharing beautiful photo albums. If possible, have your apps take
advantage of these features. Don't undermine trust in OneDrive. Over
the years that OneDrive has been available, users have come to trust
it. Preserving that trust is critical and your apps must not undermine
it by doing things that users don’t expect, especially with regard to
data privacy.
Here are some examples of conforming apps:
Apps that upload documents or photos to OneDrive with user-only access as the default.
Apps that warn users that, when the users send a link to their content stored on OneDrive, anyone who receives that link can read the associated files.
Here's an example of a nonconforming app:
An app that makes all shared files in OneDrive publicly accessible by
default, without clearly communicating this behavior to users.
That particular excerpt above, is from here which directly correlates to One Drive's API.
You can circumvent the process of the user, by using the shared One Drive folder. Which will automatically upload / synchronize data. The pitfall though, is if you use the File.Move or File.Copy without explicitly telling the user this file will be stored in your shared file to be uploaded by One Drive. Then you could be violating the terms.
You'll also have to worry about the user related permissions / account to implement that approach.
Two years late for answering, but I think this will be helpful for future use.
Once you registered your application you can get access token without the popup window, see this post Getting Access Token for Microsoft Graph Using OAuth REST API. how to get token (a postman example), I wrote the following code based on this postman example:
A c# exapmle for getting token without the popup window:
public async Task GetTokenAsync(string tenant, string clientId, string clientSecret, string username, string password)
{
HttpResponseMessage resp;
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
var req = new HttpRequestMessage(HttpMethod.Post, $"https://login.microsoftonline.com/{tenant}/oauth2/token/");
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"grant_type", "password"},
{"client_id", clientId},
{"client_secret", clientSecret},
{"resource", "https://graph.microsoft.com"},
{"username", username},
{"password", password}
});
resp = await httpClient.SendAsync(req);
string content = await resp.Content.ReadAsStringAsync();
var jsonObj = new JavaScriptSerializer().Deserialize<dynamic>(content);
string token = jsonObj["access_token"];
Console.WriteLine(token);
}
}
Once you have token it's easy to sign in.
OneDrive offers a API for uploading files. But one of the limitations in their guidelines is that all uploads via the API has to be user initiated or used accepted. So you can't upload files to OneDrive as a fully automatic process.
One solution may therfor be as gooly suggested in a comment. Copy the files to the local OneDrive folder and let the OneDrive application take care of the upload.

Adding Windows Live ID to the credential store

I have written an application that maps a user's SkyDrive to a network drive.
However when opening word documents directly from the network drive, Microsoft Office 2010 tries to load the document using WebDav and prompts for the user to login in again.
When you login it works fine, and if you click "Remember Me" it stores the credentials in the credential store, accessible via rundll32.exe keymgr.dll, KRShowKeyMgr
This only lasts until the session is closed.
I want to incorporate the automatic storing of the user's credentials into my program so that the user doesn't get prompted at all.
I have tried using the CredWrite API however that only allows me to store generic credentials. I need to store a (.Net Passport) credential.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa375187(v=vs.85).aspx
I have only been able to achieve this using the CredUIPromptForCredentials API with the flags set to CREDUI_FLAGS_SERVER_CREDENTIAL
http://msdn.microsoft.com/en-us/library/windows/desktop/aa375177(v=vs.85).aspx
I have inspected the API call that word makes to credui.dll and it is identical to the one I am calling (aside from some captions).
But it produces different results.
My code produces this credential to be saved
The word api call/subsequent code produces this credential
However I cannot figure out how to get the credential target to save correctly so that office will load it without prompting. I'd prefer to even have the prompt load if there is no option as I can pre fill the username and password so that the user just has to click OK but I'd like to do it silently.
Additionally if there is anyway to disable word's WebDav support/force it to use the network drive as an actual network drive that would also work. I have been unable to find any answer. If anyone has any idea I would greatly appreciate it.

How can I access user roles/permissions with SSRS and BIDS Custom Security Extension?

I have written and deployed a custom security extension for SSRS 2008r2 and it works beautifully apart from when trying to deploy reports from within BIDS 2008/Visual Studio.
The architecture for the security extension handles logins via multiple authorities and I manage this by creating a user session in LogonUser(), persisting this in a database and then loading the session in the client proxy class for the RS web service using a UID passed by cookie from the service, then rewriting the authentication ticket to contain the session UID plus user roles for the current user. These values can then be used in the custom authorization extension to manage user permissions on SSRS actions and objects.
The problem I have run into is that it does not work when deploying reports from BIDS. Its accesses the service directly and therefore the service proxy class is not required. I have tried handling the post-authentication event on the HTTP context but alas the session cookie is not persisted by the RS service so I cannot access the session values.
So what am I missing? Is there another method for controlling user roles & permissions that does not require hard coding usernames anywhere? Like I said, logins can be made using multiple authorities so managing permissions via just a username is not possible (and the thought of it makes me cringe).
One possible solution I thought of is writing an extension or plugin for BIDS that essentially acts as a proxy for the web service which would allow me more control over the login process, but I have no idea if this is possible (google is no help...)
Any help would be gratefully accepted!
Are you using the IAuthenticationExtension interface? The following link provides a sound example of access control if that is what you require.
http://blogs.msdn.com/b/jameswu/archive/2008/07/15/anonymous-access-in-sql-rs-2008.aspx
I would imagine that the nt username can be tracked here and then lookups made against active directory groups etc. The main headache I can see here would be enabling the correct trust policy in the ssrs policy configuration.
Well I gave up on finding an elegant solution so I have gone down the road of least resistance and followed what I gather is the SSRS guideline of having a 'master' account, which I will specify is to be used for report deployment and subscriptions. This account will need to be hard coded in or pulled from a settings file I'm not sure if there is any other way. Cheers.
Update: I have gone down this road and it works fine. If a user is logged in through the web front end they can deploy reports there, but if they want to deploy from the development environment (BIDS) they need to log in using the master account when it the login dialog shows. I have restricted the deploying account to only this specific use.

How to Elevate Role in ASP.net From anonymous to windows auth?

I have a few websites which allow both anonymous and window auth users at the same time. Basically if you hit the site with IE or Webkit based browsers on a windows system, the server instantly recognizes your active directory user and group.
In the past I've provided a link to a windows auth only page which allows the current user to login, or bounce back to where they started.
I find the management of this kind of frustrating as I need to make certain that IIS has the correct security settings for that single page after every deployment.
Is there a better way for me to allow a user to elevate from anon to authenticated?
There is no other way to do that elevation automatically. The server can't know that the current user is a Windows user and elevate them, or automatically redirect them to the Windows auth only page. On the other hand, if every user will get through the Windows auth only page, all of them which are not inside the domain will see the challenge/response dialog box (user + password).
As for the management part of making sure that this special page has the correct security settings, you can (and should) automate the check somehow. For example, by querying the IIS metabase for that setting when the application starts (in Global.asax) and if the setting is not there, log it as an email message or so.
Personally I prefer a different attitude - a special "integration/deployment" page which contains a series of tests against my application so I can make sure everything's set up correctly on the server, i.e. NTFS write permissions to certain folders, availability of the SMTP server set in web.config to send emails through, etc.
Note: You're using Windows authentication along with anonymous access. Just keep in mind that if you consider implementing Forms Authentication in the future, a misarchitecture (I don't know if by design or due to a flaw) of IIS 7 does not allow you to set the app to be Forms Authentication and set one specific page to be Windows Authentication. The override just doesn't work and it's very frustrating.
Good luck!
OK, I figured out a fairly nice way to do this...however it's not as elegant as I had hoped since it doesn't work across applications.
Basically, if you create a single page within your Anon + Integrated Auth IIS6 website or virtual directory, let's call it auth.aspx, then you can use this page to prompt authentication.
Go into IIS settings and specify that auth.aspx is Integrated Auth ONLY (no anon). Then create a hidden iframe somewhere on your page. I then created a simple JavaScript action to update the src attribute of the iframe to the auth.aspx page. This forces the browser to try and authenticate using NTLM. Once you enter valid credentials you've successfully elevated your current user beyond the generic anonymous user.
One final touch was to then include a Response.Redirect into the auth.aspx which reloads the current page. Assuming your ASP.net session tokens are set correctly, the page will reload and the user will be authenticated.

How would I authenticate against a local windows user on another machine in an ASP.NET application?

In my ASP.NET application, I need to be able to authenticate/authorise against local Windows users/groups (ie. not Active Directory) on a different machine, as well as be able to change the passwords of said remote local Windows accounts.
Yes, I know Active Directory is built for this sort of thing, but unfortunately the higher ups have decreed it needs to be done this way (so authentication against users in a database is out as well).
I've tried using DirectoryEntry and WinNT like so:
DirectoryEntry user = new DirectoryEntry(String.Format("WinNT://{0}/{1},User",
serverName, username), username, password, AuthenticationTypes.Secure)
but this results in an exception when you try to log in more than one user:
Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
I've tried making sure my DirectoryEntries are used inside a using block, so they're disposed properly, but this doesn't seem to fix the issue. Plus, even if that did work it is possible that two users could hit that line of code concurrently and therefore try to create multiple connections, so it would be fragile anyway.
Is there a better way to authenticate against local Windows accounts on a remote machine, authorise against their groups, and change their passwords?
Thanks for your help in advance.
In my opinion you cannot do this from your ASP.Net script. Because from the server you need to know what all client machines will access your page and provide sufficient rights to the server to access the client to do this extra user authentication and password change. Also this is cumbersome. One solution is to use an activex control and write this logic (user authentication and password change) in that activex control and provide the activex control sufficient rights in the clients. It is a bit ugly but this is the only possible solution without ADS.

Categories

Resources