How to check logged in facebook user in ASP.NET MVC 4? - c#

I want to check if a user is already logged in with facebook in ASP.NET MVC4. I'm using Facebook C# SDK.
I have this code for getting the access_token from facebook:
public ActionResult FacebookCallback(string code)
{
var fb = new FacebookClient();
dynamic result = fb.Post("oauth/access_token", new
{
client_id = "XXXXXXXXXXXXXX",
client_secret = "XXXXXXXXXXXXXXXXXXX",
redirect_uri = RedirectUri.AbsoluteUri,
code = code
});
var accessToken = result.access_token;
Session["AccessToken"] = accessToken;
fb.AccessToken = accessToken;
dynamic me = fb.Get("me?fields=first_name,last_name,id,email");
string email = me.email;
FormsAuthentication.SetAuthCookie(email, false);
return RedirectToAction("Index", "Home");
}
I don't know how to check if user is already logged in. Please help.

The most easiest way in my opinion if your willing to checkout Facebooks javascript SDK, then there is a FB.getLoginStatus method which will give you the info you need.
Taken from facebook's dev site:
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
// the user isn't logged in to Facebook.
}
});
There is also the user_online_presence extended permission here. Probably not what you want, but using FQL, you can request the online_presence field here, but note this only returns if the user is logged into facebook user chat.

Related

Implementing External Authentication for Mobile App in ASP.NET WebApi 2

I'm trying to build an API (using ASP.NET WebApi) that will be consumed by a native mobile app for a school project. (I'm not concerned about/developing the mobile app, this responsibility falls on a different member)
I'm at a point where I need to implement a token based Facebook login. There are a lot of tutorials available for how to implement this feature for browser based apps (this is pretty straight forward and most of it comes inbuilt), but I don't think I follow how this would work with native apps. What I don't understand is how the redirects would work?
According to this link, nothing needs to be handled specifically by my server. And I don't think I understand how this would work? How would the tokens from Facebook be handled?
Also, what part of token handling should I implement, I couldn't really find good documentation for WebApi external login authentication.
Anyway, if someone could point me to the exact flow of token exchanges that happen and what is implemented by default by ASP.NET, that would be super helpful.
Also, the biggest point of confusion for me is I don't understand how the token returned by Facebook will be handled.
I assume the token will be returned to the client (mobile app), how do I get access to it on my server?
How do I create a local token from facebook's token?
Is this all done internally/auto-magically by ASP.NET?
I'm sorry if this is something I should've been able to figure out. I did do quite a bit of research and I found myself drowning in (related & unrelated) information. I don't think I even know how to search for the information I need.
Some links I've read:
Claims And Token Based Authentication (ASP.NET Web API)
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app
I had to do pretty much the same thing for an application I was working on. I also had a lot of trouble finding information about it. It seemed like everything I found was close to what I needed, but not exactly the solution. I ended up taking bits and pieces from a bunch of different blog posts, articles, etc. and putting them all together to get it to work.
I remember two of the links you posted "Claims and Token Based Authentication" and "ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app" as being ones that had useful information.
I can't give you a comprehensive answer since I don't remember everything I had to do, nor did I even understand everything I was doing at the time, but I can give you the general idea. You are on the right track.
Essentially I ended up using the token granted by Facebook to confirm that they were logged into their Facebook account, created a user based on their Facebook user ID, and granted them my own bearer token that they could use to access my API.
The flow looks something like this:
Client authenticates with Facebook via whatever method (we used oauth.io)
Facebook returns them a token
Client sends token information to the registration endpoint of my WebApi controller
The token is validated using Facebook's Graph API, which returns user info
A user is created in the database via ASP.NET Identity with their Facebook user ID as the key
Client sends token information to the authentication endpoint of my WebApi controller
The token is validated using Facebook's Graph API, which returns user info
The user info is used to look up the user in the database, confirm they have previously registered
ASP.NET Identity is used to generate a new token for that user
That token is returned to the client
Client includes an Authorization header in all future HTTP requests with the new token granted by my service (ex. "Authorization: Bearer TOKEN")
If the WebApi endpoint has the [Authorize] attribute, ASP.NET Identity will automatically validate the bearer token and refuse access if it is not valid
There ended up being a lot of custom code for implementing the OAuth stuff with ASP.NET Identity, and those links you included show you some of that. Hopefully this information will help you a little bit, sorry I couldn't help more.
I followed this article. The flow is basically this
The server has the facebook keys just like with web login
The app asks for available social logins and displays buttons (you can hardcode this I guess)
When a button is pressed the app opens a browser and sets the URL to the one related to the specified social login. The ASP.NET then redirects the browser to facebook/google/whatever with the appropriate Challenge
The user might be logged in or not and might have given permission to your app or not. After he gives the permissions facebook redirects back to the provided callback URL
At that point you can get the external login info from the SignInManager and check if the user already exists and if you should create a new account
Finally a token is generated and the browser is redirected to a URL in which the token is placed. The app gets the token from the URL and closes the browser. Uses the token to proceed with API requests.
Honestly I have no idea if this approach is legit...
The code of the action buttons should redirect to:
public async Task<IEnumerable<ExternalLoginDto>> GetExternalLogins(string returnUrl, bool generateState = false)
{
IEnumerable<AuthenticationScheme> loginProviders = await SignInManager.GetExternalAuthenticationSchemesAsync();
var logins = new List<ExternalLoginDto>();
string state;
if (generateState)
{
const int strengthInBits = 256;
state = RandomOAuthStateGenerator.Generate(strengthInBits);
}
else
{
state = null;
}
foreach (AuthenticationScheme authenticationScheme in loginProviders)
{
var routeValues = new
{
provider = authenticationScheme.Name,
response_type = "token",
client_id = Configuration["Jwt:Issuer"],
redirect_uri = $"{Request.Scheme}//{Request.Host}{returnUrl}",
state = state
};
var login = new ExternalLoginDto
{
Name = authenticationScheme.DisplayName,
Url = Url.RouteUrl("ExternalLogin", routeValues),
State = state
};
logins.Add(login);
}
return logins;
}
The code for the callback action:
[Authorize(AuthenticationSchemes = "Identity.External")]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IActionResult> GetExternalLogin(string provider, string state = null, string client_id = null, string error = null)
{
if (error != null)
{
ThrowBadRequest(error);
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider);
}
string providerKey = User.FindFirstValue(ClaimTypes.NameIdentifier);
var externalLoginInfo = new ExternalLoginInfo(User, User.Identity.AuthenticationType, providerKey, User.Identity.AuthenticationType);
if (externalLoginInfo.LoginProvider != provider)
{
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
return new ChallengeResult(provider);
}
var userLoginInfo = new UserLoginInfo(externalLoginInfo.LoginProvider, externalLoginInfo.ProviderKey, externalLoginInfo.ProviderDisplayName);
User user = await UserManager.FindByLoginAsync(externalLoginInfo.LoginProvider, externalLoginInfo.ProviderKey);
if (client_id != Configuration["Jwt:Issuer"])
{
return Redirect($"/#error=invalid_client_id_{client_id}");
}
if (user != null)
{
return await LoginWithLocalUser(user, state);
}
else
{
string email = null;
string firstName = null;
string lastName = null;
IEnumerable<Claim> claims = externalLoginInfo.Principal.Claims;
if (externalLoginInfo.LoginProvider == "Google")
{
email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
firstName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
lastName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
}
else if (externalLoginInfo.LoginProvider == "Facebook")
{
email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
string[] nameParts = claims.First(c => c.Type == ClaimTypes.Name)?.Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
firstName = nameParts?.First();
lastName = nameParts?.Last();
}
//some fallback just in case
firstName ??= externalLoginInfo.Principal.Identity.Name;
lastName ??= externalLoginInfo.Principal.Identity.Name;
user = new User
{
UserName = email,
Email = email,
FirstName = firstName,
LastName = lastName,
EmailConfirmed = true //if the user logs in with Facebook consider the e-mail confirmed
};
IdentityResult userCreationResult = await UserManager.CreateAsync(user);
if (userCreationResult.Succeeded)
{
userCreationResult = await UserManager.AddLoginAsync(user, userLoginInfo);
if (userCreationResult.Succeeded)
{
return await LoginWithLocalUser(user, state);
}
}
string identityErrrors = String.Join(" ", userCreationResult.Errors.Select(ie => ie.Description));
Logger.LogWarning($"Error registering user with external login. Email:{email}, Errors:" + Environment.NewLine + identityErrrors);
return Redirect($"/#error={identityErrrors}");
}
}
private async Task<RedirectResult> LoginWithLocalUser(User user, string state)
{
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
DateTime expirationDate = DateTime.UtcNow.AddDays(365);
string token = user.GenerateJwtToken(Configuration["Jwt:Key"], Configuration["Jwt:Issuer"], expirationDate);
return Redirect($"/#access_token={token}&token_type=bearer&expires_in={(int)(expirationDate - DateTime.UtcNow).TotalSeconds}&state={state}");
}

Token-based Authorization in Existing ASP.NET MVC App

I have inherited an existing application. This application uses ASP.NET MVC 3. It has some APIs. Those APIs look like the following:
[AcceptVerbs(HttpVerbs.Post)]
[Endpoint]
public ActionResult AuthenticatePlayer(string username, string password)
{
// Ensure that the user entered valid credentials
if (Membership.ValidateUser(username, password) == false)
return Json(new { statusCode = StatusCodes.INVALID_CREDENTIALS, message = "You entered an invalid username or password. Please try again." });
// Get the profile of the person that just logged in.
ProfileCommon userProfile = (ProfileCommon)(ProfileCommon.Create(username));
if (userProfile != null)
{
string name = username;
if (String.IsNullOrEmpty(userProfile.FirstName) == false)
name = userProfile.FirstName;
return Json(new {
statusCode = StatusCodes.SUCCESS,
payload = name,
username = username.ToLower(),
});
}
}
[AcceptVerbs(HttpVerbs.Get)]
[Endpoint]
public ActionResult SomeUserAction(string q)
{
// TODO: Ensure the user is authorized to perform this action via a token
// Do something
return Json(new { original = q, response = DateTime.UtcNow.Millisecond }, JsonRequestBehavior.AllowGet);
}
I'm trying to figure out how to integrate a token-based authorization schema into this process. From my understanding, a token-based system would return a short-lived token and a refresh token to a user if they successfully login. Then, each method can check to see if a user is authorized to perform the action by looking at the token. I'm trying to learn if this is built-in to ASP.NET MVC or if there is a library I can use. I need to figure out the shortest way to get this done.
Thank you so much!
I've built a WebAPI Token Authentication library a year ago, providing Token based authentication:
WebAPI Token Auth Bootstrap is out of the box Token based User Auth for WebAPI applications, Provides ready to use 'TokenAuthorize'
Attribute and 'TokenAuthApiController' Controller.
Among its features - Token Based User Authentication User Property inside the
TokenAuthApiController (Id, Username, Role, LastAccess).
Token Based User Authorization TokenAuthorizeAttribute with Access
Level - Public, User, Admin or Anonymous.
Built-in Functionality Login(), Logoff(), Error(), Unauthorized()
Responses with various overloads.
You can read more about here and in its own wiki in GitHub.
Nowadays I am working on a Node.js application and I am using Json Web Tokens (JWT) using Node.js library and it is very easy and straightforward.. its Node.js after all ;)
I saw there is a .NET implementation of JWT explained on this article which I recommend you to look at.
You can use Owin ... i.e. Microsoft.owin.security
I haven't tried this implementation but this is just to give you an idea:
var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Json(new {
statusCode = StatusCodes.SUCCESS,
payload = name,
username = username.ToLower(),
accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket)
});

Posting to FB Page error(s)

Scenario: I want to get user access token of the fb page admin by JS login and retrieving token ONE TIME, and will store that in database. Then daily, I want to do wall post to those page.
I am using JS to get the initial token and storing it. Then using c# FacebookSDK for the web requests.
FB.login(function (response) {
var r = response;
// get access token of the user and update in database
$("#FacebookAccessToken").val(response.authResponse.accessToken);
},
{
scope: 'manage_pages,publish_stream'
});
Now I am saving this token in database as I will be using this for future graph calls - is this right?
On server side when I need to do a post after a day I retrieve that token and do the processing as below:
// step 1 get application access token
var fb1 = new FacebookClient();
dynamic appTokenCLient = fb1.Get("oauth/access_token", new
{
client_id = appId,
client_secret = appSecret,
grant_type = "client_credentials",
scope = "manage_pages,publish_stream",
redirect_uri = siteUrl
});
var fbTokenSettingVal = GetTokenFromDB(); // getting access token from database which was stored during JS fb login
// step 2 extend token
var fb2 = new FacebookClient(appTokenCLient.access_token);
dynamic extendedToken = fb2.Get("oauth/access_token", new
{
client_id = appId,
client_secret = appSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = fbTokenSettingVal.Val
});
var userExtendedToken = extendedToken.access_token; // get extended token and update database
// step 3 get page access token from the pages, that the user manages
var fb3 = new FacebookClient { AppId = appId, AppSecret = appSecret, AccessToken = userExtendedToken };
var fbParams = new Dictionary<string, object>();
var publishedResponse = fb3.Get("/me/accounts", fbParams) as JsonObject;
var data = JArray.Parse(publishedResponse["data"].ToString());
var pageToken = "";
foreach (var account in data)
{
if (account["name"].ToString().ToLower().Equals("PAGE_NAME"))
{
pageToken = account["access_token"].ToString();
break;
}
}
// step 4 post a link to the page - throws error !
var fb4 = new FacebookClient(pageToken);
fb4.Post("/PAGE_NAME/feed",
new
{
link = "http://www.stackoverflow.com"
});
The last 4th step throws error, when posting to selected page:
The user hasn't authorized the application to perform this action
Have tried several different ways, but in vain. Assume that there is just a simple step which I am doing wrong here.
Also, is it ok to extend the fb access token for user every time before making request?
Any way to check if token is expired or not?
If you want to use that access token for future. You need to take offline_access token and that token you need to store in database.
This offline accesstoken will be expire once user will change the password or delete your application from facebook account.
private void GetPermenentAccessTokenOfUser(string accessToken)
{
var client2 = new FacebookClient(accessToken);
//get permenent access token
dynamic result = client2.Post("oauth/access_token", new
{
client_id = _apiKey,
client_secret = _apiSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = accessToken
});
_accessToken = result.access_token;
}
Looks like for new apps we need to apply for manage_pages permission from our application:
which I am doing now. As it shows error when doing login:
Also, the app needs to be live, so they can reproduce this permission and verify that we do need this permission to post to pages. Its good for fb users safety but a time taking process for developers.
Any way this can be skipped for testing purpose?

Get access token to facebook page - WPF

I am developing a WPF application that needs post on wall of a facebook's Page, and this without login window.
Well, I want to get access token for my facebook page, and this is my code.
var fb = new FacebookClient();
string token = "";
dynamic accounts = fb.Get("/"<USER_ID>"/accounts");
foreach (dynamic account in accounts)
{
if (account.id == <PAGE_ID>)
{
token = account.access_token;
break;
}
}
But I receive a error #104. It is a simple error, that I need a access token to do this operation.
Then I use other code to get the user access token
var fb = new FacebookClient();
dynamic result = fb.Get("oauth/access_token", new
{
client_id = <PAGE_ID>,
client_secret = <APP_SECRET>,
grant_type = "fb_exchange_token",
fb_exchange_token = <USER_TOKEN>
});
But I get error #101:
"Error validating application. Cannot get application info due to a system error."
Someone knows what I have to do?
Thanks!!!
I'm not sure if you've been able to get a never expiring token for the page, so I'll explain you the steps:
Open Graph API Explorer
Select your app from the drop-down
Click "Get Access Token" button, and select the manage_pages permission.
Copy the token and run this in the browser:
https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={app-id}&client_secret={app-secret}&fb_exchange_token={step-3-token}
Copy the token from step-4 and paste in to the access_token field and call:
/{page-id}?fields=access_token
The token you get now is a never-expiring token, you can validate the same in Debugger .Use this in your app.
But beware, its not recommended to use this token on client side if your app is public.
If you use the fb_exchange_token call, it will give you a token that expires after 60 days. In order to make it work correctly, I had to go through the login web flow to guarantee I got an up-to-date page access token.
Go to the Facebook App dashboard
If you haven't already added the Facebook Login product, click "+ Add Product" and select Facebook Login
Enable the "embedded browser control" option and enter https://www.facebook.com/connect/login_success.html as the allowed redirect URL.
Make a Window with a WebView control on it. The WebBrowser control no longer works; the browser engine powering it is too old.
Add code to listen for the navigation to the success URL:
this.webView.NavigationCompleted += (sender, args) =>
{
if (args.Uri.AbsolutePath == "/connect/login_success.html")
{
if (args.Uri.Query.Contains("error"))
{
MessageBox.Show("Error logging in.");
}
else
{
string fragment = args.Uri.Fragment;
var collection = HttpUtility.ParseQueryString(fragment.Substring(1));
string token = collection["access_token"];
// Save the token somewhere to give back to your code
}
this.Close();
}
};
Add code to navigate to the facebook login URL:
string returnUrl = WebUtility.UrlEncode("https://www.facebook.com/connect/login_success.html");
this.webView.Source = new Uri($"https://www.facebook.com/dialog/oauth?client_id={appId}&redirect_uri={returnUrl}&response_type=token%2Cgranted_scopes&scope=manage_pages&display=popup");
Call window.ShowDialog() to pop up the login window, then grab the user access token.
Create some models to help you out:
public class AccountsResult
{
public List<Account> data { get; set; }
}
public class Account
{
public string access_token { get; set; }
public string id { get; set; }
}
Using the user access token, get the page access token:
FacebookClient userFacebookClient = new FacebookClient(userAccessToken);
var accountsResult = await userFacebookClient.GetTaskAsync<AccountsResult>("/me/accounts");
string pageAccessToken = accountsResult.data.FirstOrDefault(account => account.id == PageId)?.access_token;
if (pageAccessToken == null)
{
MessageBox.Show("Could not find page under user accounts.");
}
else
{
FacebookClient pageFacebookClient = new FacebookClient(pageAccessToken);
// Use pageFacebookClient here
}

Check User Tokens are valid or not using Twitter Api

I have a Asp.Net application that will post tweets in twitter.
I'm using Twitterizer2 for doing this.
First time when the user uses the application, he will be redirected to twitter for authentication.
And then the user-token will be stored in my application , so that the user will never be asked again to login to twitter.
This is working fine.
Now i want to validate the user-tokens before posting (ie valid token or not) . Is there any way to do this validation?
You can make a call to the Verify Credentials API
Make an authenticated call to
https://api.twitter.com/1/account/verify_credentials.json
It will respond with HTTP 200 OK if the tokens are correct - or 401 if they are not.
MoH's code didn't work for me. Here's what I did:
public bool IsTwitterAccessTokenValid(String access_token, String token_secret)
{
var token = new Twitterizer.OAuthTokens();
token.ConsumerKey = this.TwitterConsumerKey;
token.ConsumerSecret = this.TwitterConsumerSecret;
token.AccessToken = access_token;
token.AccessTokenSecret = token_secret;
var twitterResponse = TwitterAccount.VerifyCredentials(token);
return (twitterResponse.Result == RequestResult.Success);
}
I found out the code for validating tokens in another question.
The Twitterizer Api itself had the Methods to validate the User tokens.
The code is as follows:
Twitterizer.OAuthTokens token = new Twitterizer.OAuthTokens();
token.ConsumerKey = this.AppId;
token.ConsumerSecret = this.AppSecret;
token.AccessToken = userToken;
token.AccessTokenSecret = userSecret;
Twitterizer.TwitterResponse<Twitterizer.TwitterUser> response =
Twitterizer.TwitterAccount.VerifyCredentials(token);
if (String.IsNullOrEmpty(response.ErrorMessage))
{
//This is a valid token
}
else
{
//Invalid token
}

Categories

Resources