async and await with Twitter direct_messages/events using LinqToTwitter - c#

I'm really stuck in this for days. I'm using LinqToTwitter with ASP.Net C#
I'm trying to get the new DirectMessages work, I followed the examples but with no luck.
I want the function to work on Button click, so what I tried is:
BtnClick:
`
protected void Btn1_Click(object sender, EventArgs e)
{
string x = MyTest().Result;
}
`
MyTest:
`
static async Task<string> mytest()
{
AspNetAuthorizer auth = DoAuthorization();
var twitterCtx = new TwitterContext(auth);
List<DMEvent> AllDmEvents = new List<DMEvent>();
string Cursor;
DirectMessageEvents dmResponse =
await
(from dm in twitterCtx.DirectMessageEvents
where dm.Type == DirectMessageEventsType.List &&
dm.Count == 10
select dm)
.SingleOrDefaultAsync(); //In debugging mode, after this line is executed, it will go away and keep loading forever and never come back
AllDmEvents.AddRange(dmResponse.Value.DMEvents);
Cursor = dmResponse.Value.NextCursor;
string xxx = (JsonConvert.SerializeObject(AllDmEvents, Formatting.None));
return xxx;
}
`
DoAuthorization:
`
static AspNetAuthorizer DoAuthorization()
{
AspNetAuthorizer auth = new AspNetAuthorizer();
auth = new AspNetAuthorizer
{
CredentialStore = new SessionStateCredentialStore
{
ConsumerKey = "MyConsumerKey",
ConsumerSecret = "MyConsumerSecret ",
OAuthToken = "MyOAuthToken ",
OAuthTokenSecret = "MyOAuthTokenSecret ",
ScreenName = "MyUserName",
UserID = 12345678
}
};
return auth;
}`
Any help would be SO much appreciated!

The DoAuthorization() in your code looks like it came from the Console sample and that won't work with ASP.NET. The reason is that ASP.NET is stateless and the OAuth process brings you to the Twitter site and back. So, you have to break up the authorization into two pieces: Begin and Complete.
I'm guessing that you're using ASP.NET MVC, but the concept is similar (but different) if you're using WebForms). Here's the Begin part:
public class OAuthController : AsyncController
{
public ActionResult Index()
{
return View();
}
public async Task<ActionResult> BeginAsync()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
}
};
Notice that it uses an MvcAuthorizer, populating credentials. Once you have the MvcAuthorizer instance, redirect the user to Twitter for authorization, like this:
string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
}
That send the user to the Twitter authorization page, where they give your app permission to operate on their behalf. Twitter will redirect the user back to twitterCallback, which is why the code above modified the URL to replace the Begin with Complete in your URL. So, Twitter redirect the user back to your app, which calls the CompleteAsync() action below:
public async Task<ActionResult> CompleteAsync()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore()
};
await auth.CompleteAuthorizeAsync(Request.Url);
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database,
// isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent
// queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let
// you make queries without re-authorizing.
//
//var credentials = auth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
return RedirectToAction("Index", "Home");
}
Now that your app has the user's permissions, grab their tokens and hold them for subsequent queries so you don't have to continue the OAuth process every time the user wants to use your app. Please see the notes in the code on how to get those credentials.
Now, when you want to perform a query, instantiate an MvcAuthorizer, like this:
static async Task<string> mytest()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore()
};
var twitterCtx = new TwitterContext(auth);
List<DMEvent> AllDmEvents = new List<DMEvent>();
string Cursor;
DirectMessageEvents dmResponse =
await
(from dm in twitterCtx.DirectMessageEvents
where dm.Type == DirectMessageEventsType.List &&
dm.Count == 10
select dm)
.SingleOrDefaultAsync(); //In debugging mode, after this line is executed, it will go away and keep loading forever and never come back
AllDmEvents.AddRange(dmResponse.Value.DMEvents);
Cursor = dmResponse.Value.NextCursor;
string xxx = (JsonConvert.SerializeObject(AllDmEvents, Formatting.None));
return xxx;
}
You can see how the first statement of your modified myTest() method instantiates MvcAuthorizer with SessionStateCredentialStore, holding your credentials.
Finally, at the point in time where you want the user to authorize your app with Twitter (log in, on first query, or any other timing of your choice), check to see whether they're already authorized and re-direct if not, like this:
public ActionResult Index()
{
if (!new SessionStateCredentialStore().HasAllCredentials())
return RedirectToAction("Index", "OAuth");
return View();
}
Notice how the code above calls HasAllCredentials() on a SessionStateCredentialStore instance. I assume that you'll be adding your own logic to determine when to load the user's credentials, but wanted you to be aware of the HasAllCredentials() helper method to make it easier to know when the user must be authorized.
For more info, visit the LINQ to Twitter OAuth docs. The LINQ to Twitter source code also has Samples on how to use OAuth.

Related

OpenID Connect not working with ASP.NET MVC 5

I'm operating an ASP.NET MVC application that leverages Microsoft OpenID Connect.
(manual : https://learn.microsoft.com/ko-kr/azure/active-directory/develop/tutorial-v2-asp-webapp)
We began our service in October 2019, and had no problems with it until now. After the last successful log in, we are facing authorization failure in the redirected part and cannot verify the Claim (User.Identity) information, hence resulting in an infinite loop issue.
I hope I can get assistance with this problem.
After logging in successfully, I store the authentication information so that I can share the token value in the RESTful API which is configured separately.
In the earlier version, the token value was stored in the Token table and was stored in the session.
But when I started to used the session from the second attempt, the problem started to occur so I started not to use the session.
At this point, I tried several login attempts and saw that it worked well without a problem.
Initially, the application was properly working, but as time passed by, I can no longer retrieve the authentication information and an infinite loop occurs once again.
Code:
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties{ RedirectUri = "/Auth/SignedIn" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
public void SignOut()
{
this.RemoveSessionToken();
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
public async Task<ActionResult> SignedIn()
{
var IsLoggedIn = Request.IsAuthenticated;
var claims = (System.Security.Claims.ClaimsIdentity) ClaimsPrincipal.Current.Identity;
if (IsLoggedIn)
{
var userClaims = User.Identity as System.Security.Claims.ClaimsIdentity;
//string sessionToken = Session.SessionID;
string ipAddress = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (String.IsNullOrEmpty(ipAddress))
ipAddress = Request.ServerVariables["REMOTE_ADDR"];
var dbContext = new GetOfficeEduUsersEntities();
var token = new T_APIToken();
string office365Id = userClaims?.FindFirst("preferred_username")?.Value;
token.TokenID = office365Id;
token.IssuerID = office365Id;
token.IssueDT = DateTime.Now;
token.ExpireDT = DateTime.Now.AddMinutes(180);
dbContext.T_APIToken.Add(token);
dbContext.SaveChanges();
return RedirectToAction("Index", "Home");
}
else
{
//Error
return RedirectToAction("SignedIn");
}
}

Xamarin, Azure, customauth and passing parameters

I am in the process of rewritting our app using Xamarin.Forms with a C# backend and I'm trying to use customauth on login. I've got it working to a point but am struggling to pass back to the Xamarin app everything I want from the backend. I'm getting the token and user id but want a bit more.
The backend code on succesfull login seems relatively straightforward:
return Ok(GetLoginResult(body));
where GetLoginResult() is:
private object GetLoginResult(IUser body)
{
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
accounts account = db.accounts.Single(u => u.username.Equals(body.username));
return new LoginResult(account)
{
authenticationToken = token.RawData,
};
}
and the LoginResult class is
public class LoginResult
{
public LoginResult(accounts account)
{
Response = 200;
CustomerId = account.CustomerId;
Modules = account.Modules;
User = new LoginResultUser
{
userId = account.id,
UserName = account.UserName,
EmployeeId = account.EmployeeId
};
}
[JsonProperty(PropertyName = "Response")]
public int Response { get; set; }
etc
In the app, I'm calling the customauth as follows:
MobileServiceUser azureUser = await _client.LoginAsync("custom", JObject.FromObject(account));
The result has the token and the correct userid but how can I fill the result with the additional properties passed back by the backend? I've got the backend working and tested using postman and the results I get there are what I want but I've been unable to find out how to get it deserialized in the app.
As I known, for custom auth , MobileServiceClient.LoginAsync would invoke https://{your-app-name}.azurewebsites.net/.auth/login/custom. When using ILSPy you could find that this method would only retrieve the user.userId and authenticationToken from the response to construct the CurrentUser of your MobileServiceClient. Per my understanding, you could leverage MobileServiceClient.InvokeApiAsync to retrieve the additional user info after the user has logged in successfully. Additionally, you could try to follow this toturial for other possible approaches.
UPDATE
You could use InvokeApiAsync instead of LoginAsync to invoke the custom login endpoint directly, then retrieve the response and get the additional parameters as follows:
When logged successfully, I added a new property userName and response the client as follows:
For the client, I added a custom extension method for logging and retrieve the additional parameters as follows:
Here are the code snippet, you could refer to them:
MobileServiceLoginExtend.cs
public static class MobileServiceLoginExtend
{
public static async Task CustomLoginAsync(this MobileServiceClient client, LoginAccount account)
{
var jsonResponse = await client.InvokeApiAsync("/.auth/login/custom", JObject.FromObject(account), HttpMethod.Post, null);
//after successfully logined, construct the MobileServiceUser object with MobileServiceAuthenticationToken
client.CurrentUser = new MobileServiceUser(jsonResponse["user"]["userId"].ToString());
client.CurrentUser.MobileServiceAuthenticationToken = jsonResponse.Value<string>("authenticationToken");
//retrieve custom response parameters
string customUserName = jsonResponse["user"]["userName"].ToString();
}
}
Login processing
MobileServiceClient client = new MobileServiceClient("https://bruce-chen-002-staging.azurewebsites.net/");
var loginAccount = new LoginAccount()
{
username = "brucechen",
password = "123456"
};
await client.CustomLoginAsync(loginAccount);

How can I get ExternalIdentity after all the login process?

I am using MVC 5 and I can successfully login using Google.
I want to have access to the user external identity claims after the login process. I want in a view to access, for example, the claim "picture" from the user. However if I try to run this code it always return null. (except in the login process - auto generated code for mvc template)
Is there a way for me to have access to the external identity claims? (after the login process)
I found how the identity is created. Basically the ExternalSignInAsync makes an internal call to SignInAsync which makes a call to CreateUserIdentityAsync.
I found a class ApplicationSignInManager in the IdentityConfig file and then I changed the CreateUserIdentityAsync method to:
public override async Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var localIdentity = await user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
foreach (var item in externalIdentity.Claims)
{
if (!localIdentity.HasClaim(o => o.Type == item.Type))
localIdentity.AddClaim(item);
}
return localIdentity;
}
So every time I sign in I am going to have my claims + external claims in the loggedin user. From a view I can call:
#HttpContext.Current.GetOwinContext()
.Authentication.User.FindFirst("urn:google:picture").Value
You need to store the auth token and then use that to query the login provider's API for the information you need. Storing it is easy enough:
Startup.Auth.cs
const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string";
...
var googlePlusOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = "yourclientid",
ClientSecret = "yourclientsecret",
Provider = new GoogleOAuth2AuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:googleplus:access_token", context.AccessToken, XmlSchemaString, "Google"));
return Task.FromResult(0);
}
}
};
app.UseGoogleAuthentication(googlePlusOptions);
Then, after you create the new user in ExternalLoginCallback or ExternalLoginConfirm:
await SaveAccessToken(user, identity);
With the following definition for SaveAccessToken (just put it with the other helper methods in the controller):
private async Task SaveAccessToken(User user, ClaimsIdentity identity)
{
var userclaims = await UserManager.GetClaimsAsync(user.Id);
foreach (var at in (
from claims in identity.Claims
where claims.Type.EndsWith("access_token")
select new Claim(claims.Type, claims.Value, claims.ValueType, claims.Issuer)))
{
if (!userclaims.Contains(at))
{
await UserManager.AddClaimAsync(user.Id, at);
}
}
}
Now, you'll have the access token to use later whenever you need it. So, for Google, to get the profile photo, you'd just send a request to https://www.googleapis.com/oauth2/v3/userinfo?access_token=[token], where [token] is the value of the claim you saved.

Windows Phone Linq2Twitter - Getting an authorized user's screen name, id, and name?

I'm a noob in LinQ and having troubles with Linq2Twitter and Twitter API in general.
I cannot understand how to get an authorized user's screen name, id, and name after a successful authorization.
I searched discussion threads online and the only advice I got from Joe was to use async call when querying for results. Except that I don't have the MaterializedAsyncCallback for some reason so I'm using AsyncCallback instead.
Here are the steps that I take all the way from authorizing to attempting to obtain the user's info:
Create a PinAuthorizer with my consumer key and secret as credentials
this.pinAuth = new PinAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET
},
UseCompression = true,
GoToTwitterAuthorization = pageLink =>
Dispatcher.BeginInvoke(() => {
WebBrowser.Navigate(new Uri(pageLink + "&force_login=true", UriKind.Absolute));
})
};
Authorize Begin
this.pinAuth.BeginAuthorize(resp => ...
Enter the pin and thus getting access token and secret in pinAuth.OAuthTwitter:
pinAuth.CompleteAuthorize(
this.pin,
completeResp => Dispatcher.BeginInvoke(() =>
{ ...
Then, I try to get the user ... with an async call as that's what Joe Mayo recommended in other threads.
ITwitterAuthorizer auth = pinAuth;
TwitterContext twitterCtx = new TwitterContext(pinAuth);
(from user in twitterCtx.User
where user.UserID != "JoeMayo"
select user).AsyncCallback(asyncResponse => {
var response = asyncResponse.ToList();
User acc = response.FirstOrDefault();
// Anything in this block is pointless
// as I never get into this async callback block.
// But this is where I expect to get the user's info
// (screen name, name, id, etc.)
});
I never get the async response.
(I also do not have MaterializedAsyncCallback for some reason).
How do I get the authorized user's screen name, id, and name?
You didn't actually fire the query at all!
(from user in twitterCtx.User
where user.UserID != "JoeMayo"
select user).AsyncCallback(users => {
// result is in users variable
var user = users.SingleOrDefault();
if(user != null)
{
// use user here.
}
}).SingleOrDefault();

Facebook .NET SDK: How to authenticate with ASP.NET MVC 2

I am trying to get the grip on the Facebook SDK and at the same time transitioning from ASP.NET forms to MVC (finally). So please bear with me ..
I have created two controller actions:
FBLogon is execetued when the user clicks on the FB login button on the form.
He is then redirected to the FB login page.
Afterwards he gets sent back to the FBAuthorize page, which is supposed to parse the returned url for the access token. I get something like:
http://localhost:5000/account/FBAuthorize#access_token=199143326771791|827213759889396d5408fee6-100001815992604|BmYchAOMqSoZ2L0TYgCrtpoKP3M&expires_in=0
The problem I see, is that as the access_token is passed behind a #, asp.net cannot parse it on the server. Am I doing something fundamentaly wrong?
Code follows:
public ActionResult FBLogon()
{
var settings = ConfigurationManager.GetSection("facebookSettings");
IFacebookApplication current = null;
if (settings != null)
{
current = settings as IFacebookApplication;
if (current.AppId == "{app id}" || current.AppSecret == "{app secret}")
{
return View();
}
}
string[] extendedPermissions = new[] { "publish_stream", "offline_access" };
var oauth = new FacebookOAuthClient { ClientId = current.AppId, RedirectUri = new Uri("http://localhost:5000/account/FBAuthorize") };
var parameters = new Dictionary<string, object>
{
{ "response_type", "token" },
{ "display", "page" }
};
if (extendedPermissions != null && extendedPermissions.Length > 0)
{
var scope = new StringBuilder();
scope.Append(string.Join(",", extendedPermissions));
parameters["scope"] = scope.ToString();
}
var loginUrl = oauth.GetLoginUrl(parameters);
return Redirect(loginUrl.ToString());
}
public ActionResult FBAuthorize()
{
FacebookOAuthResult result;
if (FacebookOAuthResult.TryParse(Request.Url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
Ok. The facebook docs say it quite clearly:
Because the access token is passed in
an URI fragment, only client-side code
(such as JavaScript executing in the
browser or desktop code hosting a web
control) can retrieve the token. App
authentication is handled by verifying
that the redirect_uri is in the same
domain as the Site URL configured in
the Developer App
from http://developers.facebook.com/docs/authentication/ ---> Client-side Flow Section.
So I'm sending the token back to my server to complete the authentication..
Update:
The sending back to the server I do using Javascript something like this:
var appId = "<%: Facebook.FacebookContext.Current.AppId %>";
if (window.location.hash.length > 0) {
accessToken = window.location.hash.substring(1);
var url = window.location.href.replace(/#/, '?');
window.location = url;
}
On the server then I have the following action. Not very nice but it works..
public ActionResult FBAuthorize()
{
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
/// hack to make FacebookOuthResult accept the token..
url = url.Replace("FBAuthorize?", "FBAuthorize#");
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string[] extendedPermissions = new[] { "user_about_me", "offline_access" };
var fb = new FacebookClient(result.AccessToken);
dynamic resultGet = fb.Get("/me");
var name = resultGet.name;
RegisterModel rm = new Models.RegisterModel();
rm.UserName = name;
rm.Password = "something";
rm.Email = "somethig";
rm.ConfirmPassword = "23213";
//Label1.Text = name;
//Response.Write(name);
//return RedirectToAction("register", "Account", rm);
ViewData["Register"] = rm;
return RedirectToAction("Register");
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
I found this post http://facebooksdk.codeplex.com/discussions/244568 on codeplex. I think this is what you need.
Note that instead of using the client-side flow, you need to use the server-side flow.
This is what you should do
Create a login link for server-side flow. After Authorization, facebook will return an url containing a code instead of a access token.
Then you request for a token from facebook using the code. this is my example
public ActionResult FBAuthorize()
{
FacebookOAuthClient cl = new FacebookOAuthClient(FacebookContext.Current);
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
// verify that there is a code in the url
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string code = result.Code;
// this line is necessary till they fix a bug *see details below
cl.RedirectUri = new UriBuilder("http://localhost:5000/account/FBAuthorize").Uri;
var parameters = new Dictionary<string, object>();
//parameters.Add("permissions", "offline_access");
Dictionary<String, Object> dict = (Dictionary<String, Object>)cl.ExchangeCodeForAccessToken(code, new Dictionary<string, object> { { "redirect_uri", "http://localhost:5000/account/FBAuthorize" } });
Object Token = dict.Values.ElementAt(0);
TempData["accessToken"] = Token.ToString();
return RedirectToAction ("ShowUser");
}
else
{
var errorDescription = result.ErrorDescription;
}
}
else
{
// TODO: handle error
}
return View();
}
*There is bug when using IIS in localhost, see the original post for details (the redirect uri when asking for the token must be the same as the one used asking for the code)
It is highly recommended to use IIS and not visual studio web server. There are many things that wont work in visual studio web server.
I am in the same spot you are at the moment.
We never get the Request.QueryString populated becasue of the "fragment" or # in the url.
Love to know if you solved this and how.
It does not look like the FacebookOAuthResult class was written to be used in web applications of any sort.
you can change the response type in you scope paramas to be "code" then it will send back a code in the querystring in which you can swap for a token.

Categories

Resources