Impersonation, Active Directory, and "user does not have authority to xxxx" issues - c#

I have 2 ASP.NET MVC 3 applications. I am using impersonation via the web.config to allow me to query Active Directory to get details on the user. The application uses Windows authentication and does not allow anonymous users. One application is the primary application where the user performs their tasks. The other allows the user to set up other user's to look like them in application one.
The test user's are getting the following error:
SQL1092N "<DOMAIN ID>" does not have the authority to perform the requested command.
This happens after I send a web request from my primary application to the secondary one. To get that working I had to make the request impersonate the actual user and not the identity the application uses for impersonation. This is actually an SO question I posted and had answered. That's here: How do I call an MVC Action via a WebRequest and validate the request through Active Directory?
At the end of that code, I call:
impersonationContext.Undo();
It is after this web request takes place, that the primary application tries accessing the database and now it seems that the above call has undone the impersonation of the application, so the user's attempt to do anything that opens a database connection fails. At least, that's my working theory after a day of head bashing.
My question is, how can I get the impersonation of the application to revert back to the user in the web.config? Or, when making my web request, is there a way to ensure the impersonation context only applies to that request?
The whole point of all of this is that the second application has its own sql server database. The primary application uses DB2. I would like to write the database access code once, but use it in both applications. Currently that's what I've done, but my method of relying on the web request to get the data may not be the best approach.
I'm open to any thoughts, comments, suggestions, and/or criticism. How should I go about handling this?

Okay...my theory that the IPrincipal context was changed when making the web request proved accurate, which made this fix extremely easy. Best part is, I can keep using the api I built to make this request without duplicating the Sql Server Entity Framework parts.
I have the following call to my api library:
proxyRequestResultDetails = ProxyApiWrapper.GetProxies(
adUserInfo.AssociateId,
context.User);
This code is being called by an authorization filter attribute. The method prototype looks like
public void OnAuthorization(AuthorizationContext filterContext)
Internally, the call makes the GetProxies method following call:
public static StreamReader GetWebRequestStream(
string url,
string contentType,
bool useDefaultCredentials,
IPrincipal user)
{
var impersonationContext = ((WindowsIdentity)user.Identity).Impersonate();
var request = WebRequest.Create(url);
try
{
request.ContentType = contentType;
//request.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
//request.UseDefaultCredentials = useDefaultCredentials;
//IWebProxy p = new WebProxy();
//request.Proxy = p.
request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
request.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
var response = (HttpWebResponse)request.GetResponse();
return new StreamReader(response.GetResponseStream());
}
catch (Exception e)
{
impersonationContext.Undo();
throw e;
}
finally
{
impersonationContext.Undo();
}
}
When the calling method returns, the identity of user is no longer that of the one set for the application to
impersonate. The fix is pretty simple:
//Track current identity before proxy call
IPrincipal user = context.User;
proxyRequestResultDetails = ProxyApiWrapper.GetProxies(
adUserInfo.AssociateId,
context.User);
//Undo any impersonating done in the GetProxies call
context.User = user;
2 lines of code resolved 12 hours of head ache. It could have been worse. Anyhow. Thanks for being a sounding board. I tried
having this conversion with the duck, but the duck got confused.

Related

Unable to return user favorites via Tableau REST API

UPDATE: Sept 2019.
This API call now works as intended.
Issues on the Tableau end appear to have been resolved and the call now returns the correct data.
===============================================================
I'm using the Tableau REST API via C# to try and get a list of users favorites.
I know the user has some, because its me.
I have tried using API Version 2.8,3.0, 3.1 and 3.2 with little to no joy.
2.8 and 3.0 respond with:
<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.8.xsd"> //3.0.xsd when using API Version 3.0
<favorites/> //There should be a plethora of favorites of all varieties in here.
</tsResponse>
3.1 and 3.2 give me a (404) Not found.
The code i have in c# is:
public static string QueryFavourites(string APIVersion, string AuthToken, string SiteID, string UserID)
{
string result = "";
try
{
string url = $#"{Server}/api/{APIVersion}/sites/{SiteID}/favorites/{UserID}";
// Create the web request
WebRequest request = WebRequest.Create(url) as WebRequest;
request.PreAuthenticate = true;
request.Headers.Add($"x-tableau-auth: {AuthToken}");
// Get response
using (WebResponse response = request.GetResponse())
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
// Read the whole contents and return as a string
result = reader.ReadToEnd();
}
return result;
}
catch(Exception E)
{
logger.Error("Error! System Says: " + E.Message);
return result;
}
}
I know the method works, as it is used for multiple other API calls using a different URL for each (depending on the call).
Does anyone know if its an issue on the tableau end or on my end?
Apparently it should work with Tableau server 2.8 or above, which we have. (i think we're running 2018.1)
Is anyone able to get a list of favorites for a user using tableau REST API?
Where am i going wrong?
(I have also posted the question on Tableau Forum.)
UPDATE:
I have included the CURL and Headers of the request, as well as the results, in the screenshots below. (I use 'Restlet Client' more than 'Postman' so screenshots are from the former.) ID's and authentication tokens have been removed as they are sensitive information, and i don't think my company would be happy with me putting them on the public facing internet.
All ID's and auth keys are in the correct case and presented correctly. They are used in several other API calls with success and are pulled direct from Tableau via the API.
The exceptions, i have found out are the inability to find the version of the API that i am calling. so v2.6 - v2.8 and v3.0 all "work". Other versions return a 404001 VERSION_NOT_FOUND error.
The approach i would take is:
Query a user on the site. (the user that has the favorites)
Check if the user is actually: the same user you are authenticated as; and the same user you are gonna query for favorites
If they are the same, try adding a favorite with the REST API (DataSource, View or Workbook)
Get the favorites for the user, the datasource/view/workbook you added as a favorite should be in there.
If you want to Update the user, Add user to site or Add user to Group, I've added links to the documentation
You can do these things with Postman/tool of your choice.
What you can also try is ensuring the user that is querying another user (or the same) is a server admin (just to be safe), and making sure that you are a member of the same site of another (or the same) user.
Hope this helps!
EDIT: Maybe you can try adding a new user with group regular to a site, ensuring that you are a member of the site too. Afterwards adding a favorite and getting the favorites for the user of group regular. If that doesnt work u can verify whether its impossible to get favorites for users of group regular as well, besides admins.
Finally found out what was happening.
It doesn't work as intended.
It will only return user favorites for the user that is authenticated in the authentication token, regardless of what user id you put in the request.
Had a call with Tableau support and accidentally figured it out, when we switched authenticated user.
I will leave this here in case anyone else comes across the same issue.

Kerberos Token asks to be called again to complete the context

I am attempting to obtain a Kerberos Token from a C# Application (not web-based, a standalone executable).
I have referred to this question to figure out how to do it, but even trying both answers, I get the same problem.
When I reach the GetToken line (using Furkat's answer as a reference here), I get an exception:
KerberosRequestorSecurityToken securityToken = tokenProvider.GetToken(TimeSpan.FromMinutes(1)) as KerberosRequestorSecurityToken;
Here is the exception and the nested innerExceptions:
Exception: The NetworkCredentials provided were unable to create a Kerberos credential, see inner exception for details.
innerException: Authenticating to a service running under a user account which requires Kerberos multilegs, is not supported.
innerException: The function completed successfully, but must be called again to complete the context
I have some serious problems trying to find any examples of this working for a non-web based application, the StackOverflow question I linked is pretty much the closest I've got to getting what I need.
I also have problems figuring out exactly how things are supposed to work, since I can't get an example to work on my side. I'm looking for some sort of unique token for the user, that can then be passed to a SAML POST call to a server for Single Sign On. What will this token look like? Is it right to use TokenImpersonationLevel.Impersonation, instead of Identification here? (Identification gives me the same problem).
So my question is about my error and how to fix it, but I would really appreciate an explanation with the answer, telling me about the context (what was going wrong, what I misunderstood, etc).
Here's my complete Method. It's in Proof-Of-Concept stage right now, so forgive the temporary bad naming and ugly code. I'm making lots of trial-and-error.
public string Method5()
{
try
{
var userName1 = new WindowsPrincipal(WindowsIdentity.GetCurrent()).Identity.Name;
var domainName = userName1.Split('\\').First();
var userName = userName1.Split('\\').Last();
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
var domain = Domain.GetCurrentDomain().ToString();
using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
{
string spn = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName).UserPrincipalName;
KerberosSecurityTokenProvider tokenProvider = new KerberosSecurityTokenProvider(spn, TokenImpersonationLevel.Impersonation, CredentialCache.DefaultNetworkCredentials);
KerberosRequestorSecurityToken securityToken = tokenProvider.GetToken(TimeSpan.FromMinutes(1)) as KerberosRequestorSecurityToken;
string serviceToken = Convert.ToBase64String(securityToken.GetRequest());
return serviceToken;
}
}
catch (Exception ex)
{
return "Failure";
}
}
The error indicates that you are requesting a Kerberos User2User token. The multileg bit is correct, but somewhat misleading. The issue is that AD determines it's a U2U request and makes the API return a specific error, indicating it's U2U and requires a retry with different parameters. .NET doesn't understand this retry, hence the error.
The reason you're requesting a U2U token is because you're calling the token provider asking for it to request a token to access the given SPN, which in this case is just an ordinary user. This is generally not useful in client/server applications.
KerberosSecurityTokenProvider tokenProvider = new KerberosSecurityTokenProvider(spn, TokenImpersonationLevel.Impersonation, CredentialCache.DefaultNetworkCredentials);
What this code is doing is saying for a user that has been inferred by impersonation or authentication previously, request a token so that user can access a remote service {SPN}. A token is only useful for a single user to a single service. You can't just collect a token and user it everywhere. This is not how Kerberos-proper works. Kerberos determines the name of that service by the SPN. In this case it already knows who the caller is.
So, the correct solution is:
var identity = Thread.CurrentPrincipal.Identity; // domain\username1
var spn = "host/someservice.domain.com";
var tokenProvider = new KerberosSecurityTokenProvider(spn);
var securityToken = tokenProvider.GetToken(TimeSpan.FromMinutes(1)); // token for username1 to host/someservice.domain.com

Cross-domain authentication ASP.net MVC

I have two different web application built with ASP.net MVC. This two application may not be running in the same server nor in the same domain.
I would like that if a user login in one of them, automatically should be login in the other. The same should work with logout.
Which do you think is the best solution? Do you know about some example code?
Thanks!
--- EDITED WITH MORE INFO ---
Use case scenario:
The user has the web application A opened on a tab, and at some point of the app there is a link that redirects the user to the web application B. If he is logged in on A, I would like to show him the full page, and if he is not, redirect him to the login form.
Why I need to do it:
Applications A and B are already built. Apparently, the only way of accessing B is clicking on the link located in A, that only is shown if you have previously logged. The problem is that if you know the URL of some page of B (are long and complex, but still) you can write it on the browser and access B, which it means a security problem.
My Answer may not be the the best one, However you can use some tricky mechanism like
whenever you are going on another application you need to pass one token from application A to B.
Validate this token on B site.
and Authorized that user based on token. (i mean apply silent or backdoor login)
I assume you cannot communicate between applications A and B using any shared store. (This could allow some shared session implementation).
The more industry standard way (OpenID Connect) of doing that is like some of the other answers have hinted at. I will try and give more details to get you on the right track.
Both application A and B should relay the authentication process to a trusted 3rd party (which could be hosted in withe A, B or a different application altogether) - Let's call it C
When the user arrives at either A or B (no matter that B has weird complicated URLs, she can always bookmark those) his request should contain an authorization token. If it doesn't, she is not authenticated and would be redirected to C and presented with some login mechanism - say user/pass form.
After successful login, she is redirected back to A/B (depending on where she came from) to complete what ever she was doing with the authentication token. Now, having the authentication token present she is authenticated.
If she is authenticated with A and then redirected to B, this redirect should contain the token as well, B would know how to trust that token.
Now, If he just opens opens up a new tab, B would not see any token, and so she would be redirected to C, only to be redirected back (she is already authenticated, remember?) to B with the token, and now all is good.
What I described is a common flow using OpenID connect, and if using .net, I really suggest using IdentityServer from Thinktecture to do the hard work for you and be your "C".
Another option, is to pay for such "C" hosted as a SaaS application - check out Auth0
You can implement OAuth in A Project. You can get more help here: http://www.openauthentication.org/about
OWIN OAuth 2.0 Authorization Server
http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
I think what you are after is CAS (Central Authentication Service)
https://en.wikipedia.org/wiki/Central_Authentication_Service
there are numbers of CAS provider available. I would recommend you to check this out https://wiki.jasig.org/display/CAS/Home
it will give you number of out-of-the-box solutions exist to enable
web services written in a specific language, or based on a framework,
to use CAS. This will help you implement a SSO solution in
a matter of hours
Thanks to the answer of #Kaushik Thanki I have implemented some code that fix my problem. I will post here the solution that it works for me, even if it is not the optimus.
First of all, I have implemented in A a method to make a Post request to B. Inside this method I take the id of the user and I make a hash of it with some other parameter and passwords. Then, I send to B the user id, the hash, and a boolean to choose between login and logout.
private void SendPostRequest(bool login)
{
// Create the combine string
string data = // userId combined with more stuff
// Create the hash of the combine string
HashAlgorithm algorithm = MD5.Create();
byte[] hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(data));
StringBuilder sb = new StringBuilder();
foreach (byte b in hash)
sb.Append(b.ToString("X2"));
string encriptedData = sb.ToString();
// Fill the url with the path and the data
string url = "http://localhost/xxx/yyy/ExternalAuthentication/Login?id=" + _cachedCustomer.Id + "&hash=" + encriptedData + "&login=" + login.ToString();
// Make the Post request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream resStream = response.GetResponseStream();
}
After it, I have created in B a new class to handle the login logic. I have use a HttpContext.Current.Application variable to store the status of the authentication:
public class ExternalAuthenticationController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Login(string id, string hash, string login)
{
// Create the combine string
string data = //user id + same stuff than in A;
// Create the hash of the combine string
HashAlgorithm algorithm = MD5.Create();
byte[] hashArray = algorithm.ComputeHash(Encoding.UTF8.GetBytes(data));
StringBuilder sb = new StringBuilder();
foreach (byte b in hashArray)
sb.Append(b.ToString("X2"));
string originalHash = sb.ToString();
// Compare the two hash. If they are the same, create the variable
if (hash.CompareTo(originalHash) == 0)
{
if (System.Web.HttpContext.Current.Application["Auth"] == null)
{
System.Web.HttpContext.Current.Application["Auth"] = false;
}
if (Convert.ToBoolean(login))
{
System.Web.HttpContext.Current.Application["Auth"] = true;
}
else
{
System.Web.HttpContext.Current.Application["Auth"] = false;
}
}
}
Probably, the answer provided by #vijay shiyani is better and more generalistic, but from my point of view it requires a lot of time to implement it.

OAuth authentication without browser [duplicate]

I'm trying to create a .NET-based client app (in WPF - although for the time being I'm just doing it as a console app) to integrate with an OAuth-enabled application, specifically Mendeley (http://dev.mendeley.com), which apparently uses 3-legged OAuth.
This is my first time using OAuth, and I'm having a lot of difficulty getting started with it. I've found several .NET OAuth libraries or helpers, but they seem to be more complicated than I think I need. All I want to do is be able to issue REST requests to the Mendeley API and get responses back!
So far, I've tried:
DotNetOpenAuth
http://github.com/bittercoder/DevDefined.OAuth
http://oauth.googlecode.com/svn/code/csharp/
The first (DotNetOpenAuth) seems like it could possibly do what I needed if I spent hours and hours trying to work out how. The second and third, as best I can tell, don't support the verification codes that Mendeley is sending back -- although I could be wrong about this :)
I've got a consumer key and secret from Mendeley, and with DotNetOpenAuth I managed to get a browser launched with the Mendeley page providing a verification code for the user to enter into the application. However, at this point I got lost and couldn't work out how to sensibly provide that back to the application.
I'm very willing to admit that I have no idea where to start with this (although it seems like there's quite a steep learning curve) - if anyone can point me in the right direction I'd appreciate it!
I agree with you. The open-source OAuth support classes available for .NET apps are hard to understand, overly complicated (how many methods are exposed by DotNetOpenAuth?), poorly designed (look at the methods with 10 string parameters in the OAuthBase.cs module from that google link you provided - there's no state management at all), or otherwise unsatisfactory.
It doesn't need to be this complicated.
I'm not an expert on OAuth, but I have produced an OAuth client-side manager class, that I use successfully with Twitter and TwitPic. It's relatively simple to use. It's open source and available here: Oauth.cs
For review, in OAuth 1.0a...kinda funny, there's a special name and it looks like a "standard" but as far as I know the only service that implements "OAuth 1.0a" is Twitter. I guess that's standard enough. ok, anyway in OAuth 1.0a, the way it works for desktop apps is this:
You, the developer of the app, register the app and get a "consumer key" and "consumer secret". On Arstechnica, there's a well written analysis of why this model isn't the best, but as they say, it is what it is.
Your app runs. The first time it runs, it needs to get the user to explicitly grant approval for the app to make oauth-authenticated REST requests to Twitter and its sister services (like TwitPic). To do this you must go through an approval process, involving explicit approval by the user. This happens only the first time the app runs. Like this:
request a "request token". Aka temporary token.
pop a web page, passing that request token as a query param. This web page presents UI to the user, asking "do you want to grant access to this app?"
the user logs in to the twitter web page, and grants or denies access.
the response html page appears. If the user has granted access, there's a PIN displayed in a 48-pt font
the user now needs to cut/paste that pin into a windows form box, and click "Next" or something similar.
the desktop app then does an oauth-authenticated request for an "Access token". Another REST request.
the desktop app receives the "access token" and "access secret".
After the approval dance, the desktop app can just use the user-specific "access token" and "access secret" (along with the app-specific "consumer key" and "consumer secret") to do authenticated requests on behalf of the user to Twitter. These don't expire, although if the user de-authorizes the app, or if Twitter for some reason de-authorizes your app, or if you lose your access token and/or secret, you'd need to do the approval dance again.
If you're not clever, the UI flow can sort of mirror the multi-step OAuth message flow. There is a better way.
Use a WebBrowser control, and open the authorize web page within the desktop app. When the user clicks "Allow", grab the response text from that WebBrowser control, extract the PIN automatically, then get the access tokens. You send 5 or 6 HTTP requests but the user needs to see only a single Allow/Deny dialog. Simple.
Like this:
If you've got the UI sorted, the only challenge that remains is to produce oauth-signed requests. This trips up lots of people because the oauth signing requirements are sort of particular. That's what the simplified OAuth Manager class does.
Example code to request a token:
var oauth = new OAuth.Manager();
// the URL to obtain a temporary "request token"
var rtUrl = "https://api.twitter.com/oauth/request_token";
oauth["consumer_key"] = MY_APP_SPECIFIC_KEY;
oauth["consumer_secret"] = MY_APP_SPECIFIC_SECRET;
oauth.AcquireRequestToken(rtUrl, "POST");
THAT'S IT. Simple. As you can see from the code, the way to get to oauth parameters is via a string-based indexer, something like a dictionary. The AcquireRequestToken method sends an oauth-signed request to the URL of the service that grants request tokens, aka temporary tokens. For Twitter, this URL is "https://api.twitter.com/oauth/request_token". The oauth spec says you need to pack up the set of oauth parameters (token, token_secret, nonce, timestamp, consumer_key, version, and callback), in a certain way (url-encoded and joined by ampersands), and in a lexicographically-sorted order, generate a signature on that result, then pack up those same parameters along with the signature, stored in the new oauth_signature parameter, in a different way (joined by commas). The OAuth manager class does this for you automatically. It generates nonces and timestamps and versions and signatures automatically - your app doesn't need to care or be aware of that stuff. Just set the oauth parameter values and make a simple method call. the manager class sends out the request and parses the response for you.
Ok, then what? Once you get the request token, you pop the web browser UI in which the user will explicitly grant approval. If you do it right, you'll pop this in an embedded browser. For Twitter, the URL for this is "https://api.twitter.com/oauth/authorize?oauth_token=" with the oauth_token appended. Do this in code like so:
var url = SERVICE_SPECIFIC_AUTHORIZE_URL_STUB + oauth["token"];
webBrowser1.Url = new Uri(url);
(If you were doing this in an external browser you'd use System.Diagnostics.Process.Start(url).)
Setting the Url property causes the WebBrowser control to navigate to that page automatically.
When the user clicks the "Allow" button a new page will be loaded. It's an HTML form and it works the same as in a full browser. In your code, register a handler for the DocumentedCompleted event of the WebBrowser control, and in that handler, grab the pin:
var divMarker = "<div id=\"oauth_pin\">"; // the div for twitter's oauth pin
var index = webBrowser1.DocumentText.LastIndexOf(divMarker) + divMarker.Length;
var snip = web1.DocumentText.Substring(index);
var pin = RE.Regex.Replace(snip,"(?s)[^0-9]*([0-9]+).*", "$1").Trim();
That's a bit of HTML screen scraping.
After grabbing the pin, you don't need the web browser any more, so:
webBrowser1.Visible = false; // all done with the web UI
...and you might want to call Dispose() on it as well.
The next step is getting the access token, by sending another HTTP message along with that pin. This is another signed oauth call, constructed with the oauth ordering and formatting I described above. But once again this is really simple with the OAuth.Manager class:
oauth.AcquireAccessToken(URL_ACCESS_TOKEN,
"POST",
pin);
For Twitter, that URL is "https://api.twitter.com/oauth/access_token".
Now you have access tokens, and you can use them in signed HTTP requests. Like this:
var authzHeader = oauth.GenerateAuthzHeader(url, "POST");
...where url is the resource endpoint. To update the user's status, it would be "http://api.twitter.com/1/statuses/update.xml?status=Hello".
Then set that string into the HTTP Header named Authorization.
To interact with third-party services, like TwitPic, you need to construct a slightly different OAuth header, like this:
var authzHeader = oauth.GenerateCredsHeader(URL_VERIFY_CREDS,
"GET",
AUTHENTICATION_REALM);
For Twitter, the values for the verify creds url and realm are "https://api.twitter.com/1/account/verify_credentials.json", and "http://api.twitter.com/" respectively.
...and put that authorization string in an HTTP header called X-Verify-Credentials-Authorization. Then send that to your service, like TwitPic, along with whatever request you're sending.
That's it.
All together, the code to update twitter status might be something like this:
// the URL to obtain a temporary "request token"
var rtUrl = "https://api.twitter.com/oauth/request_token";
var oauth = new OAuth.Manager();
// The consumer_{key,secret} are obtained via registration
oauth["consumer_key"] = "~~~CONSUMER_KEY~~~~";
oauth["consumer_secret"] = "~~~CONSUMER_SECRET~~~";
oauth.AcquireRequestToken(rtUrl, "POST");
var authzUrl = "https://api.twitter.com/oauth/authorize?oauth_token=" + oauth["token"];
// here, should use a WebBrowser control.
System.Diagnostics.Process.Start(authzUrl); // example only!
// instruct the user to type in the PIN from that browser window
var pin = "...";
var atUrl = "https://api.twitter.com/oauth/access_token";
oauth.AcquireAccessToken(atUrl, "POST", pin);
// now, update twitter status using that access token
var appUrl = "http://api.twitter.com/1/statuses/update.xml?status=Hello";
var authzHeader = oauth.GenerateAuthzHeader(appUrl, "POST");
var request = (HttpWebRequest)WebRequest.Create(appUrl);
request.Method = "POST";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authzHeader);
using (var response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode != HttpStatusCode.OK)
MessageBox.Show("There's been a problem trying to tweet:" +
Environment.NewLine +
response.StatusDescription);
}
OAuth 1.0a is sort of complicated under the covers, but using it doesn't need to be.
The OAuth.Manager handles the generation of outgoing oauth requests, and the receiving and processing of oauth content in the responses. When the Request_token request gives you an oauth_token, your app doesn't need to store it. The Oauth.Manager is smart enough to do that automatically. Likewise when the access_token request gets back an access token and secret, you don't need to explicitly store those. The OAuth.Manager handles that state for you.
In subsequent runs, when you already have the access token and secret, you can instantiate the OAuth.Manager like this:
var oauth = new OAuth.Manager();
oauth["consumer_key"] = CONSUMER_KEY;
oauth["consumer_secret"] = CONSUMER_SECRET;
oauth["token"] = your_stored_access_token;
oauth["token_secret"] = your_stored_access_secret;
...and then generate authorization headers as above.
// now, update twitter status using that access token
var appUrl = "http://api.twitter.com/1/statuses/update.xml?status=Hello";
var authzHeader = oauth.GenerateAuthzHeader(appUrl, "POST");
var request = (HttpWebRequest)WebRequest.Create(appUrl);
request.Method = "POST";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authzHeader);
using (var response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode != HttpStatusCode.OK)
MessageBox.Show("There's been a problem trying to tweet:" +
Environment.NewLine +
response.StatusDescription);
}
You can download a DLL containing the OAuth.Manager class here. There is also a helpfile in that download. Or you can view the helpfile online.
See an example of a Windows Form that uses this manager here.
WORKING EXAMPLE
Download a working example of a command-line tool that uses the class and technique described here:

How do I use the cookie container with RestSharp and ASP.NET sessions?

I'd like to be able to call an authentication action on a controller and if it succeeds, store the authenticated user details in the session.
However, I'm not sure how to keep the requests inside the session as I'm using RestSharp as a detached client. I need to somehow get a key back from the server on successful authorisation and then for each future call, check the key with that stored in the session.
How do I ensure the RestClient in RestSharp sends all future requests with the cookie set correctly so inside service calls, the session variable can be retrieved correctly?
I've been looking at the cookie container with HttpFactory but there doesn't seem to be any documentation on this anywhere.
If someone is having a similar problem, please note that the above is not quite required for a simple "store my cookies after each request" problem.
Jaffas approach above works, but you can simply attach a CookieStore to your RestClient and have the cookies be stored automatically.
I know this is not a solution for everyone, since you might want to store dedicated cookies only. On the other hand it makes your life easier for testing a REST client!
(I used Jaffas variables for ease):
CookieContainer _cookieJar = new CookieContainer();
var client = new RestClient("http://<test-server>/letteron"); //test URL
client.CookieContainer = _cookieJar;
I worked this out in the end.
Basically create a cookie container, then add the session cookie from the response into the cookie container. All future requests will then contain this cookie.
var sessionCookie = response.Cookies.SingleOrDefault(x => x.Name == "ASP.NET_SessionId");
if (sessionCookie != null)
{
_cookieJar.Add(new Cookie(sessionCookie.Name, sessionCookie.Value, sessionCookie.Path, sessionCookie.Domain));
}

Categories