I'm a hobby developer and I am fiddlng around with asp.net MVC and was trying to just get the basic Oauth to work via Twitter.
All I did was put a controller named Twitter with this code:
(it's from an online example for webforms but i slightly modified it and put it into 2 action methods, index and callback.. is this right way to do it ?)
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using OAuthLibrary;
namespace mvcTwit.Controllers
{
public class TwitterController : Controller
{
private const string AccessUrl = "http://twitter.com/oauth/access_token";
private const string AuthorizeUrl = "http://twitter.com/oauth/authorize?oauth_token={0}";
private const string RequestUrl = "http://twitter.com/oauth/request_token";
//
// GET: /Twitter/
public ActionResult Index()
{
// add these to web.config
var consumerKey = ConfigurationManager.AppSettings["consumerKey"];
var consumerSecret = ConfigurationManager.AppSettings["consumerSecret"];
// look for an access token in the callback
var requestToken = Request.QueryString["oauth_token"];
if (requestToken == null)
{
requestToken = OAuth.GetRequestToken(RequestUrl,
consumerKey,
consumerSecret);
var collection = HttpUtility.ParseQueryString(requestToken);
var authorizeUrl = String.Format(AuthorizeUrl,
collection[0]);
Response.Redirect(authorizeUrl);
}
return View();
}
public ActionResult Callback(string oauth_token)
{
var consumerKey = ConfigurationManager.AppSettings["consumerKey"];
var consumerSecret = ConfigurationManager.AppSettings["consumerSecret"];
//var requestToken = Request.QueryString["oauth_token"];
var requestToken = oauth_token;
// oauth is complete and callback is returning
// the possibly authorized request token
var collection = HttpUtility.ParseQueryString(requestToken);
// obtain access token
var accessToken = OAuth.GetAccessToken(AccessUrl,
consumerKey,
consumerSecret,
collection[0],
collection[1]);
collection = HttpUtility.ParseQueryString(accessToken);
// make a Twitter request with the access token and secret
var url = "http://twitter.com/account/verify_credentials.xml";
var verify = OAuth.GetProtectedResource(url,
"GET",
consumerKey,
consumerSecret,
collection[0],
collection[1]);
ViewData["oauth_token"] = verify;
return View();
}
}
}
When i go to mysite.com/Twitter, it does its thing and takes me to twitter.com/oauth/authorize?oauth_token=(long string here)
Then after i fill in my u/n and p/w, it takes me back to my site:
mysite.com/Twitter/callback?oauth_token=(long string)
but the error on the page is:
Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
My question is, is the signature of the Callback action correct because it expects a string back from twitter. And obviously I need to add a route in my global.asax file. What would that route look like?.. I have tried everything and i can't get it to work. Is the root of my issue or am i making a programming mistake..lol
Don't be hard on me, i'm no expert, but just learning as i go.
And, I'm testing this on a website, not localhost.
Thank You.
p.s. I have spent way to long on this and am looking for help as a last resort, so thanks for your kindness.
Looks like var collection = HttpUtility.ParseQueryString(requestToken); is not returning you two results as you expect;
var accessToken = OAuth.GetAccessToken(
AccessUrl, consumerKey, consumerSecret,
collection[0], collection[1]); <<-- this line is your problem?
I think you need to split the collection[0] into the two Oauth token parts.
Related
I'm trying to get in memory hosting working for integration testing our web api project, but have hit an issue with authentication when using it. I have recreated this on a basic Web Api project using the default Web Api template and the values controller it creates.
We are using Autofac so have included this in the test too. We are also using windows authentication for the web api, but for the purposes of the test this is not important.
The issue is that the "server" in the test always returns "Authorization has been denied for this request."
So what is the best way to authorize with the in memory server? I've commented out some code from another stackoverflow question but that also didn't seem to work.
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http;
using Autofac;
using Autofac.Integration.WebApi;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WebApiTesting.Controllers;
namespace WebApiTesting.Tests.Controllers
{
[TestClass]
public class ValuesControllerTest
{
private const string URL = "http://test.testing.com/";
private const string Username = "user";
private const string Password = "supersecret";
[TestMethod]
public void InMemoryHosting()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional});
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var server = new HttpServer(config);
var builder = new ContainerBuilder();
// Register API controllers using assembly scanning.
builder.RegisterApiControllers(typeof(ValuesController).Assembly);
var container = builder.Build();
server.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
var client = new HttpClient(server);
using (var request = CreateRequest("api/values", "application/json", HttpMethod.Get))
{
//Act
using (HttpResponseMessage response = client.SendAsync(request).Result)
{
// Assert
Assert.IsNotNull(response.Content);
Assert.AreEqual("application/json", response.Content.Headers.ContentType.MediaType);
var content = response.Content.ReadAsStringAsync().Result;
Assert.AreNotEqual("{\"Message\":\"Authorization has been denied for this request.\"}", content);
}
}
}
private HttpRequestMessage CreateRequest(string url, string mthv, HttpMethod method)
{
var request = new HttpRequestMessage { RequestUri = new Uri(URL + url) };
/*byte[] toEncodeAsBytes = Encoding.UTF8.GetBytes(string.Format("{0}:{1}", Username, Password));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(toEncodeAsBytes));*/
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mthv));
request.Method = method;
return request;
}
}
}
Just to document what worked in the end, I added the following line to my CreateRequest Method
Thread.CurrentPrincipal = new ClientRolePrincipal(new HttpListenerBasicIdentity(Username, Password));
I'm not really sure if this is the best way to handle it, but it does work at least.
[This question relates to ASP.NET MVC4, and it is about best-practice approach - so please, don't suggest hacks.]
I want to authenticate users using an auth token sent in the request URL. It works similarly to a password reset token, except in this case it does not go to a reset page but instead grants access to some portion of the site. The idea is to send the URL with the auth token to a verified email address of the user. Users can click the link and perform some actions without typing their password.
Out-of-the-box, ASP.NET has the [Authorize] attribute and the SimpleMembershipProvider - these seem to work great, but they do some voodoo magic under the hood (like auto-generating database tables), so I don't know how to extend them to add this link-based auth token.
I don't expect an exact answer, but please do point me to the right direction.
Thanks!
Uf, broad question. But I will try at least to direct you to a right direction.
So first if suggest that you use Forms Authentication as a base, but you will have to customize using of it. And I presume that you do not want to use cookies for the authentication as this is native behaviour of the Forms Authentication.
The most important point you should consider to have it you custom query string token based authentication.
Create a login action and in this action you will authorize the user, if he have granted access you ask FormsAuthentication to create AuthCookie. For the further on you just take the httpCookie.Value as your auth token that you will carry in query string.
You need to implement the Application_BeginRequest in the Global.asax that will handle this query string tokens and translate it into the cookie. With this approach you can leverage all the ASP.NET Forms Authentication infrastructure.
This is quite high level picture w/o code. If you need more detail help I can also provide it to you.
You should just use a regular Action that accepts HttpGet.
Upon receiving the token, immediately invalid it so it can't be used again.
Also, only accept tokens that are within your pre-defined range of time period, like 24 or 72 hours.
Thank you Peter for idea.
If smb need to create JWT token authorization for old ASP.NET MVC5.I wrote small example. I don't serialize cookie to JWT. I create a JWT and after I am checking it in the BeginRequest. If everything is ok, I create a cookie and set it to the httpContext.Request. I used authentication mode="Forms" for application and it require cookies.
For create JWT token:
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
[AllowAnonymous]
[HttpPost]
public ActionResult LoginJWT(LoginViewModel model)
{
ActionResult response = null;
if (ModelState.IsValid)
{
if (true) //todo: check user login&password
{
var payload = new Dictionary<string, object>
{
{ "iss", "subject" },
{ "sub", "api" },
{ "exp", DateTimeOffset.UtcNow.AddHours(2).ToUnixTimeSeconds()},
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds()},
{ "jti", Guid.NewGuid() },
{ "uid", "64" } //custom field for identificate user
};
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
response = Content(token);
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Login or password are not found");
}
}
else
{
response = new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Errors in Model");
}
return response;
}
For check JWT token in Global.asax:
public override void Init()
{
this.BeginRequest += this.BeginRequestHandler;
base.Init();
}
private void BeginRequestHandler(object sender, EventArgs e)
{
var bearerToken = this.Context.Request.Headers["Authorization"];
if (bearerToken != null)
{
var token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
int userId = 0;
try
{
IJsonSerializer serializer = new JsonNetSerializer();
var provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.DecodeToObject<IDictionary<string, string>>(token, secret, verify: true);
if (json.TryGetValue("uid", out var uid))
{
userId = Convert.ToInt32(uid);
}
}
catch (TokenExpiredException)
{
Console.WriteLine("Token has expired");
}
catch (SignatureVerificationException)
{
Console.WriteLine("Token has invalid signature");
}
if (userId != 0)
{
// check user by id, if found create cookie.
}
}
}
I used:
jwt-dotnet/jwt library 7.2.1
I saw couple of libraries which we can use to post to twitter. But I want to create my own as later we need to extend this for other social networks also .
I am using the RESTSharp to make things little easy for me.
In my controller, I wrote 2 methods..
public ActionResult TwitterLogin()
{
var authorizeUrl = TwitterService.Authorize();
if(!String.IsNullOrEmpty(authorizeUrl))
{
return Redirect(authorizeUrl);
}
else
{
return View();
}
}
public ActionResult AuthorizeCallback()
{
TwitterService.AuthorizeCallback();
return View();
}
In Twitter Service
public string Authorize()
{
client = new RestClient(BaseUrl) {Authenticator = OAuth1Authenticator.ForRequestToken(ConsumerKey, ConsumerSecret, CallbackUrl)};
var request = new RestRequest("oauth/request_token", Method.POST);
var response = client.Execute(request);
if(response.StatusCode == HttpStatusCode.OK)
{
var qs = HttpUtility.ParseQueryString(response.Content);
oauth_token = qs["oauth_token"];
oauth_token_secret = qs["oauth_token_secret"];
request = new RestRequest("oauth/authorize");
request.AddParameter("oauth_token", oauth_token);
return client.BuildUri(request).ToString();
}
return String.Empty;
}
public void AuthorizeCallback()
{
var verifier = "123456"; // <-- Breakpoint here (set verifier in debugger)
var request = new RestRequest("oauth/access_token", Method.POST);
client.Authenticator = OAuth1Authenticator.ForAccessToken(ConsumerKey, ConsumerSecret, oauth_token, oauth_token_secret, verifier);
var response = client.Execute(request);
var qs = HttpUtility.ParseQueryString(response.Content);
oauth_token = qs["oauth_token"];
oauth_token_secret = qs["oauth_token_secret"];
}
Now my concern is
Whether i am doing it right ?
Regarding Oauth, from what I understood , we create a request token from the twitter, ask the user to authorize it and use it to get an accesss Token and use it for signing other requests. I wrote this code primarly looking this link
https://github.com/restsharp/RestSharp/blob/master/RestSharp.IntegrationTests/oAuth1Tests.cs
Whether we can write this any better ?
Also somebody could guide me on this , how to use the RestSharp to create OAuth requests to use the API's like twitter. Most of the internet references are based on the custom libs
I just started playing around with RestSharp and WebApi and I've run into a little bit of an issue.
I'm not sure if this is best practice or even possible, but I'll demonstrate through code (this isn't my exact code but it's the exact same concept)
[HttpPost]
public HttpResponseMessage CreateEmployee(Employee emp, int employeeType)
{
// CREATE EMPLOYEE
return Request.CreateResponse(HttpStatusCode.Created, emp.id);
}
I've created a console app to test this using RestSharp. Here's what I have:
var client = new RestClient();
client.BaseUrl = #"http://localhost:15507";
var employee = new Employee();
//populate employee model
postrequest.Method = Method.POST;
postrequest.Resource = "api/Employee/CreateEmployee";
postrequest.AddHeader("Accept", "application/json");
postrequest.AddHeader("Content-Type", "application/json");
postrequest.RequestFormat = DataFormat.Json;
postrequest.AddBody(new { emp = employee, listId = 2 });
var res = client.Execute(postrequest);
The error that I get is that employeeType parameter comes in as null. Am I formatting this properly? Is this something that's even possible to do?
When i remove the employeeType parameter from the WebApi action method and modify the request to:
postrequest.AddBody(employee);
everything works fine.
any ideas? Thanks
if you are expecting employeetype from uri and if its not part of defined route, you can send it as query string parameter...Ex:
api/Employee/CreateEmployee?employeeType=
I'm building an application in ASP.NET MVC (using C#) and I would like to know how I can perform calls like curl http://www.mywebsite.com/clients_list.xml inside my controller
Basically I would like to build a kind of REST API to perform actions such as show edit and delete, such as Twitter API.
But unfortunately until now I didn't find anything besides that cURL for windows on this website: http://curl.haxx.se/
So I don't know if is there any traditional way to retrieve this kind of call from URL with methods like post delete and put on the requests, etc...
I just would like to know an easy way to perform commands like curl inside my controller on my ASP.NET MVC Application.
UPDATE:
Hi so I managed to make GET Requests but now I'm having a serious problem in retrieve POST Request for example, I'm using the update status API from Twitter that in curl would work like this:
curl -u user:password -d "status=playing with cURL and the Twitter API" http://twitter.com/statuses/update.xml
but on my ASP.NET MVC application I'm doing like this inside my custom function:
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.Credentials = new NetworkCredential("username", "password");
request.Headers.Add("status", "Tweeting from ASP.NET MVC C#");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return responseText;
Now the problem is that this request is returning 403 Forbidden,
I really don't know why if it works perfectly on curl
:\
UPDATE:
I finally manage to get it working, but probably there's a way to make it cleaner and beautiful, as I'm new on C# I'll need more knowledge to do it, the way the POST params are passed makes me very confused because is a lot of code to just pass params.
Well, I've created a Gist - http://gist.github.com/215900 , so everybody feel free to revise it as you will. Thanks for your help çağdaş
also follow the code here:
public string TwitterCurl()
{
//PREVENT RESPONSE 417 - EXPECTATION FAILED
System.Net.ServicePointManager.Expect100Continue = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.Credentials = new NetworkCredential("twitterUsername", "twitterPassword");
//DECLARE POST PARAMS
string headerVars = String.Format("status={0}", "Tweeting from ASP.NET MVC C#");
request.ContentLength = headerVars.Length;
//SEND INFORMATION
using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream(), ASCIIEncoding.ASCII))
{
streamWriter.Write(headerVars);
streamWriter.Close();
}
//RETRIEVE RESPONSE
string responseText = String.Empty;
using (StreamReader sr = new StreamReader(request.GetResponse().GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return responseText;
/*
//I'M NOT SURE WHAT THIS IS FOR
request.Timeout = 500000;
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "Custom Twitter Agent";
#if USE_PROXY
request.Proxy = new WebProxy("http://localhost:3000", false);
#endif
*/
}
Try using Microsoft.Http.HttpClient. This is what your request would look like
var client = new HttpClient();
client.DefaultHeaders.Authorization = Credential.CreateBasic("username","password");
var form = new HttpUrlEncodedForm();
form.Add("status","Test tweet using Microsoft.Http.HttpClient");
var content = form.CreateHttpContent();
var resp = client.Post("http://www.twitter.com/statuses/update.xml", content);
string result = resp.Content.ReadAsString();
You can find this library and its source included in the WCF REST Starter kit Preview 2, however it can be used independently of the rest of the stuff in there.
P.S. I tested this code on my twitter account and it works.
Example code using HttpWebRequest and HttpWebResponse :
public string GetResponseText(string url) {
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
responseText = sr.ReadToEnd();
}
return responseText;
}
To POST data :
public string GetResponseText(string url, string postData) {
string responseText = String.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentLength = postData.Length;
using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) {
sw.Write(postData);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
responseText = sr.ReadToEnd();
}
return responseText;
}
This is the single line of code I use for calls to a RESTful API that returns JSON.
return ((dynamic) JsonConvert.DeserializeObject<ExpandoObject>(
new WebClient().DownloadString(
GetUri(surveyId))
)).data;
Notes
The Uri is generated off stage using the surveyId and credentials
The 'data' property is part of the de-serialized JSON object returned
by the SurveyGizmo API
The Complete Service
public static class SurveyGizmoService
{
public static string UserName { get { return WebConfigurationManager.AppSettings["SurveyGizmo.UserName"]; } }
public static string Password { get { return WebConfigurationManager.AppSettings["SurveyGizmo.Password"]; } }
public static string ApiUri { get { return WebConfigurationManager.AppSettings["SurveyGizmo.ApiUri"]; } }
public static string SurveyId { get { return WebConfigurationManager.AppSettings["SurveyGizmo.Survey"]; } }
public static dynamic GetSurvey(string surveyId = null)
{
return ((dynamic) JsonConvert.DeserializeObject<ExpandoObject>(
new WebClient().DownloadString(
GetUri(surveyId))
)).data;
}
private static Uri GetUri(string surveyId = null)
{
if (surveyId == null) surveyId = SurveyId;
return new UriBuilder(ApiUri)
{
Path = "/head/survey/" + surveyId,
Query = String.Format("user:pass={0}:{1}", UserName, Password)
}.Uri;
}
}
Look into the System.Net.WebClient class. It should offer the functionality you require. For finer grained control, you might find WebRequest to be more useful, but WebClient seems the best fit for your needs.