I'm trying to authenticate up against Sharepoint so that it's possible for me to upload files onto a specific Sharepoint site.
I'm trying to use an X.509 certificate to retrieve the access token, but I keep getting (401): Unauthorized.
Here's the way I try to retrieve the access token with the certificate:
string authority = SettingsHelper.Authority;
string clientID = SettingsHelper.ClientId;
string serverName = SettingsHelper.SharepointServerName;
//Retreive the certificate path
string certFile = Server.MapPath(SettingsHelper.CertificatePath);
string certPassword = SettingsHelper.CertificatePassword;
AuthenticationResult authenticationResult = null;
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
//Create the certificate file, using the path (certFile), password (certPassword) and the MachineKeySet
X509Certificate2 cert = new X509Certificate2(certFile, certPassword, X509KeyStorageFlags.MachineKeySet);
//Create the ClientAssertionCertificate using the clientID and the actual certificate
ClientAssertionCertificate cac = new ClientAssertionCertificate(clientID, cert);
//Retreive the access token using the serverName and client assertion
authenticationResult = authenticationContext.AcquireToken(serverName, cac);
And here's how I try to upload a specific file onto a specific Sharepoint list:
WebRequest request = null;
HttpWebResponse response = null;
byte[] bytesToUpload = bytes;
var returnValue = "";
string requestUriString = string.Format("{0}/_api/web/GetFolderByServerRelativeUrl(#sru)/Files/Add(url=#fn,overwrite=true)?#sru='{1}'&#fn='{2}'", url, HttpUtility.UrlEncode(serverRelativeUrl), HttpUtility.UrlEncode(fileName));
request = (HttpWebRequest)HttpWebRequest.Create(requestUriString);
request.Method = "POST";
(request as HttpWebRequest).Accept = "*/*";
request.ContentType = "application/json;odata=verbose";
request.Headers.Add("Authorization", String.Format("Bearer {0}", authenticationResult.AccessToken));
request.ContentLength = bytesToUpload.Length;
// Write the local file to the remote system
using (Stream requestStream = request.GetRequestStream())
{
BinaryWriter writer = new BinaryWriter(requestStream);
writer.Write(bytesToUpload, 0, bytesToUpload.Length);
writer.Close();
}
// Get a web response back
response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.Default))
{
returnValue = sr.ReadToEnd();
sr.Close();
}
if (request.RequestUri.ToString().Contains("GetFolderByServerRelativeUrl") == true)
{
returnValue = "";
}
Some of the variables comes from the parameters:
UploadEmail(System.IO.File.ReadAllBytes(emlFilePath), "https://(blablabla).sharepoint.com", "sites/(bla)/(bla)/Emails", email.Subject + ".msg");
I'm not sure what's wrong, and I'm definitely not sure how to fix this.
NOTE: Please don't tell me to use NetworkCredentials, I'd rather use the certificate or something else, but not NetworkCredentials
EDIT
Managed to debug the code and find this in the response header of the WebRequest:
The better approach would be using the SharePoint Client Side Object Model (as hbulens suggested in comments).
Here's the code that uploads the file to the library in O365 (just replace the string literals with your own details):
string username = "YOUR_USERNAME";
string password = "YOUR_PASSWORD";
string siteUrl = "https://XXX.sharepoint.com";
ClientContext context = new ClientContext(siteUrl);
SecureString pass = new SecureString();
foreach (char c in password.ToCharArray()) pass.AppendChar(c);
context.Credentials = new SharePointOnlineCredentials(username, pass);
Site site = context.Site;
context.Load(site);
context.ExecuteQuery();
Web web = site.OpenWeb("YOUR_SUBSITE");
context.Load(web);
context.ExecuteQuery();
List docLib = web.Lists.GetByTitle("YOUR_LIBRARY");
context.Load(docLib);
FileCreationInformation newFile = new FileCreationInformation();
string filePath = #"YOUR_LOCAL_FILE";
newFile.Content = System.IO.File.ReadAllBytes(filePath);
newFile.Url = System.IO.Path.GetFileName(filePath);
Microsoft.SharePoint.Client.File uploadFile = docLib.RootFolder.Files.Add(newFile);
context.Load(uploadFile);
context.ExecuteQuery();
You can run it in console application. Two dll's that you need to reference are:
Microsoft.SharePoint.Client.dll
Microsoft.SharePoint.Client.Runtime.dll
Related
I am using .net 4.8 WebClient and the Microsoft.SharePointOnline.CSOM package, trying to download files from SharePoint online.
const string username = "myusername#domain.com";
const string password = "mypassword";
const string url = "https://tenant.sharepoint.com/:b:/r/filepath.pdf";
var securedPassword = new SecureString();
foreach (var c in password.ToCharArray())
{
securedPassword.AppendChar(c);
}
var credentials = new SharePointOnlineCredentials(username, securedPassword);
using (var client = new WebClient())
{
client.Credentials = credentials;
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
client.DownloadFile(url, "C:\\temp\\test.pdf"); //this file path definitely exists
}
The error I get is a 401 unauthorized.
I logged out and logged into the SharePoint environment using the same credentials, to ensure that they are correct.
I've tried adding a couple different user agents in the header to see if that helps.
client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
//or
client.Headers.Add("User-Agent: Other");
But it didn't make any difference.
This is similar to Download File From SharePoint 365 which is using what looks like the same code as I am, but Download Document from SharePoint Online using c# webclient stopped working suggests that something has changed and this form of authentication no longer works for connecting to SharePoint online. However I have been unable to find any recent documentation on what we're supposed to use now.
You could refer to the code provided by official document.
https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/working-with-folders-and-files-with-rest
As I test,it works well.
public static void DownloadFileViaRestAPI(string webUrl, ICredentials credentials, string documentLibName, string fileName, string path)
{
webUrl = webUrl.EndsWith("/") ? webUrl.Substring(0, webUrl.Length - 1) : webUrl;
string webRelativeUrl = null;
if (webUrl.Split('/').Length > 3)
{
webRelativeUrl = "/" + webUrl.Split(new char[] { '/' }, 4)[3];
}
else
{
webRelativeUrl = "";
}
using (WebClient client = new WebClient())
{
client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
client.Credentials = credentials;
Uri endpointUri = new Uri(webUrl + "/_api/web/GetFileByServerRelativeUrl('" + webRelativeUrl + "/" + documentLibName + "/" + fileName + "')/$value");
byte[] data = client.DownloadData(endpointUri);
FileStream outputStream = new FileStream(path + fileName, FileMode.OpenOrCreate | FileMode.Append, FileAccess.Write, FileShare.None);
outputStream.Write(data, 0, data.Length);
outputStream.Flush(true);
outputStream.Close();
}
}
static void Main(string[] args)
{
string siteURL = "https://contoso.sharepoint.com/sites/dev";
//set credential of SharePoint online
SecureString secureString = new SecureString();
foreach (char c in "password".ToCharArray())
{
secureString.AppendChar(c);
}
ICredentials credentials = new Microsoft.SharePoint.Client.SharePointOnlineCredentials("amos#contoso.onmicrosoft.com", secureString);
//set credential of SharePoint 2013(On-Premises)
//string userName = "Administrator";
//string password = "xxxxxxx";
//string domain = "CONTOSO";
//ICredentials credentials = new NetworkCredential(userName, password, domain);
DownloadFileViaRestAPI(siteURL, credentials, "lib", "test.css", "c:\\SPFX\\");
Console.WriteLine("success");
Console.ReadLine();
}
We use Multi-factor authentication on our Sharepoint. We are trying to download some documents from sharepoint to manipulate in our application. I have tried to access the sharepoint using both the "traditional" method as well as trying to go thru our Azure AD. Both methods dont allow us to access the sharepoint - we get permission denied
Any help would be greatly appreciated
This is what i have tried already - going thru AD
string clientId = "xxx";
string clientSecret = "yyy";
string tenant = "zzz";
AuthenticationResult result = null;
var data = AzureAdOptions.Settings.Authority;
AuthenticationContext authContext = new
AuthenticationContext(AzureAdOptions.Settings.Authority);
ClientCredential credential = new
ClientCredential(AzureAdOptions.Settings.ClientId,
AzureAdOptions.Settings.ClientSecret);
result = await
authContext.AcquireTokenAsync(AzureAdOptions.Settings.PreceptWebApiResourceId, credential);
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
and then the more "traditional way"
WebRequest request = WebRequest.Create("https://my.sharepoint.com:/sites/files/");
string userName = "mylogon";
string password = "#!";
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
request.Credentials = new NetworkCredential(userName, securePassword);
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(userName + ":" + password));
request.Headers.Add("Authorization", "Basic " + encoded);
WebResponse response = request.GetResponse();
Replace the line of code below
request.Credentials = new NetworkCredential(userName, securePassword);
with
request.Credentials = new SharePointOnlineCredentials(userName, securePassword);
Or using SharePointPnPCoreOnline.
var authManager = new AuthenticationManager();
var ctx = authManager.GetWebLoginClientContext("https://contoso.sharepoint.com/");
Refer to: CSOM.MFA.cs
I'm triggering an external web page outside of sharepoint which needs to read lists using the sharepoint web api.
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(sharepointUrl.ToString() + "/_api/web/lists");
endpointRequest.Method = "GET";
endpointRequest.Accept = "application/json;odata=verbose";
//endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);
HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse();
I can access the API using chrome if I'm logged in but I suspect I need the access token line.. but can't seem to find a way to populate it.
Currently it returns:
No connection could be made because the target machine actively refused it...
You need to set credential of HttpWebRequest.
Here you go:
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(sharepointUrl.ToString() + "/_api/web/lists");
string password = "XXXXX";
string userName = "XXXX";
SecureString secureString = new SecureString();
foreach (char c in password.ToCharArray())
{
secureString.AppendChar(c);
}
endpointRequest.Credentials = new SharePointOnlineCredentials(userName, secureString);
//.........
If you are using SharePoint online, it can work with my answer above because i have tested.
If you are using SharePoint 2013/2010/2016, the code will be as below.
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(sharepointUrl.ToString() + "/_api/web/lists");
string password = "XXXXX";
string userName = "XXXX";
string domain = "XXX"
endpointRequest.Credentials = new NetworkCredential(userName, password, domain);
//.........
I was try to post the excel data in azure data catalog. i was written in console Application.
my code is
static void Main()
{
string DefaultCatalog = "DefaultCatalog";
string DefaultGlossary = "DefaultGlossary";
string fullUri = string.Format("https://api.azuredatacatalog.com/catalogs/{0}/glossaries/{1}/terms?api-version=2016-03-30",
DefaultCatalog, DefaultGlossary);
HttpWebRequest request = WebRequest.Create(fullUri) as HttpWebRequest;
request.KeepAlive = true;
request.Method = "GET";
request.Accept = "application/json;adc.metadata=full";
request.Headers.Add("Authorization", AccessToken().Result.CreateAuthorizationHeader());
request.AllowAutoRedirect = false;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
if (response != null && response.StatusCode == HttpStatusCode.Redirect)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
var itemPayload = reader.ReadToEnd();
JToken terms;
JObject.Parse(itemPayload).TryGetValue("value", out terms);
if (terms != null)
{
var r = JsonConvert.DeserializeObject<JArray>(terms.ToString());
}
}
}
}
static async Task<AuthenticationResult> AccessToken()
{
string clientId = "MyClientId";
string client_secret = "MyClientSecret";
string tenentId = "MytenentId";
if (_authResult == null)
{
// Resource Uri for Data Catalog API
string resourceUri = "https://api.azuredatacatalog.com/";
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
string authorityUri = "https://login.windows.net/MytenentId/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
_authResult = await authContext.AcquireTokenAsync(resourceUri, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Always));
//ClientCredential cc = new ClientCredential(clientId, client_secret);
//_authResult = await authContext.AcquireTokenAsync(resourceUri, cc);
}
return _authResult;
}
I want to get the list of glossary from my azure datacatalog. But it always returning Unauthorized error.
"The remote server returned an error: (403) Forbidden."
my error is
You'll need to use to get the correct token:
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
Hope this helps,
Monica
make sure you are in the list of glossary admins.
Also ADC has great code samples in github which does what you want to do, check it out:
https://github.com/Azure-Samples/data-catalog-bulk-import-glossary
The Data Catalog contains only delegate permission.
But, I tried using Application permission. So, It throws Unauthorized after I changed it into user login based (Delegated Permission).
Now it's fixed.
Thanks for sharing the answer #Monica and #Max
I have read several posts about login in to sites that needs email and password, but i couldn't find a solution about logging in to a specific site called geni.com. Is there a way?
CookieContainer cookie;
string user = "somemail#somehost.com";
string pass = "123456";
string formUrl = "http://www.geni.com/login/";
string formParams = String.Format("profile_username={0}&password={1}", "MYUSERNAME", "MYPASS");
string cookieHeader;
HttpWebRequest myWebRequest;
WebResponse myWebResponse;
String URL = textBox1.Text;
myWebRequest = (HttpWebRequest)WebRequest.Create("formUrl");
myWebRequest.ContentType = "application/x-www-form-urlencoded";
myWebRequest.Method = "POST";
string login = string.Format("go=&Fuser={0}&Fpass={1}", user, pass);
byte[] postbuf = Encoding.ASCII.GetBytes(login);
myWebResponse = myWebRequest.GetResponse(); //Returns a response from an Internet resource
cookieHeader = myWebResponse.Headers["Set-cookie"];
cookie = myWebRequest.CookieContainer = new CookieContainer();
myWebRequest.CookieContainer = cookie;
Stream streamResponse = myWebResponse.GetResponseStream();
StreamReader sreader = new StreamReader(streamResponse);
Rstring = sreader.ReadToEnd();
I am a bit confused, can anybody help me??
Here's a link to their API documentation. To login you'll want to call their API like this:
https://www.geni.com/platform/oauth/request_token?client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET&grant_type=client_credentials