Linq to Twitter Authenticated But Getting 215 "Bad Authentication Data" - twitter

I have been using Linq to Twitter for Authenticating with OAuth for the Twitter API 1.1.
I set my credentials and then pass them to Linq to Twitter which states I that isAuth returns true. However, at run time I receive at 215 "Bad Authentication Data" error. Has anyone else had this problem?
var auth = new SingleUserAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = TwitterSettings.ConsumerKey,
ConsumerSecret = TwitterSettings.ConsumerKeySecret,
OAuthToken = TwitterSettings.AccessToken,
AccessToken = TwitterSettings.AccessTokenSecret,
}
};
auth.Authorize();
}
And here is the if else that is stating I am authorized:
if (auth == null || !auth.IsAuthorized)
{
}
// If Twitter Authorizes Application
if (auth.IsAuthorized && i == 1)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(new Uri("https://api.twitter.com/1.1/search/tweets.json?q=%23freebandnames&since_id=24012619984051000&max_id=250126199840518145&result_type=mixed&count=4"));
string json = await response.Content.ReadAsStringAsync();
JObject obj = JObject.Parse(json);
var jsonResults = obj["results"];
await JsonConvert.DeserializeObjectAsync<List<Tweet>>(jsonResults.ToString());
}
I am able to get into the auth.IsAtuhorized portion of the code but the response is returning a 215 error. Also I know my URI is correct because I copied it straight from the Twitter API 1.1 example page to test the call. Thanks in Advance

You're using the authorizer properly, but then you aren't using LINQ to Twitter to make your query. You're using HttpClient instead and there isn't a relationship between the two APIs. Here's a example of how to perform a Search with LINQ to Twitter:
var srch =
(from search in twitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == "LINQ to Twitter" &&
search.Count == 7
select search)
.SingleOrDefault();
Console.WriteLine("\nQuery: {0}\n", srch.SearchMetaData.Query);
srch.Statuses.ForEach(entry =>
Console.WriteLine(
"ID: {0, -15}, Source: {1}\nContent: {2}\n",
entry.StatusID, entry.Source, entry.Text));
You can visit the LINQ to Twitter Search Documentation for more info on available parameters.
Update, Here's an asynchronous sample:
(from search in twitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == QueryTextBox.Text
select search)
.MaterializedAsyncCallback(asyncResponse =>
Dispatcher.BeginInvoke(() =>
{
if (asyncResponse.Status != TwitterErrorStatus.Success)
{
MessageBox.Show("Error during query: " + asyncResponse.Exception.Message);
return;
}
Search search = asyncResponse.State.SingleOrDefault();
var tweets =
(from status in search.Statuses
select new Tweet
{
UserName = status.User.Identifier.ScreenName,
Message = status.Text,
ImageSource = status.User.ProfileImageUrl
})
.ToList();
SearchListBox.ItemsSource = tweets;
}));

Related

Twitter external login with Owin gives HTTP 403 (Forbidden) on callback

I am trying to implement twitter sign in/up. In a asp.net web app, but i am getting 403 http status on the final callback.
I have my callback urls configured in the twitter app portal (I think they are correct)
I give a little bit of context of what i am trying to do
Redict the user to the twitter sining
Then the first callback executes (no issue here) and i call the twitter api to get the user details.
After getting the user details i return a challenge result so i can get the user identity and i specify a second callback for that
The second callback does not execute.
Does somebody can point out to me what am I doing wrong? Or how can i debug the issue?
I am aware that twitter checks that the callback url needs to be set in the app developer portal I got that from this question question
Here's my code and config
app.UseTwitterAuthentication(new TwitterAuthenticationOptions()
{
ConsumerKey = "key",
ConsumerSecret = "qCLLsuS79YDkmr2DGiyjruV76mWZ4hVZ4EiLU1RpZkxOfDqwmh",
Provider = new Microsoft.Owin.Security.Twitter.TwitterAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:twitter:access_token", context.AccessToken));
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:twitter:access_secret", context.AccessTokenSecret));
return Task.FromResult(0);
}
},
BackchannelCertificateValidator = new Microsoft.Owin.Security.CertificateSubjectKeyIdentifierValidator(new[]
{
"A5EF0B11CEC04103A34A659048B21CE0572D7D47", // VeriSign Class 3 Secure Server CA - G2
"0D445C165344C1827E1D20AB25F40163D8BE79A5", // VeriSign Class 3 Secure Server CA - G3
"7FD365A7C2DDECBBF03009F34339FA02AF333133", // VeriSign Class 3 Public Primary Certification Authority - G5
"39A55D933676616E73A761DFA16A7E59CDE66FAD", // Symantec Class 3 Secure Server CA - G4
"‎add53f6680fe66e383cbac3e60922e3b4c412bed", // Symantec Class 3 EV SSL CA - G3
"4eb6d578499b1ccf5f581ead56be3d9b6744a5e5", // VeriSign Class 3 Primary CA - G5
"5168FF90AF0207753CCCD9656462A212B859723B", // DigiCert SHA2 High Assurance Server C‎A
"B13EC36903F8BF4701D498261A0802EF63642BC3" // DigiCert High Assurance EV Root CA
}),
});
Calling twitter sign in (I specify the first callback url and this one works )
[AllowAnonymous]
public ActionResult TwitterRegistration()
{
string UrlPath = HttpContext.Request.Url.Authority;
// pass in the consumerkey, consumersecret, and return url to get back the token
NameValueCollection dict = new TwitterClient().GenerateTokenUrl(ConsumerKey, ConsumerSecret, "https://" + UrlPath + "/Account/TwitterRegistrationCallback");
// set a session var so we can use it when twitter calls us back
Session["dict"] = dict;
// call "authenticate" not "authorize" as the twitter docs say so the user doesn't have to reauthorize the app everytime
return Redirect("https://api.twitter.com/oauth/authenticate?oauth_token=" + dict["oauth_token"]);
}
After the callback I call the twitter api to get the user data that works too
[AllowAnonymous]
public ActionResult TwitterRegistrationCallback(string oauth_token, string oauth_verifier)
{
TwitterClient twitterClient = new TwitterClient();
NameValueCollection dict = (NameValueCollection)Session["dict"];
NameValueCollection UserDictionary = HttpUtility.ParseQueryString(twitterClient.GetAccessToken(ConsumerKey, ConsumerSecret, oauth_token, oauth_verifier, dict));
TwitterUserModel twitterUser = JsonConvert.DeserializeObject<TwitterUserModel>(twitterClient.GetTwitterUser(ConsumerKey, ConsumerSecret, UserDictionary));
Session["twitterUser"] = twitterUser;
// Returning challenge not working just redirecting to the action inn case of twitter as we are already authenitcated
return new ChallengeResult("Twitter", Url.Action("ExternalRegistrationCallback", "Account", null));
}
But when I return the Challange result which ends up calling
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
it gives me the exception below (which is the same in the original question)
Here is the callback that is not being called
// GET: /Account/ExternalRegistrationCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalRegistrationCallback()
{
//TODO: Check
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Manage");
}
var loginInfo = await _authenticationManager.GetExternalLoginInfoAsync();
if (Session["twitterUser"] != null)
{
//Workarround for twitter registration callback not using the challenge
loginInfo = new ExternalLoginInfo();
TwitterUserModel twitterUser = (TwitterUserModel)Session["twitterUser"];
loginInfo.Email = twitterUser.email;
}
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Get the information about the user from the external login provider
var info = await _authenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
// Sign in the user with this external login provider if the user already has a login
var result = await _signInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
//User is already registered We show error and tell the user to go back to login page?
return RedirectToLocal((string)Session["ReturnUrl"]);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
//
return RedirectToAction("SendCode", new { ReturnUrl = (string)Session["ReturnUrl"], RememberMe = false });
case SignInStatus.Failure:
default:
// User is authenticated through the previous challange, So here needs to be saved
RegistrationBasicViewModel model = (RegistrationBasicViewModel)Session["RegistrationModel"];
//Check the user is in our db?
ApplicationUser user = _userManager.FindByEmail(loginInfo.Email);
IdentityResult identityResult;
if (user == null)
{
user = new ApplicationUser
{
UserName = loginInfo.Email,
Email = loginInfo.Email,
FirstName = model.FirstName,
LastName = model.LastName,
Nickname = model.Nickname
};
identityResult = await _userManager.CreateAsync(user);
}
else
{
//TODO : Here we might want to tell the user it already exists
identityResult = IdentityResult.Success;
//IdentityResult.Failed(new string[] { "User already registered" });
}
if (identityResult.Succeeded)
{
identityResult = await _userManager.AddLoginAsync(user.Id, info.Login);
if (identityResult.Succeeded)
{
//Adding the branch after te user is sucessfully added
await _signInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
_userBranchService.AddUserBranch(user.Id, model.BranchId);
//Redirect to home page
return RedirectToLocal((string)Session["ReturnUrl"]);
}
}
setPartnerBranchViewBag(model.PartnerId, (string) Session["partner"]);
AddErrors(identityResult);
return View("Register", model );
}
}
Twitter config
[HttpRequestException: Response status code does not indicate success: 403 (Forbidden).]
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() +223
Microsoft.Owin.Security.Twitter.<ObtainRequestTokenAsync>d__23.MoveNext(
Apparently Owin uses a default url (not the url set on the Challange)
The default url is /signin-twitter So in my case i had to configure https://localhost:44378/signin-twitter as one of the callback urls in the twitter app portal
Even after adding the /signin-twitter to my callback url's, I receive the "Response status code does not indicate success: 403 (Forbidden)." error.
[HttpRequestException: Response status code does not indicate success: 403 (Forbidden).]
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() +121662
Microsoft.Owin.Security.Twitter.<ObtainRequestTokenAsync>d__23.MoveNext() +2389
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +31
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +60
Microsoft.Owin.Security.Twitter.<ApplyResponseChallengeAsync>d__12.MoveNext() +1091
This exception is thrown, even when using the default, out of the box Asp.NET MVC template.

RestSharp / MailChimp 'API key missing' error

When I post a campaign on Mailchimp using RestSharp, it tells me my API key is missing, but when I click "Get Campaign", it successfully shows me all the campaign data.
Can anyone tell me where I'm going wrong? Here's my code:
public MailChimpPostModel PostCampaign(MailChimpPostModel post)
{
var auth = _userBusinessObject.GetUserWebsiteAuthorizationByWebsite(_userId,
_websiteId,
_linkvanaNetworkSiteId);
ApiBaseUrl = <url> ;
if (auth == null)
throw new RestRequestResponseException { Error = RestErrorsEnum.NotAuthenticated };
var request = new RestRequest(3.0/campaigns, Method.POST);
request.AddParameter("access_token", <Token>);
request.AddParameter("apikey", <Token> + "-" + <dc>);
request.AddHeader("content-type", "application/json");
request.AddBody(post);
var response = Execute<MailChimpPostModel>(request);
return response;
}
// replace usX to match the last 3 of your API
var client = new RestClient("https://usX.api.mailchimp.com/3.0/");
client.Authenticator = new HttpBasicAuthenticator("user", APIKey);

Linq to Twitter - Bad Authentication data

I've the the latest version of Linq to Twitter (3.1.2), and I'm receiving the "Bad Authentication data" error with the code below:
var auth = new ApplicationOnlyAuthorizer
{
CredentialStore = new InMemoryCredentialStore
{
ConsumerKey = "xxxx",
ConsumerSecret = "xxxx"
}
};
using (var twitter = new TwitterContext(auth))
{
var users = twitter.User.Where(s => s.Type == UserType.Search && s.Query == "filter:verified").ToList();
}
I thought at first that it could be Twitter taking a while to accept my new credentials, but I used Twitter's OAuth tool with my keys, and they produced tokens without issue. Any ideas what I'm missing here?
I could not find a duplicate, as the code referenced # https://stackoverflow.com/questions/16387037/twitter-api-application-only-authentication-with-linq2twitter#= is no longer valid in the version I am running.
That query doesn't support Application-Only authorization. Here's the Twitter docs to that:
https://dev.twitter.com/rest/reference/get/users/search
Instead, you can use SingleUserAuthorizer, documented here:
https://github.com/JoeMayo/LinqToTwitter/wiki/Single-User-Authorization
Like this:
var auth = new SingleUserAuthorizer
{
CredentialStore = new SingleUserInMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"],
AccessToken = ConfigurationManager.AppSettings["accessToken"],
AccessTokenSecret = ConfigurationManager.AppSettings["accessTokenSecret"]
}
};
To find out what type of authorization is possible, you can visit the L2T wiki at:
https://github.com/JoeMayo/LinqToTwitter/wiki
and each API query and command has a link at the bottom of the page to the corresponding Twitter API documentation.

How can I login to Meteor with native device Facebook?

Suppose I logged into my device's Facebook authentication, like system Facebook on iOS. I obtain an access token.
How can I use the access token to login to Meteor's Facebook Oauth provider?
To login with Facebook using an access token obtained by another means, like iOS Facebook SDK, define a method on the server that calls the appropriate Accounts method:
$FB = function () {
if (Meteor.isClient) {
throw new Meteor.Error(500, "Cannot run on client.");
}
var args = Array.prototype.slice.call(arguments);
if (args.length === 0) {
return;
}
var path = args[0];
var i = 1;
// Concatenate strings together in args
while (_.isString(args[i])) {
path = path + "/" + args[i];
i++;
}
if (_.isUndefined(path)) {
throw new Meteor.Error(500, 'No Facebook API path provided.');
}
var FB = Meteor.npmRequire('fb');
var fbResponse = Meteor.sync(function (done) {
FB.napi.apply(FB, [path].concat(args.splice(i)).concat([done]));
});
if (fbResponse.error !== null) {
console.error(fbResponse.error.stack);
throw new Meteor.Error(500, "Facebook API error.", {error: fbResponse.error, request: args});
}
return fbResponse.result;
};
Meteor.methods({
/**
* Login to Meteor with a Facebook access token
* #param accessToken Your Facebook access token
* #returns {*}
*/
facebookLoginWithAccessToken: function (accessToken) {
check(accessToken, String);
var serviceData = {
accessToken: accessToken
};
// Confirm that your accessToken is you
try {
var tokenInfo = $FB('debug_token', {
input_token: accessToken,
access_token: Meteor.settings.facebook.appId + '|' + Meteor.settings.facebook.secret
});
} catch (e) {
throw new Meteor.Error(500, 'Facebook login failed. An API error occurred.');
}
if (!tokenInfo.data.is_valid) {
throw new Meteor.Error(503, 'This access token is not valid.');
}
if (tokenInfo.data.app_id !== Meteor.settings.facebook.appId) {
throw new Meteor.Error(503, 'This token is not for this app.');
}
// Force the user id to be the access token's user id
serviceData.id = tokenInfo.data.user_id;
// Returns a token you can use to login
var loginResult = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});
// Login the user
this.setUserId(loginResult.userId);
// Return the token and the user id
return loginResult;
}
}
This code depends on the meteorhacks:npm package. You should call meteor add meteorhacks:npm and have a package.json file with the Facebook node API: { "fb": "0.7.0" }.
If you use demeteorizer to deploy your app, you will have to edit the output package.json and set the scrumptious dependency from "0.0.1" to "0.0.0".
On the client, call the method with the appropriate parameters, and you're logged in!
In Meteor 0.8+, the result of Accounts.updateOrCreateUserFromExternalService has changed to an object containing {userId: ...} and furthermore, no longer has the stamped token.
You can get the accessToken in the Meteor.user() data at Meteor.user().services.facebook.accessToken (be aware this can only be accessed on the server side as the services field is not exposed to the client.
So when a user logs in with facebook on your meteor site these fields would be populated with the user's facebook data. If you check your meteor user's database with mongo or some other gui tool you could see all the fields which you have access to.
Building on DrPangloss' most excellent answer above, combining it with this awesome post: http://meteorhacks.com/extending-meteor-accounts.html
You'll run into some issues using ObjectiveDDP in trying to get the client persist the login. Include the header:
#import "MeteorClient+Private.h"
And manually set the required internals. Soon I'll make a meteorite package and an extension to MyMeteor (https://github.com/premosystems/MyMeteor) but for now it's manual.
loginRequest: {"accessToken":"XXXXXb3Qh6sBADEKeEkzWL2ItDon4bMl5B8WLHZCb3qfL11NR4HKo4TXZAgfXcySav5Y8mavDqZAhZCZCnDDzVbdNmaBAlVZAGENayvuyStkTYHQ554fLadKNz32Dym4wbILisPNLZBjDyZAlfSSgksZCsQFxGPlovaiOjrAFXwBYGFFZAMypT9D4qcZC6kdGH2Xb9V1yHm4h6ugXXXXXX","fbData":{"link":"https://www.facebook.com/app_scoped_user_id/10152179306019999/","id":"10152179306019999","first_name":"users' first name","name":"user's Full Name","gender":"male","last_name":"user's last name","email":"users#email.com","locale":"en_US","timezone":-5,"updated_time":"2014-01-11T23:41:29+0000","verified":true}}
Meteor.startup(
function(){
Accounts.registerLoginHandler(function(loginRequest) {
//there are multiple login handlers in meteor.
//a login request go through all these handlers to find it's login hander
//so in our login handler, we only consider login requests which has admin field
console.log('loginRequest: ' + JSON.stringify(loginRequest));
if(loginRequest.fbData == undefined) {
return undefined;
}
//our authentication logic :)
if(loginRequest.accessToken == undefined) {
return null;
} else {
// TODO: Verfiy that the token from facebook is valid...
// https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.0#checktoken
// graph.facebook.com/debug_token? input_token={token-to-inspect}&access_token={app-token-or-admin-token}
}
//we create a user if not exists, and get the userId
var email = loginRequest.fbData.email || "-" + id + "#facebook.com";
var serviceData = {
id: loginRequest.fbData.id,
accessToken: loginRequest.accessToken,
email: email
};
var options = {
profile: {
name: loginRequest.fbData.name
}
};
var user = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, options);
console.log('Logged in from facebook: ' + user.userId);
//send loggedin user's user id
return {
userId: user.userId
}
});
}
);
This answer could be improved further as we can now directly debug the token from a REST http request using futures. Credit still goes to #DoctorPangloss for the principal steps necessary.
//Roughly like this - I removed it from a try/catch
var future = new Future();
var serviceData = {
accessToken: accessToken,
email: email
};
var input = Meteor.settings.private.facebook.id + '|' + Meteor.settings.private.facebook.secret
var url = "https://graph.facebook.com/debug_token?input_token=" + accessToken + "&access_token=" + input
HTTP.call( 'GET', url, function( error, response ) {
if (error) {
future.throw(new Meteor.Error(503, 'A error validating your login has occured.'));
}
var info = response.data.data
if (!info.is_valid) {
future.throw(new Meteor.Error(503, 'This access token is not valid.'));
}
if (info.app_id !== Meteor.settings.private.facebook.id) {
future.throw(new Meteor.Error(503, 'This token is not for this app.'));
}
// Force the user id to be the access token's user id
serviceData.id = info.user_id;
// Returns a token you can use to login
var user = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});
if(!user.userId){
future.throw(new Meteor.Error(500, "Failed to create user"));
}
//Add email & user details if necessary
Meteor.users.update(user.userId, { $set : { fname : fname, lname : lname }})
Accounts.addEmail(user.userId, email)
//Generate your own access token!
var token = Accounts._generateStampedLoginToken()
Accounts._insertLoginToken(user.userId, token);
// Return the token and the user id
future.return({
'x-user-id' : user.userId,
'x-auth-token' : token.token
})
});
return future.wait();
Use this instead of the JS lib suggested by #DoctorPangloss. Follow the same principles he suggested but this avoids the need to integrate an additional library

DotNetOpenAuth detect fetch request like google

With google, you can fetch the user's email like this:
var fetch = new FetchRequest();
fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
request.AddExtension(fetch);
and get it back like this:
var fetch = response.GetExtension<FetchResponse>();
string email = "";
if (fetch != null)
{
email = fetch.GetAttributeValue(WellKnownAttributes.Contact.Email);
}
When writing a provider, how can I return the values asked for?
The OpenIdProviderWebForms sample that comes with DotNetOpenAuth includes returning user attributes. Have you checked it out?

Resources