YouTube API - Video Upload - 401 Unauthorized - c#

This is driving me crazy! I am using the following code below. 90% of the time I get the following error. Every now and then it works with no problem. Please help. I checked the Developer Key, Username and Password.
Error: System.Net.WebException: The remote server returned an error:
(401) Unauthorized. at System.Net.HttpWebRequest.GetResponse()
at
Google.GData.Client.ResumableUpload.ResumableUploader.UploadStreamPart(Int32
partIndex, String httpMethod, Uri sessionUri, Authenticator
authentication, Stream payload, String mediaType, AsyncData data)
at
Google.GData.Client.ResumableUpload.ResumableUploader.UploadStream(String
httpMethod, Uri sessionUri, Authenticator authentication, Stream
payload, String mediaType, AsyncData data) at
Google.GData.Client.ResumableUpload.ResumableUploader.Insert(Authenticator
authentication, AbstractEntry payload, AsyncData data) at
Google.GData.Client.ResumableUpload.ResumableUploader.AsyncInsertWorker(AsyncResumableUploadData
data, AsyncOperation asyncOp, SendOrPostCallback
completionMethodDelegate) Value cannot be null. Parameter name:
inputStream can not be null
**My Code:**
newVideo.Tags.Add(new MediaCategory("Nonprofit", YouTubeNameTable.CategorySchema));
newVideo.Description = DateTime.Now.ToShortDateString();
newVideo.YouTubeEntry.Private = false;
ResumableUploader m_ResumableUploader = null;
Authenticator YouTubeAuthenticator;
m_ResumableUploader = new ResumableUploader(100); //chunksize 1 MB
m_ResumableUploader.AsyncOperationCompleted += new AsyncOperationCompletedEventHandler(m_ResumableUploader_AsyncOperationCompleted);
m_ResumableUploader.AsyncOperationProgress += new AsyncOperationProgressEventHandler(m_ResumableUploader_AsyncOperationProgress);
YouTubeAuthenticator = new ClientLoginAuthenticator(ConfigurationManager.AppSettings["APPLICATION_NAME"].ToString(), ServiceNames.YouTube, ConfigurationManager.AppSettings["LOGIN_USERNAME"].ToString(), ConfigurationManager.AppSettings["LOGIN_PASSWORD"].ToString());
YouTubeAuthenticator.DeveloperKey = ConfigurationManager.AppSettings["DEVELOPER_KEY"].ToString();
string contentType = MediaFileSource.GetContentTypeForFileName(fileName);
newVideo.MediaSource = new MediaFileSource(filePath, contentType);
AtomLink link = new AtomLink("http://uploads.gdata.youtube.com/resumable/feeds/api/users/" + ConfigurationManager.AppSettings["YT_USERNAME"].ToString() + "/uploads");
link.Rel = ResumableUploader.CreateMediaRelation;
newVideo.YouTubeEntry.Links.Add(link);
m_ResumableUploader.InsertAsync(YouTubeAuthenticator, newVideo.YouTubeEntry, new object());
I think I should add.. It breaks on the last line ytr.ParseVideo(e.ResponseStream);
void m_ResumableUploader_AsyncOperationCompleted(object sender, AsyncOperationCompletedEventArgs e)
{
YouTubeRequestSettings ys = new YouTubeRequestSettings(ConfigurationManager.AppSettings["APPLICATION_NAME"].ToString(),
ConfigurationManager.AppSettings["DEVELOPER_KEY"].ToString(), ConfigurationManager.AppSettings["LOGIN_USERNAME"].ToString(),
ConfigurationManager.AppSettings["LOGIN_PASSWORD"].ToString());
YouTubeRequest ytr = new YouTubeRequest(ys);
Video v = ytr.ParseVideo(e.ResponseStream);
}

My guess would be that you're not uploading into the account that corresponds to the stored credentials that you have. In general, you shouldn't do things like ConfigurationManager.AppSettings["YT_USERNAME"].ToString() in your GData API v2 code; instead, you should use the value default as the user portion of a URL, which refers to the currently authenticated user.

Related

The remote server returned an error: (410) Gone in C#

I have a web API2 application which is consumed by a third party application. When the application hits my end-point, my application send oAuth credentials for authentication and gets the results from the third party application.
Recently some of the transactions are failing and when i added some logs, i saw that the error: The remote server returned an error: (410) Gone is occurring for all failed transactions. Unfortunately I am unable to reproduce this issue when I am calling my application. The following is the code that I am using. What could be the issue that is causing this error?
public async Task<customerModel> SendSigned(string url)
{
customerModel customermodel = null;
try
{
OAuthBase oauthBase = new OAuthBase();
string oAuthKey = ConfigurationManager.AppSettings["oAuthKey"];
string oAuthSecret = ConfigurationManager.AppSettings["oAuthSecret"];
string timestamp = oauthBase.GenerateTimeStamp();
string nonce = oauthBase.GenerateNonce();
string normalizedUrl;
string normalizedRequestParameters;
string sig = HttpUtility.UrlEncode(oauthBase.GenerateSignature(
new Uri(url), oAuthKey, oAuthSecret, string.Empty, string.Empty,
"GET", timestamp, nonce, out normalizedUrl, out normalizedRequestParameters));
string requestUrl = String.Format("{0}?{1}&oauth_signature={2}", normalizedUrl, normalizedRequestParameters, sig);
HttpWebRequest request = null;
request = (HttpWebRequest)HttpWebRequest.Create(requestUrl);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
myXMLDocument = new XmlDocument();
customermodel = GetCustomerInformation(response);
}
return await Task.Run(() => customermodel);
}
catch (Exception ex)
{
_logger.Error("Error in SendSigned method", ex.InnerException);
return customermodel;
}
}
The explanation of 410 is The target resource is no longer available at the origin server and that this condition is likely to be permanent based on
this link (similar to a 404)
I would suggest you to think about recent changes you made to your
API signatures
Folder restructure/reorganization of assets/resources
Routing changes
Rename of resources

DocuSign: Error calling CreateEnvelope

I have the following code for adding a new Envelope with a local file:
EnvelopeDefinition envelope = new EnvelopeDefinition
{
Status = "sent"
};
// Get the contract file
Byte[] bytes = File.ReadAllBytes("[Local Filename]");
// Add a document to the envelope
DocuSign.eSign.Model.Document doc = new DocuSign.eSign.Model.Document();
doc.DocumentBase64 = System.Convert.ToBase64String(bytes);
doc.Name = contract.FileName;
doc.DocumentId = "1";
envelope.Documents = new List<DocuSign.eSign.Model.Document>();
envelope.Documents.Add(doc);
// Add a recipient to sign the documeent
Signer signer = new Signer();
signer.Email = recipientEmail;
signer.Name = recipientName;
signer.RecipientId = "1";
// Create a |SignHere| tab somewhere on the document for the recipient to sign
signer.Tabs = new Tabs();
signer.Tabs.SignHereTabs = new List<SignHere>();
SignHere signHere = new SignHere();
signHere.DocumentId = "1";
signHere.PageNumber = "1";
signHere.RecipientId = "1";
signHere.XPosition = "100";
signHere.YPosition = "150";
signer.Tabs.SignHereTabs.Add(signHere);
envelope.Recipients = new Recipients();
envelope.Recipients.Signers = new List<Signer>();
envelope.Recipients.Signers.Add(signer);
DocuSignAuthentication Creds = new DocuSignAuthentication
{
Username = "[My Username]",
Password = "[My Password]",
IntegratorKey = "[My Integration Key]"
};
ApiClient apiClient = new ApiClient("https://demo.docusign.net/restapi/v2/accounts/XXXXXXX");
string authHeader = JsonConvert.SerializeObject(Creds);
DocuSign.eSign.Client.Configuration cfg = new DocuSign.eSign.Client.Configuration(apiClient);
cfg.AddDefaultHeader("X-DocuSign-Authentication", authHeader);
EnvelopesApi envelopeApi = new EnvelopesApi(cfg);
EnvelopeSummary response = envelopeApi.CreateEnvelope("XXXXXXX", envelope);
The server returns a very little information in the error:
[ApiException: Error calling CreateEnvelope: ]
DocuSign.eSign.Api.EnvelopesApi.CreateEnvelopeWithHttpInfo(String accountId, EnvelopeDefinition envelopeDefinition, CreateEnvelopeOptions options) in
Y:\dev\SDKs\csharp\sdk\src\main\csharp\DocuSign\eSign\Api\EnvelopesApi.cs:2606
DocuSign.eSign.Api.EnvelopesApi.CreateEnvelope(String accountId, EnvelopeDefinition envelopeDefinition, CreateEnvelopeOptions options) in Y:\dev\SDKs\csharp\sdk\src\main\csharp\DocuSign\eSign\Api\EnvelopesApi.cs:2532
[Rest of the stack trace relates to our code]
If we try to use fiddler to capture any error messages from the server, the error becomes:
Could not establish trust relationship for the SSL/TLS secure channel
Is there anyway to get a more information about what's wrong with our request? Or is there an un-encrypted developer endpoint available to work through these kinds of issues? Any help you can spare would be greatly appreciated.
The error "Could not establish trust relationship for the SSL/TLS secure channel" means that the HTTPRequest call from the client (which can be your server, but is a client in the sense that it made the HTTPRequest) to the server (DocuSign in this case) could not use TLS over SSL (https:// address) to ensure that there's a certificate that allows to encrypt the information in the HTTPRequest and HttpResponse. This can happen for a variety of reasons including not having the current version of TLS on the client as well as having the wrong certificate or other issues with the network. I would recommend trying to make this call from a different server, potentially outside your corporate network to isolate the problem. Also, check the version of TLS to ensure it's at least 1.1

Federated authentication in Sharepoint 2013: getting rtFa and FedAuth cookies

The scenario is the following: I need to perform a federated authentication of a user (which uses his university account) into the Sharepoint site of his university and to obtain both the FedAuth and rtFa cookies (which I have to pass to SharePoint REST webservices in order to access resources).
I made some attempts but there is at least an issue in each one:
1) Using Microsoft.SharePoint.Client library
ClientContext context = new ClientContext(host);
SharePointOnlineCredentials creds = new SharePointOnlineCredentials(user, passw);
context.Credentials = creds;
Uri sharepointuri = new Uri(host);
string authCookie = creds.GetAuthenticationCookie(sharepointuri);
Web web = context.Web;
context.Load(web, w=>w.Lists);
context.ExecuteQuery();
fedAuthString = authCookie.Replace("SPOIDCRL=", string.Empty);
This way I manage to get the FedAuth cookie but I am unable to get the rtFa cookie.
How can I get the rtFa cookie at this point?
Can I intercept the HTTP request involved in such an operation (i.e., context.ExecuteQuery()) -- which presumably contains the rtFa cookie in the headers?
Or, can I get the rtFa cookie by only leveraging on the FedAuth cookie?
2) Using MsOnlineClaimsHelper
This is a helper class which can be found on the Internet (e.g., here http://blog.kloud.com.au/tag/msonlineclaimshelper/ ).
This class, as it is, works with normal authentication but fails with federated authentication.
So I adjusted it in order to make it work in this case.
As long as I understand, the steps are the following:
Authenticate using username and password to the STS ADFS service of the university (the "federated party" or the ISSUER) -- here the Relying Party is Sharepoint O365 STS ("https://login.microsoftonline.com/extSTS.srf")
If the auth succeeds, I get back a SAML assertion containing the claims and a security token
Now, I authenticate to the SharePoint site by passing the Security Token
If the token is recognized, I get back a response which contains the two cookies (FedAuth and rtFa)
I am not an expert in this matter, and I came out with the following code:
This is the code that calls the method above and try to get FedAuth and rtFa from credentials in two steps (step 1: get SAML token from Federated Party; step 2: pass token from Federated Party to Sharepoint):
private List<string> GetCookies(){
// 1: GET SAML XML FROM FEDERATED PARTY THE USER BELONGS TO
string samlToken = getResponse_Federation(sts: "https://sts.FEDERATEDDOMAIN.com/adfs/services/trust/13/usernamemixed/",
realm: "https://login.microsoftonline.com/extSTS.srf");
// 2: PARSE THE SAML ASSERTION INTO A TOKEN
var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers;
SecurityToken token = handlers.ReadToken(new XmlTextReader(new StringReader(samlToken )));
// 3: REQUEST A NEW TOKEN BASED ON THE ISSUED TOKEN
GenericXmlSecurityToken secToken = GetO365BinaryTokenFromToken(token);
// 4: NOW, EASY: I PARSE THE TOKEN AND EXTRACT FEDAUTH and RTFA
...............
}
private string getResponse_Federation(string stsUrl, string relyingPartyAddress)
{
var binding = new Microsoft.IdentityModel.Protocols.WSTrust.Bindings.UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential);
binding.ClientCredentialType = HttpClientCredentialType.None;
var factory = new WSTrustChannelFactory(binding, stsUrl);
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";
factory.Credentials.SupportInteractive = false;
factory.TrustVersion = TrustVersion.WSTrust13;
IWSTrustChannelContract channel = null;
try
{
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress(relyingPartyAddress), //("urn:sharepoint:MYFEDERATEDPARTY"),
ReplyTo = relyingPartyAddress,
KeyType = WSTrust13Constants.KeyTypes.Bearer,
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
RequestDisplayToken = true,
};
channel = (WSTrustChannel)factory.CreateChannel();
RequestSecurityTokenResponse response = null;
SecurityToken st = channel.Issue(rst, out response);
var genericToken = st as GenericXmlSecurityToken;
return genericToken.TokenXml.OuterXml;
}
catch (Exception e)
{
return null;
}
}
private GenericXmlSecurityToken GetO365BinaryTokenFromToken(SecurityToken issuedToken)
{
Uri u = new Uri("https://login.microsoftonline.com/extSTS.srf");
WSHttpBinding binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.IssuedToken;
Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory channel =
new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
binding, new EndpointAddress("https://login.microsoftonline.com/extSTS.srf"));
channel.TrustVersion = TrustVersion.WSTrust13;
channel.Credentials.SupportInteractive = false;
GenericXmlSecurityToken token = null;
try
{
RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Bearer)
{
};
rst.AppliesTo = new EndpointAddress("urn:sharepoint:MYFEDERATEDPARTY");
channel.ConfigureChannelFactory();
var chan = (Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel)channel.CreateChannelWithIssuedToken(issuedToken);
RequestSecurityTokenResponse rstr = null;
token = chan.Issue(rst, out rstr) as GenericXmlSecurityToken;
return token;
}
catch (Exception ex){
Trace.TraceWarning("WebException in getO365BinaryTokenFromADFS: " + ex.ToString());
throw;
}
}
I managed to get back a SAML token from the university STS. However, when parsed, the resulting SecurityToken has no security keys (i.e., the SecurityKeys collection is empty)
With no keys, I get on GetO365BinaryTokenFromToken() but when I try to send the token to the SharePoint Authentication service -- I get the following error:
"The signing token Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken has no keys. The security token is used in a context that requires it to perform cryptographic operations, but the token contains no cryptographic keys. Either the token type does not support cryptographic operations, or the particular token instance does not contain cryptographic keys. Check your configuration to ensure that cryptographically disabled token types (for example, UserNameSecurityToken) are not specified in a context that requires cryptographic operations (for example, an endorsing supporting token)."
I think that there are also some configuration issues that I cannot control directly, on both sides (the university STS ADFS and the Sharepoint STS).
I hope that more expert people would bring clarity in this process and even provide advice to actually make this scenario work.
File download function
With the following function, I am able to download a file (given an URL such as https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf) by issuing BOTH the FedAuth and the rtFa cookie. If I do not pass the rtFa cookie, I get an "Unauthorized" response.
public static async Task<byte[]> TryRawWsCall(String url, string fedauth, string rtfa, CancellationToken ct, TimeSpan? timeout = null) {
try {
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = new System.Net.CookieContainer();
CookieCollection cc = new CookieCollection();
cc.Add(new Cookie("FedAuth", fedauth));
cc.Add(new Cookie("rtFa", rtfa));
handler.CookieContainer.Add(new Uri(url), cc);
HttpClient _client = new HttpClient(handler);
if (timeout.HasValue)
_client.Timeout = timeout.Value;
ct.ThrowIfCancellationRequested();
var resp = await _client.GetAsync(url);
var result = await resp.Content.ReadAsByteArrayAsync();
if (!resp.IsSuccessStatusCode)
return null;
return result;
}
catch (Exception) { return null; }
}
In fact, only FedAuth cookie is mandatory when it comes to SharePoint Online/Office 365 authentication.
According to Remote Authentication in SharePoint Online Using Claims-Based Authentication:
The FedAuth cookies enable federated authorization, and the rtFA
cookie enables signing out the user from all SharePoint sites, even if
the sign-out process starts from a non-SharePoint site.
So, it is enough to provide SPOIDCRL HTTP header in order to perform authentication in SharePoint Online/Office 365, for example:
var request = (HttpWebRequest)WebRequest.Create(endpointUri);
var credentials = new SharePointOnlineCredentials(userName,securePassword);
var authCookie = credentials.GetAuthenticationCookie(webUri);
request.Headers.Add(HttpRequestHeader.Cookie, authCookie);
The following examples demonstrates how to perform active authentication in SharePointOnline/Office 365 by providing FedAuth cookie.
Example 1: Retrieve FormDigest via SharePoint 2013 REST API (uisng MsOnlineClaimsHelper class)
public static string GetFormDigest(Uri webUri, string userName, string password)
{
var claimsHelper = new MsOnlineClaimsHelper(webUri, userName, password);
var endpointUri = new Uri(webUri,"/_api/contextinfo");
var request = (HttpWebRequest)WebRequest.Create(endpointUri);
request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
request.Method = WebRequestMethods.Http.Post;
request.Accept = "application/json;odata=verbose";
request.ContentType = "application/json;odata=verbose";
request.ContentLength = 0;
var fedAuthCookie = claimsHelper.CookieContainer.GetCookieHeader(webUri); //FedAuth are getting here
request.Headers.Add(HttpRequestHeader.Cookie, fedAuthCookie); //only FedAuth cookie are provided here
//request.CookieContainer = claimsHelper.CookieContainer;
using (var response = (HttpWebResponse) request.GetResponse())
{
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var content = streamReader.ReadToEnd();
var t = JToken.Parse(content);
return t["d"]["GetContextWebInformation"]["FormDigestValue"].ToString();
}
}
}
Example 2: Retrieve FormDigest via SharePoint 2013 REST API (using SharePointOnlineCredentials class)
public static string GetFormDigest(Uri webUri, string userName, string password)
{
var endpointUri = new Uri(webUri, "/_api/contextinfo");
var request = (HttpWebRequest)WebRequest.Create(endpointUri);
request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
request.Method = WebRequestMethods.Http.Post;
request.Accept = "application/json;odata=verbose";
request.ContentType = "application/json;odata=verbose";
request.ContentLength = 0;
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
request.Credentials = new SharePointOnlineCredentials(userName,securePassword);
using (var response = (HttpWebResponse)request.GetResponse())
{
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var content = streamReader.ReadToEnd();
var t = JToken.Parse(content);
return t["d"]["GetContextWebInformation"]["FormDigestValue"].ToString();
}
}
}
Update
The modified version of the example for downloading a file:
public static async Task<byte[]> DownloadFile(Uri webUri,string userName,string password, string relativeFileUrl, CancellationToken ct, TimeSpan? timeout = null)
{
try
{
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
var credentials = new SharePointOnlineCredentials(userName, securePassword);
var authCookie = credentials.GetAuthenticationCookie(webUri);
var fedAuthString = authCookie.TrimStart("SPOIDCRL=".ToCharArray());
var cookieContainer = new CookieContainer();
cookieContainer.Add(webUri, new Cookie("SPOIDCRL", fedAuthString));
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookieContainer;
HttpClient _client = new HttpClient(handler);
if (timeout.HasValue)
_client.Timeout = timeout.Value;
ct.ThrowIfCancellationRequested();
var fileUrl = new Uri(webUri, relativeFileUrl);
var resp = await _client.GetAsync(fileUrl);
var result = await resp.Content.ReadAsByteArrayAsync();
if (!resp.IsSuccessStatusCode)
return null;
return result;
}
catch (Exception) { return null; }
}
I created a github project based on https://stackoverflow.com/users/1375553/vadim-gremyachev 's answer https://github.com/nddipiazza/SharepointOnlineCookieFetcher
with a project that can generate these cookies.
It has releases for Windows, Centos7 and Ubuntu16 and I used mono develop to build it so that it is platform independent.
Intended for users who are not making programs with CSOM in c# but still want to be able to easily get the cookies.
Usage
One Time Only Step: (see Access to the path "/etc/mono/registry" is denied)
sudo mkdir /etc/mono
sudo mkdir /etc/mono/registry
sudo chmod uog+rw /etc/mono/registry
Run program:
Linux: ./SharepointOnlineSecurityUtil -u youruser#yourdomain.com -w https://tenant.sharepoint.com
Windows: SharepointOnlineSecurityUtil.exe -u youruser#yourdomain.com -w https://tenant.sharepoint.com
Enter a password when promped
Result of stdout will have SPOIDCRL cookie.
I still needed both FedAuth and rtFa cookies for my purposes. I tried using just FedAuth, but it wouldn't work without both. Another developer confirmed he saw the same behavior.
NOTE: Legacy authentication must be enabled in your tenant for this to work.
Here is a thread to help obtain both FedAuth and rtFa.
Send Post request to https://login.microsoftonline.com/extSTS.srf with the following body.
Replace UserName, Password, EndPoint Address with relevant values.
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken>
<o:Username>[username]</o:Username>
<o:Password>[password]</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<a:EndpointReference>
<a:Address>[endpoint]</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
Note the content of the wsse:BinarySecurityToken node within the response data.
Send Post request to https://YourDomain.sharepoint.com/_forms/default.aspx?wa=wsignin1.0.
Replace 'YourDomain with relevant value. Provide the wsse:BinarySecurityToken content within the body of the request.
The response header will contain the FedAuth and rtFa cookies.
This is what I did. Might be useful to future readers!
For my usecase, I am running a WinForms Windows client side app. I was able to grab the FedAuth and rtFA cookie using a embedded WebBrowser Control.
I have uploaded my sample test project to github here: https://github.com/OceanAirdrop/SharePointOnlineGetFedAuthAndRtfaCookie
Here is what I did:
Step 01: Naviagte to SharePoint Url in WebBrowser Control
Using the WebBrowser control, first navigate to a web page on your sharepoint site that you have access to. It can be any page. The aim here is to get the cookies from the loaded page. This step only needs to be done once in the app.
webBrowser1.Navigate(#"https://xx.sharepoint.com/sites/xx/Forms/AllItems.aspx");
Step 02: Grab Cookies from WebBrowser Control
Next, override the Navigated event in the WebBrowser control. This lets you know the page has fully loaded.
Now, heres the wrinkle!! The FedAuth cookies are written with an HTTPOnly flag, which means they cannot be accessed from the .NET Framework. This means if you try to access the cookies of the WebBrowser control, you will get null string back!
// This line of code wont work and will return null
var cookies = webBrowser1.Document.Cookie;
So, to get around this, you instead need to call InternetGetCookieEx in the WININET.dll. I took the code from here. This is what the Navigated function handler looks like:
private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
try
{
if (webBrowser1.Url.AbsoluteUri == "about:blank")
return;
// This line calls through to InternetGetCookieEx
var cookieData = GetWebBrowserCookie.GetCookieInternal(webBrowser1.Url, false);
if (string.IsNullOrEmpty(cookieData) == false)
{
textBoxCookie.Text = cookieData;
var dict = ParseCookieData(cookieData);
textBoxFedAuth.Text = dict["FedAuth"];
textBoxrtFa.Text = dict["rtFa"];
}
}
catch (Exception)
{
}
}
Step 03: Progrmatically Make WebRequest Using Cookies
Now that we have the FedAuth and rtFA cookies we can continue on and use the HttpClient to call any andpoint we need. In my case calling many endpoints that contain images. The code looks like this:
private void buttonDownloadImage_Click(object sender, EventArgs e)
{
try
{
var url = $"https://xx.sharepoint.com/sites/xx/xx/Images/{textBoxImageName.Text}";
var handler = new HttpClientHandler();
handler.CookieContainer = new System.Net.CookieContainer();
// Add our cookies to collection
var cc = new CookieCollection();
cc.Add(new Cookie("FedAuth", textBoxFedAuth.Text));
cc.Add(new Cookie("rtFa", textBoxrtFa.Text));
handler.CookieContainer.Add(new Uri(url), cc);
var httpClient = new HttpClient(handler);
var resp = httpClient.GetAsync(url).Result;
var byteData = resp.Content.ReadAsByteArrayAsync().Result;
if (resp.IsSuccessStatusCode)
{
pictureBox1.Image = byteArrayToImage(byteData);
}
}
catch (Exception)
{
}
}
Thats it. And it works like a charm.

How to post Twitter message to authenticated user's timeline via OAuth

I've been wrestling with the Twitter API for a few days now but I cannot post a message to an authenticated user's timeline. I've got an ASP.NET MVC 4 application that signs a user in via Twitter and saves the access token that comes back from the sign in process. That part works fine. I can see my application with read and write permissions within the authenticated user's twitter account.
I'm then using that access token, along with the consumer key, consumer secret and oauth token secret associated with my Twitter application, to post to the user's timeline. I'm getting a 401 unauthorised error every time. I've tried using the 1.1 API and the 1 API with the same result.
Most of the code comes from Gary Short's article here: http://garyshortblog.wordpress.com/2011/02/11/a-twitter-oauth-example-in-c/
Here's what I've got so far. If anyone can spot any clues as to what I'm missing I'd be most grateful.
public async Task<bool> Push(TwitterMessage twitterMessage)
{
const string updateApi = "http://api.twitter.com/1/statuses/update.json";
const string oauthConsumerKey = "<consumerKey>";
const string consumerSecret = "<consumerSecret>";
const string oauthSignatureMethod = "HMAC-SHA1";
const string oauthTokenSecret = "<tokenSecret>";
var signingKey = string.Format("{0}&{1}", consumerSecret.Escaped(), oauthTokenSecret.Escaped());
var postBody = "status=" + Uri.EscapeDataString(twitterMessage.MessageContent);
var oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
var oauthToken = "<authenticatedUserToken>";
var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
var oauthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();
var message = string.Format("POST {0}?{1} HTTP/1.1", updateApi, postBody.Escaped());
var hasher = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey));
var signatureString = Convert.ToBase64String(hasher.ComputeHash(new ASCIIEncoding().GetBytes(message)));
ServicePointManager.Expect100Continue = false;
var request = (HttpWebRequest)WebRequest.Create(updateApi);
request.KeepAlive = false;
var authorisationBuilder = new StringBuilder();
authorisationBuilder.Append("OAuth ");
authorisationBuilder.AppendFormat("oauth_consumer_key=\"{0}\",", oauthConsumerKey.Escaped());
authorisationBuilder.AppendFormat("oauth_signature_method=\"{0}\",", oauthSignatureMethod.Escaped());
authorisationBuilder.AppendFormat("oauth_timestamp=\"{0}\",", oauthTimestamp.Escaped());
authorisationBuilder.AppendFormat("oauth_nonce=\"{0}\",", oauthNonce.Escaped());
authorisationBuilder.AppendFormat("oauth_token=\"{0}\",", oauthToken.Escaped());
authorisationBuilder.AppendFormat("oauth_signature=\"{0}\"", signatureString.Escaped());
var authorisation = authorisationBuilder.ToString();
request.Headers.Add("Authorization", authorisation);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (var stream = await request.GetRequestStreamAsync())
{
var bodyBytes = new ASCIIEncoding().GetBytes(postBody);
stream.Write(bodyBytes, 0, bodyBytes.Length);
}
//Allow us a reasonable timeout in case Twitter's busy
request.Timeout = 3 * 60 * 1000;
try
{
var response = await request.GetResponseAsync() as HttpWebResponse;
return true;
}
catch (WebException)
{
return false;
}
}
public static string Escaped(this string input)
{
return Uri.EscapeDataString(input);
}
UPDATE Looking at this SO post it looks like I can't use the DotNetOpenAuth twitter client for authorisation, which I had been doing. The suggestion there is to extend the twitter consumer class instead to perform the authorisation, which will allow me to retrieve the user's token secret (the missing piece of my puzzle I think). Will post another update when I get this working.
Check this code and link/article simple and easy :
protected void btnTweet_Click(object sender, EventArgs e)
{
string oauthAccessToken = Session["twtoken"].ToString();
string oauthAccessTokenSecret = Session["twsecret"].ToString();
OAuthHelper oauthhelper = new OAuthHelper();
oauthhelper.TweetOnBehalfOf(oauthAccessToken, oauthAccessTokenSecret, txtTweet.Text);
if (string.IsNullOrEmpty(oauthhelper.oauth_error))
Response.Write("Twit Posted Successfully");
else
Response.Write(oauthhelper.oauth_error);
}
Read more how to get access token and secret key and download OAuthHelper and OAuthUtility Class below is the link -
How to post tweet on behalf of an user from asp.net using oauth authentication
Login with twitter using oauth authentication in asp.net and get access token, screen name and userid
So the problem is an issue with DotNetOpenAuth as it currently stands. For Twitter authentication, the DotNetOpenAuth client doesn't allow for the full authorisation flow (needed for posting to a user's timeline). Only the access token is retrieved from the initial handshake and not the access token secret. I was using the access token secret associated with my Twitter app, rather than the Twitter user who was signing in, so authorisation was failing every time.
UPDATE: I've finally gone with using Daniel Crenna's Tweetsharp library, which makes the code a little simpler than writing my own API wrapper would have been:
public async Task<bool> Push(TwitterAccount account)
{
var twitterService = new TwitterService(consumerKey, consumerSecret);
twitterService.AuthenticateWith(account.AccessToken, account.AccessTokenSecret);
var options = new SendTweetOptions {Status = string.Format("{0} {1}", account.Message.MessageContent, account.Message.ShortLink)};
var status = twitterService.SendTweet(options);
return status != null;
}

WebAuthenticationBroker authenticate with LiveID returns Success status but no Security Token

I am using ACS to authenticate in a Windows 8 application. I am observing exactly what I expect in that the UI displays the authentication dialog and on successfully entering my LiveID credentials I am returned to my code with a Success status but I do not receive a security token, I simply get "https://XXXXX.accesscontrol.windows.net/v2/wsfederation?wa=wsignin1.0" in result.ResponseData
The code is as follows:
string loginUriString = "https://XXXXX.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%2f%YYYYY.cloudapp.net";
string redirectUriSting = "https://XXXXX.accesscontrol.windows.net:443/v2/wsfederation";
string authToken;
bool IsAuthenticated = false;
private async Task AuthenticateAsync()
{
var requestUri = new Uri(loginUriString, UriKind.RelativeOrAbsolute);
var redirectUri = new Uri(redirectUriSting, UriKind.RelativeOrAbsolute);
//var testUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
var result = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
requestUri,
redirectUri);
if (result.ResponseStatus != WebAuthenticationStatus.Success)
throw new Exception(string.Format("Login failed : {0}", result.ResponseErrorDetail));
//authToken = ExtractTokenFromResponse(result.ResponseData);
//if (!string.IsNullOrEmpty(authToken))
//{
_client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("OAuth", result.ResponseData);
IsAuthenticated = true;
//}
}
I have seen one other SO question here with what seems like a similar problem but nothing else. Have I got something wrong?
The WebAuthenticationBroker simply keeps browsing until the next requested page is the one specified by the callbackUri parameter. At that point it returns the final URL to you so if you want to get anything back it needs to be encoded in that URL.
In the ACS control panel for the relying party you need to specify a return url that is somewhere on your site. For example https://YYYYY.cloudapp.net/federationcallback. Then create a controller to handle accept a post to that URL. The post will have a form field wresult which is some xml that will contain the token returned from ACS.
You can then send the token back to the WebAuthenticationBroker by redirecting to https://YYYYY.cloudapp.net/federationcallback/end?token={whatever you want to return}
You would then need to change the usage of the authentication broker to the following:
var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
new Uri("https://XXXXX.accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=http%3a%2f%2fYYYYY.cloudapp.net%2f"),
new Uri("https://YYYYY.cloudapp.net/federationcallback/end")
);
// The data you returned
var token = authenticateResult.ResponseData.Substring(authenticateResult.ResponseData.IndexOf("token=", StringComparison.Ordinal) + 6);

Categories