Facebook login stuck in permissions page in dispaly=touch mode- windows 8 - facebook

I was trying to implement Facebook login in windows 8 application . I am using following url to request Facebook login
https://www.facebook.com/dialog/oauth?client_id=APP_ID&response_type=token&scope=email%2coffline_access%2cpublish_stream&redirect_uri=http%3a%2f%2fwww.facebook.com%2fconnect%2flogin_success.html&display=touch
I am using webview to request this url .I am getting the login page correctly shown below.
After user enter login credential it is getting redirecting to another page and getting stuck there.I guess that page is supposed to be given permissions.I am attaching a screen shown below
Nothing will happen if i click on cancel or install ..
if i am removing display=touch from the request everything will works fine.But the login poge and permissions page are displayed like in web browser.That is not optimized for touch ..
I have tested the same with webbrowser control in wpf..But the problem is still exisiting there. Any ideas?

On Windows 8 You should be using WebAuthenticationBroker.
Here is code example:
private async void Authenticate()
{
//Facebook Authentication Uri
var facebookUri = "https://www.facebook.com/dialog/oauth";
//Standard redirect uri for desktop/non-web based apps
var redirectUri = "https://www.facebook.com/connect/login_success.html";
//Place your appa client id here
var clientId = "";
//The type of token that can be requested
var responseType = "token";
//Response pattern
var pattern = string.Format("{0}#access_token={1}&expires_in={2}", redirectUri, "(?.+)", "(?.+)");
try
{
String FacebookURL = "https://www.facebook.com/dialog/oauth?client_id=" +
clientId + "&redirect_uri=" + Uri.EscapeUriString(redirectUri) + "&scope=read_stream&display=touch&response_type=token";
System.Uri StartUri = new Uri(FacebookURL);
System.Uri EndUri = new Uri(redirectUri);
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
StartUri,
EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var response = WebAuthenticationResult.ResponseData.ToString();
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
//Handle HTTP error
}
else
{
//Handle error
}
}
catch (Exception ex)
{
//Handle error
}
}

Related

Identity server stuck in redirect loop

I'm using identityserver4 for single sign-on. For most of the time application function smoothly but intermittently we face a redirect loop issue which becomes a show stopper for us until we restart's our app service. The page goes on loading continuously before finally showing a 'Bad request - Request Too Long' page with message: HTTP Error 400. The size of the request headers is too long. If we check the network tab, we can see that the application is looping between the identity server and client application redirect sign in pages. The application insight tells us that the client app gives a 401 on his home/index page and then a 302 on the signin-oidc url, then goes to the identity server connect/token, then connect/userinfo endpoints to get claims and comes back to the client home/index page to again get a 401. The loop continues (Identity server says user is authenticated while client says it is not). We are unable to find a fix for this since long. Any help is appreciated. Attaching the client side configuration for reference.
Findings
Our client app is an mvc app & we have used Session's & TempData in few area's. This areas are the triggering point of the redirect issue. What we have observed is, when the client initially login the authentication cookie is created (Cookie Name: AudDiscoveryAuth) and I could see it being passed in header for each request made to the controller actions. But once the user visit's any such area where we have used Session/TempData and Log out or any other user tries to login, Identity server successfully authenticates the user also the userendpoint to retrieve the details is being invoked however the cookie itself is not being created and is missing in every request to the Index/Home action method hence the redirect loop. Wondering what could be hampering in issuing cookie when using session variable elsewhere in the application or is their a setting missing.
Also in every redirect the occurrence of OpenIdConnect.nonce.XXX cookie is incremented. Once the count of OpenIdConnect.nonce.XXX reaches more then a certain level we get the bad request error page
public void Configuration(IAppBuilder app)
{
string baseClientAddress = ConfigurationManager.AppSettings["ApplicationUrl"];
int slidingExpiryHrs = Convert.ToInt32(ConfigurationManager.AppSettings["SlidingExpiryHrs"]);
int slidingExpiryMins = Convert.ToInt32(ConfigurationManager.AppSettings["SlidingExpiryMins"]);
TimeSpan expireTimeSpan = new TimeSpan(slidingExpiryHrs, slidingExpiryMins, 0);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType,
CookieName = "AudDiscoveryAuth",
ExpireTimeSpan = expireTimeSpan,
SlidingExpiration = true
});
app.UseOpenIdConnectAuthenticationPatched(new OpenIdConnectAuthenticationOptions
{
ClientId = "ratingspro.web",
Authority = IdsvrConstants.BaseAddress,
RedirectUri = baseClientAddress + "signin-oidc/",
PostLogoutRedirectUri = baseClientAddress + "signout-callback-oidc/",
ResponseType = "code id_token",
Scope = "openid api1 ratingspro.webapi offline_access",
UseTokenLifetime = false,
SignInAsAuthenticationType = DefaultAuthenticationType,
RequireHttpsMetadata = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = HttpClientFactory.Create();
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = IdsvrConstants.TokenEndpoint,
ClientId = "ratingspro.web",
ClientSecret = "secret",
Code = n.Code,
RedirectUri = n.RedirectUri,
});
if (tokenResponse.IsError)
{
LogHelper.LogMessage("RatingsproApp: Startup => tokenResponseError: " + tokenResponse.Error);
throw new AuthenticationException(tokenResponse.Error);
}
var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = IdsvrConstants.UserInfoEndpoint,
Token = tokenResponse.AccessToken
});
if (userInfoResponse.IsError)
{
throw new AuthenticationException(userInfoResponse.Error);
}
var claims = userInfoResponse.Claims;
if (claims.Any(c => c.Type == "ApplicationAccessDenied"))
{
throw new AuthenticationException(claims.FirstOrDefault(c => c.Type == "ApplicationAccessDenied").Value);
}
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(claims);
id.AddClaim(new Claim("AccessToken", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
client.Dispose();
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
return Task.FromResult(0);
},
AuthenticationFailed = x =>
{
x.ProtocolMessage.RedirectUri = "/";
return Task.CompletedTask;
}
}
});
}
}

No 'Access-Control-Allow-Origin', only errors on first call but works subsequently

I have an AngularJS app which is trying to auth with my Web Api. I receive the below error during the first call to my server if the user does not exist in my database, but does not happen on subsequent calls to the same method once the user exists in my db. (relevant code at the bottom)
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:1378' is therefore not allowed access. The response had HTTP status code 500.
The flow of the logic is:
AngularJS auths with Facebook when the user clicks login
App does an $http.post to my server for auth/login passing their credentials
Server polls Facebook API for user details
If user exists, update their profile and auth 'em
Else, create new membership user, update with FB details, and auth 'em
The only thing that's different if they don't exist in the database (which is when the defect occurs) is that the login method asynchronously calls a createUser method then returns data. No additional external calls are made.
API startup method enabling CORS:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var cors = new EnableCorsAttribute("*","*","*");
config.EnableCors(cors);
ConfigureOAuth(app);
app_start.WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
API Controller:
[Route("Login")]
[HttpPost]
[AllowAnonymous]
public async Task<FacebookUserModel> Login(FacebookUserRequest user)
{
FacebookUserModel fbUser = new FacebookUserModel();
// Build FacebookUser object
try {
// Grab basic user details
string profileRequestUri = "https://graph.facebook.com/" + user.fbID + "?access_token=" + user.access_token;
HttpWebRequest profileRequest = (HttpWebRequest)WebRequest.Create(profileRequestUri);
profileRequest.Method = WebRequestMethods.Http.Get;
profileRequest.Accept = "application/json";
HttpWebResponse profileResponse = (HttpWebResponse)profileRequest.GetResponse();
Stream profileResponseStream = profileResponse.GetResponseStream();
StreamReader profileStreamReader = new StreamReader(profileResponseStream);
fbUser = JsonConvert.DeserializeObject<FacebookUserModel>(profileStreamReader.ReadToEnd());
} catch (Exception) ...
try {
// Grab profile picture
string pictureRequestUri = "https://graph.facebook.com/" + user.fbID + "/picture";
HttpWebRequest pictureRequest = (HttpWebRequest)WebRequest.Create(pictureRequestUri);
pictureRequest.Method = WebRequestMethods.Http.Get;
HttpWebResponse pictureResponse = (HttpWebResponse)pictureRequest.GetResponse();
fbUser.profilePictureUri = pictureResponse.ResponseUri.ToString();
} catch (Exception) ...
// If user exists, change password to new token and return)
if(userExists)
{
try {
IdentityUser identityUser = _repo.FindUser(ID, pass).Result;
FacebookUserModel dbUser = db.FacebookUserObjects.First(u => u.identityUserID == identityUser.Id);
db.Entry(dbUser).CurrentValues.SetValues(fbUser);
db.SaveChangesAsync();
fbUser.identityUserID = identityUser.Id;
return fbUser;
}
catch (Exception e)
{ return null; }
}
// Else, create the new user using same scheme
else
{
UserModel newUser = new UserModel
{
UserName = ID,
Password = pass,
ConfirmPassword = pass
};
// Create user in Identity & linked Facebook record
createUser(newUser, fbUser);
return fbUser;
}
}
private async void createUser(UserModel newUser, FacebookUserModel fbUser)
{
IdentityResult result = await _repo.RegisterUser(newUser);
var identityUser = await _repo.FindUser(newUser.UserName, newUser.Password);
fbUser.identityUserID = identityUser.Id;
db.FacebookUserObjects.Add(fbUser);
db.SaveChangesAsync();
}
AngularJS calls to my server:
var _login = function (fbID, fbToken) {
$http.post(serviceBase + 'auth/login', { "fbID": fbID, "access_token": fbToken }).then(function (response) {
var data = "grant_type=password&username=" + fbID + "&password=" + pass;
$http.post(serviceBase + 'auth/token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
.success(function (tokenResponse) {
authServiceFactory.bearerToken = tokenResponse.access_token;
})
.error(function (err) {
console.log("token error:", err);
});
authServiceFactory.userObject = response.data;
window.localStorage['userObject'] = JSON.stringify(authServiceFactory.userObject);
})
};
Why would I get the No 'Access-Control-Allow-Origin' error only on the first call, but not subsequent ones?
Update
I have a workaround in place that works, but I don't really like. The issue only arose when calling a second method from my login controller, so if I moved that code up into the login controller instead of a secondary method it works without the CORS error. This really bothers me though and is inefficient, I'd love to know a better way around it.
if you're working with angularjs you might want to check out satellizer. It makes the auth process really simple and has some awesome built in window popup control.
As far as the Access-Control-Allow-Origin calls it could be happening because you explicitly set headers on the one call and the other ones are falling back to the default http provider? Check out $http and see if providing those defaults might work around it.

Facebook OAuthException - This authorization code has expired. [code=100]

I have setup the Facebook API SDK for ColdFusion - https://github.com/affinitiz/facebook-cf-sdk
I've setup a login process which works successfully but after 10 minutes or so when I return and refresh the page, it shows the following error:
This authorization code has expired. [code=100]
Is there something I am missing with the FB login? Am I meant to be checking against something manually in order to persist the session?
Cheers
Shaun
<cfscript>
import facebook.sdk.FacebookApp;
import facebook.sdk.FacebookGraphAPI;
// Replace this with your appId and secret
APP_ID = "";
SECRET_KEY = "";
SCOPE = "publish_stream";
if (APP_ID is "" or SECRET_KEY is "") {
// App not configured
facebookGraphAPI = new FacebookGraphAPI();
} else {
// Create facebookApp instance
facebookApp = new FacebookApp(appId=APP_ID, secretKey=SECRET_KEY);
// See if there is a user from a cookie or session
userId = facebookApp.getUserId();
if (userId) {
try {
userAccessToken = facebookApp.getUserAccessToken();
facebookGraphAPI = new FacebookGraphAPI(accessToken=userAccessToken, appId=APP_ID);
userObject = facebookGraphAPI.getObject(id=userId);
userFriends = facebookGraphAPI.getConnections(id=userId, type='friends', limit=10);
authenticated = true;
} catch (any exception) {
// Usually an invalid session (OAuthInvalidTokenException), for example if the user logged out from facebook.com
// this is where the error occurs
userId = 0;
facebookGraphAPI = new FacebookGraphAPI();
}
} else {
facebookGraphAPI = new FacebookGraphAPI();
}
// Login or logout url will be needed depending on current user state.
if (userId) {
logoutUrl = facebookApp.getLogoutUrl();
} else {
parameters = {scope=SCOPE};
loginUrl = facebookApp.getLoginUrl(parameters);
}
}
Apparently you're not exchanging the OAuth code to an Access Token. See the docs here how to do that: Manually Build a Login Flow

How can i post the feed to facebook page as admin using c# sdk?

I want to update the facebookpage using c# sdk. I have partially successful with this, the problem is whenever I post messages to the page, post is visible only for admin(i am the admin of the page)is logged In. I want the post or feed to be visible to every one who visit the page.
(even admin is logged out post's are not visible to admin also)
The following code i am trying to achieve
public ActionResult FacebookPagePost()
{
string app_id = "xxxx";
string app_secret = "xxx";
string scope = "publish_stream,manage_pages";
string page_Id = "xxX";
if (Request["code"] == null)
{
return Redirect(string.Format(
"https://graph.facebook.com/oauth/authorize?client_id={0}&redirect_uri={1}&scope={2}",
app_id, Request.Url.AbsoluteUri, scope));
}
else
{
try
{
Dictionary<string, string> tokens = new Dictionary<string, string>();
string url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&scope={2}&code={3}&client_secret={4}",
app_id, Request.Url.AbsoluteUri, scope, Request["code"].ToString(), app_secret);
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string vals = reader.ReadToEnd();
foreach (string token in vals.Split('&'))
{
tokens.Add(token.Substring(0, token.IndexOf("=")),
token.Substring(token.IndexOf("=") + 1, token.Length - token.IndexOf("=") - 1));
}
}
string access_token = tokens["access_token"];
var client = new FacebookClient(access_token);
dynamic fbAccounts = client.Get("/me/accounts");
dynamic messagePost = new ExpandoObject();
messagePost.picture = "http://pic.com/pic.png";
messagePost.link = "http://www.examplearticle.com";
messagePost.name = "name goes here";
messagePost.description = "description goes here";
//Loop over the accounts looking for the ID that matches your destination ID (Fan Page ID)
foreach (dynamic account in fbAccounts.data) {
if (account.id == page_Id)
{
//When you find it, grab the associated access token and put it in the Dictionary to pass in the FB Post, then break out.
messagePost.access_token = account.access_token;
break;
}
}
client.Post("/" + page_Id + "/feed", messagePost);
}
catch (FacebookOAuthException ex)
{
}
catch (Exception e)
{
}
}
}
1) Create a Facebook App at: developers.facebook.com and get yourself an APPID and APPSECRET. (there are a lot of tutorials online for doing this so I will skip repeating it)
2) Go to: http://developers.facebook.com/tools/explorer and choose your app from the dropdown and click "generate access token".
3) After that do the following steps here:
https://stackoverflow.com/questions/17197970/facebook-permanent-page-access-token to get yourself a permanent page token.
(I can not stress this enough, follow the steps carefully and thoroughly)*
*I have tool I built that does this for me, all I enter is the APPID, APPSECRET and ACCESSTOKEN which the tool then generates a permanent page token for me. Anyone is welcomed to use it and help make it better,
https://github.com/devfunkd/facebookpagetokengenerator
=========================================================================
Ok at this point you should have your APPID, APPSECRET and a PERMANENT PAGE TOKEN.
=========================================================================
In your Visual Studio solution:
4) Using Nuget:Install-Package Facebook
5) Implement the Facebook client:
public void PostMessage(string message)
{
try
{
var fb = new FacebookClient
{
AppId = ConfigurationManager.AppSettings.Get("FacebookAppID"),
AppSecret = ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
AccessToken = ConfigurationManager.AppSettings.Get("FacebookAccessToken")
};
dynamic result = fb.Post("me/feed", new
{
message = message
});
}
catch (Exception exception)
{
// Handle your exception
}
}
I hope this helps anyone who is struggling to figure this out.

Azure Mobile Service Single Sign on Microsoft Account

I have implemented single sign on using the WL api, but I only recently realized that I need to call mobileService.login on top of that to use the nice authentication features of Azure Mobile Services.
I followed this tutorial
http://www.windowsazure.com/en-us/develop/mobile/tutorials/single-sign-on-windows-8-js/#add-authentication
and added this piece of code:
var login = function () {
return new WinJS.Promise(function (complete) {
WL.init();
WL.login({ scope: ["wl.signin", "wl.basic", "wl.birthday", "wl.emails"] }).then(function (result) {
session = result.session;
WinJS.Promise.join([
WL.api({ path: "me", method: "GET" }),
mobileService.login("microsoftaccount", session.authentication_token)
]).done(function (results) {
var profile = results[0];
var mobileServicesUser = results[1];
var title = "Welcome " + profile.first_name + "!";
var message = "You are now logged in as: " + mobileServicesUser.userId;
var dialog = new Windows.UI.Popups.MessageDialog(message, title);
dialog.showAsync().done(complete);
});
}, function (error) {
session = null;
var dialog = new Windows.UI.Popups.MessageDialog("You must log in.", "Login Required");
dialog.showAsync().done(complete);
});
});
}
however on this line
mobileService.login("microsoftaccount", session.authentication_token)
my session.authentication_token is undefined. (I have an access_token)
If I don't pass the token, I am prompted to sign in every time I launch the app, which defeats the purpose of the integrated sign on.
Any ideas?
To get the authentication token, you need to pass the redirect URI to the call to WL.init:
WL.init({
redirect_uri: "<< INSERT REDIRECT DOMAIN HERE >>"
});
Where the redirect domain must be the same as the one in your Live Connect application.