FB Logout with C# sdk - c#

I'm logged in to my app using my FB credentials. In the end I do a logout and remove my session variables. I'm logged out from application, but the FB session remains open although I do a post to the FB logout page with the post like in the code:
if (Session["FBAccessToken"] != null){
var fb = new Facebook.FacebookClient();
string accessToken = Session["FBAccessToken"] as string;
//var logoutUrl = fb.GetLogoutUrl(new { access_token = accessToken, next = "https://www.facebook.com/", });
var logoutUrl = fb.GetLogoutUrl(new { next = "https://www.facebook.com/", });
fb.Post(logoutUrl.AbsoluteUri, new { access_token = accessToken });
Session.RemoveAll();
}
I've tried both: logoutUrl generated with and without access token parameter, neither worked for me.

There has been changes on facebook logout since my last blog post. Here is the way to logout.
var fb = new FacebookClient();
var logoutUrl = fb.GetLogoutUrl(new {access_token = "...", next = "...." });
// redirect to logoutUrl.AbsoluteUri
next url cannot be any arbitrary url. I has to be the one that is part of the site url which you used to retrieve the access token.

#prabir has the answer. Here is a complete answer after I tweaked it for my MVC app. Just replace "localhost:51042/" with whatever URL is appropriate for your app.
This actually fills a hole in the tutorial: http://www.asp.net/mvc/overview/getting-started/using-oauth-providers-with-mvc
public ActionResult LogOff()
{
WebSecurity.Logout();
if (Session["facebooktoken"] != null)
{
var fb = new Facebook.FacebookClient();
var logoutUrl = fb.GetLogoutUrl(new { access_token = Session["facebooktoken"], next = "http://localhost:51042/" });
Response.Redirect(logoutUrl.AbsoluteUri);
Session.RemoveAll();
}
return RedirectToAction("Index", "Home");
}

Related

IdentityServer shows blank PostLogoutRedirectUri for Android Native App

I have created an OAuth Server using IdentityServer4 and .Net Core Signin Manager. The Login works great and returns to my app. The Logout doesn't seem to know who is logging out. The Logout Razor Page code is as follows:
public async Task<IActionResult> OnGet(string logoutId)
{
var logout = await _interaction.GetLogoutContextAsync(logoutId);
PostLogoutRedirectUri = logout?.PostLogoutRedirectUri;
AutomaticRedirectAfterSignOut = (PostLogoutRedirectUri != null);
ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName;
SignOutIframeUrl = logout?.SignOutIFrameUrl;
LogoutId = logoutId;
if (User?.Identity.IsAuthenticated == true)
{
var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider)
{
var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
if (providerSupportsSignout)
{
if (LogoutId == null)
{
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// before we signout and redirect away to the external IdP for signout
LogoutId = await _interaction.CreateLogoutContextAsync();
}
ExternalAuthenticationScheme = idp;
}
}
// delete local authentication cookie
await _signInManager.SignOutAsync();
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, ExternalAuthenticationScheme);
}
if (AutomaticRedirectAfterSignOut)
return Redirect(PostLogoutRedirectUri);
else
return Page();
}
When it gets called, there is a logoutId. It gets the context, but PostLogoutRedirectUri is blank. ClientId and ClientName are also blank, but the context has a field called ClientIds and the first entry is the correct ClientId for my app. The log shows as follows:
IdentityServer4.Validation.EndSessionRequestValidator: Information: End session request validation success
{
"SubjectId": "6841dc6c-0bd7-4f72-8f1c-f7czzzzzzzzz",
"Raw": {
"post_logout_redirect_uri": "mps.mobile.app://callback"
}
}
IdentityServer4.Hosting.IdentityServerMiddleware: Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.EndSessionCallbackEndpoint for /connect/endsession/callback
IdentityServer4.Endpoints.EndSessionCallbackEndpoint: Information: Successful signout callback.
I am using IdentityModel for the Client App. I have the logout coded as follows:
_options = new OidcClientOptions
{
Authority = MPSOidc.Authority,
ClientId = MPSOidc.ClientID,
Scope = "openid profile myapi offline_access email",
RedirectUri = MPSOidc.RedirectUri,
PostLogoutRedirectUri = MPSOidc.RedirectUri,
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect,
Browser = new ChromeCustomTabsBrowser(this)
};
var oidcClient = new OidcClient(_options);
var r = new LogoutRequest();
await oidcClient.LogoutAsync(r);
It seems like the PostLogoutRedirectUri should show up here. Does anyone know a way to make this happen? If not, can the ClientId be used to get the Client information to find the PostLogoutRedirectUri there?
Thanks,
Jim
Here is what it was. When I logged out on the OidcClient, I didn't pass the ID Token. On my client Android app, I had to add the ID Token to the logout request:
var r = new LogoutRequest()
{
IdTokenHint = MPSOidc.Tokens.IdentityToken
};
That's all it took.
Cheers.

How to implement refresh token workflow into OAUTH workflow in MVC C# app?

I am new to OAUTH. I have been working on implementing OAUTH into my MVC c# application to access ping federate. After much research, and failed attempt at using the ping federate nuget, I came across this link that finally gave some clarity to the full process with a coding example. I have came across much generic examples of the endpoints i need to access but never a full workflow coding example. After implementing that code with some changes and was successful at signing in the ping user into my MVC app, I started doing more research about the refresh token. Questions...
Q. I know how to access a a refresh token, meaning I know which endpoint used to refresh the access token after I have authenticated the user in ping federate. But what is the refresh token used for? Is it used to extend my application's session once it ends? Or it used for if the user signs out of my application then they click the 'Sign in with Ping Federate' link on the login and not have them authenticate again as long as the refresh token is still valid?
Q. And if the refresh token is used for when after a user authenticates the first time, and I save the refresh token in the db and then user signs back using that 'Sign in with Ping Federate' link on my login back how can I know what user that is to lookup the refresh token in the db to give them access to my site without re-authenticating them with ping federate? Since when they come to that link 'Sign in with Ping Federate' I do not know who they are?
This is the below code that I am using, from user MatthiasRamp in the link i provided...I want to add my refresh token logic with the below code.
public async Task<ActionResult> Login(string returnUrl)
{
if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);
if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
_returnUrl = returnUrl;
//callback function
_redirectUrl = Url.Action("AuthorizationCodeCallback", "ExternalLogin", null, Request.Url.Scheme);
Dictionary<string, string> authorizeArgs = null;
authorizeArgs = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"response_type", "code"}
,{"scope", "read"}
,{"redirect_uri", _redirectUrl}
// optional: state
};
var content = new FormUrlEncodedContent(authorizeArgs);
var contentAsString = await content.ReadAsStringAsync();
return Redirect("http://localhost:64426/oauth/authorize?" + contentAsString);}
public async Task<ActionResult> AuthorizationCodeCallback()
{
// received authorization code from authorization server
string[] codes = Request.Params.GetValues("code");
var authorizationCode = "";
if (codes.Length > 0)
authorizationCode = codes[0];
// exchange authorization code at authorization server for an access and refresh token
Dictionary<string, string> post = null;
post = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"client_secret", "ClientSecret"}
,{"grant_type", "authorization_code"}
,{"code", authorizationCode}
,{"redirect_uri", _redirectUrl}
};
var client = new HttpClient();
var postContent = new FormUrlEncodedContent(post);
var response = await client.PostAsync("http://localhost:64426/token", postContent);
var content = await response.Content.ReadAsStringAsync();
// received tokens from authorization server
var json = JObject.Parse(content);
_accessToken = json["access_token"].ToString();
_authorizationScheme = json["token_type"].ToString();
_expiresIn = json["expires_in"].ToString();
if (json["refresh_token"] != null)
_refreshToken = json["refresh_token"].ToString();
//SignIn with Token, SignOut and create new identity for SignIn
Request.Headers.Add("Authorization", _authorizationScheme + " " + _accessToken);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
var ctxUser = ctx.Authentication.User;
var user = Request.RequestContext.HttpContext.User;
//redirect back to the view which required authentication
string decodedUrl = "";
if (!string.IsNullOrEmpty(_returnUrl))
decodedUrl = Server.UrlDecode(_returnUrl);
if (Url.IsLocalUrl(decodedUrl))
return Redirect(decodedUrl);
else
return RedirectToAction("Index", "Home");
}

instaSharp oauthResponse Not work

I want to use instaSharp to use instagram api , get followers anp posts and ...
when I get code with callbackUrl I cant Send Requesttoken(code)
and my oauthResponse is null ...
this is my code :
async Task getaouth()
{
var clientId = ConfigurationManager.AppSettings["client_id"];
var clientSecret = ConfigurationManager.AppSettings["client_secret"];
var redirectUri = ConfigurationManager.AppSettings["redirect_uri"];
var realtimeUri = "";
InstagramConfig config = new InstagramConfig(clientId, clientSecret, redirectUri, realtimeUri);
Session.Add("InstaSharp.config", config);
// add this code to the auth object
var auth = new InstaSharp.OAuth(config);
// now we have to call back to instagram and include the code they gave us
// along with our client secret
var oauthResponse = await auth.RequestToken(code);
// tell the session that we are authenticated
//config.isAuthenticated = true;
Response.Write(r.ToString());
// both the client secret and the token are considered sensitive data, so we won't be
// sending them back to the browser. we'll only store them temporarily. If a user's session times
// out, they will have to click on the authenticate button again - sorry bout yer luck.
Session.Add("InstaSharp.AuthInfo", oauthResponse);
// all done, lets redirect to the home controller which will send some intial data to the app
//return RedirectToAction("Index", "Home");
Response.Write("");
}
after this my oauthResponse is null !
and after call this method
_users.GetSelf()
i get it :
An exception of type 'System.InvalidOperationException' occurred in InstaSharp.dll but was not handled in user code
Additional information: You are not authenticated
Have you already registered your test client application on your account at Instagram developers?
If you haven't, sing in with your account here, click on top-button "Manager clients" and add your test application to get the correct client ID and client secret informations.
Use this way,
Install latest InstaSharp and just do this:
private InstagramConfig _config;
public async Task< ActionResult> somename(string code)
{
if (code != null)
{
_config = new InstagramConfig(["InstgramClientId"],
["InstgramClientSecret"],
["InstgramRedirectUrl"]
);
var instasharp = new InstaSharp.OAuth(_config);
var authInfo = await instasharp.RequestToken(code);
var user = new InstaSharp.Endpoints.Users(_config, authInfo);
ViewBag.Username = user.OAuthResponse.User.Username;
ViewBag.Token = authInfo.AccessToken;
return View();
}
return View("name");
}
In your case, you have to make call to your method like:
var authresponse = await getaouth();
Make sure your calling function is async task.

Facebook OAuth issue MVC4

Annoyingly I have been trying to solve this issue for many hours, every answer is no direct solution:
{
"error": {
"message": "Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request",
"type": "OAuthException",
"code": 100
}
}
I have been banging my head against the wall!. I am using the facebook soap client for this http://pastebin.com/G7ysgbdX
This will allow us to integrate (with the user's permission) their timeline. Facebook is saying the URL's do not match. How do I test this?
This works locally but not on the server with the exact same code except http://free-rsvp.com/ instead of localhost:53111/
This is the below URL. I cannot see anything wrong!
Any advice would be appreciated.
provider_%3dFacebookPro%26_sid_%3da80359b555c54b8f8d2f4f8e803f9125&client_secret=212360b3ea6478fd0a0491e736b54256&code=AQCyTDStiB5gsQpKMx4uI1yFesVnLnWfE3u70VsB02-4HSyUCTbcf_3oHMo7QQI2as_pw1tpFONs8tClq4FxCr4AzuCMzLBsRnyOM3dcattTATdU-ahq5cjr4lPJNp2gkTrpgWUmqDEVQ8PBvYFB1LdWJpojxRIC24lv0GkQSSqdrct41UGHfDjhnPfI1mV945NgVJSfebhJP7O0GWxP9o9g_4svDCKa2LtCRbo7nDfWLeiE9fGULhmuJDjefAFZ5VMiYj8SrA4QtZXIu8jUQSQT89VYEP8PuG2hS_wMr0TL_GmcvEhNzQ8psPpPWFYhmSo">https://graph.facebook.com/oauth/access_token?client_id=523007704381837&redirect_uri=http%3a%2f%2ffree-rsvp.com%3a80%2fAccount%2fExternalLoginCallback%3fReturnUrl%3d%252FDashboard%26_provider_%3dFacebookPro%26_sid_%3da80359b555c54b8f8d2f4f8e803f9125&client_secret=212360b3ea6478fd0a0491e736b54256&code=AQCyTDStiB5gsQpKMx4uI1yFesVnLnWfE3u70VsB02-4HSyUCTbcf_3oHMo7QQI2as_pw1tpFONs8tClq4FxCr4AzuCMzLBsRnyOM3dcattTATdU-ahq5cjr4lPJNp2gkTrpgWUmqDEVQ8PBvYFB1LdWJpojxRIC24lv0GkQSSqdrct41UGHfDjhnPfI1mV945NgVJSfebhJP7O0GWxP9o9g_4svDCKa2LtCRbo7nDfWLeiE9fGULhmuJDjefAFZ5VMiYj8SrA4QtZXIu8jUQSQT89VYEP8PuG2hS_wMr0TL_GmcvEhNzQ8psPpPWFYhmSo
EDIT
The url that takes me to facebook is:
provider_%253DFacebookPro%2526_sid_%253Dbfe82f104ab34d1aa0f44c477c4d7819%26scope%3Demail%252Cuser_likes%252Cfriends_likes%252Cuser_birthday%252Cpublish_checkins%252Cpublish_stream%26client_id%3D523007704381837%26ret%3Dlogin&cancel_uri=http%3A%2F%2Ffree-rsvp.com%2FAccount%2FExternalLoginCallback%3FReturnUrl%3D%252FDashboard%26_provider_%3DFacebookPro%26_sid_%3Dbfe82f104ab34d1aa0f44c477c4d7819%26error%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied%23_%3D_&display=page">https://www.facebook.com/login.php?skip_api_login=1&api_key=523007704381837&signed_next=1&next=https%3A%2F%2Fwww.facebook.com%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Ffree-rsvp.com%252FAccount%252FExternalLoginCallback%253FReturnUrl%253D%252FDashboard%2526_provider_%253DFacebookPro%2526_sid_%253Dbfe82f104ab34d1aa0f44c477c4d7819%26scope%3Demail%252Cuser_likes%252Cfriends_likes%252Cuser_birthday%252Cpublish_checkins%252Cpublish_stream%26client_id%3D523007704381837%26ret%3Dlogin&cancel_uri=http%3A%2F%2Ffree-rsvp.com%2FAccount%2FExternalLoginCallback%3FReturnUrl%3D%252FDashboard%26_provider_%3DFacebookPro%26_sid_%3Dbfe82f104ab34d1aa0f44c477c4d7819%26error%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied%23_%3D_&display=page
here is a piece of my server-side authentication code (MVC4 project). The part you should look at is how redirect URL is generated - identically - in both "Authorize" and "AuthorizeCallback" functions - and passed to facebook client:
[HttpPost]
public ActionResult Authorize(Guid eventId)
{
var #event = this.eventRepository.Find(eventId);
var redirectUri = ConfigurationProvider.HostingEndpoint + this.Url.Action("AuthorizeCallback", new { eventCode = #event.Code });
var service = new FacebookClient();
var loginUrl = service.GetLoginUrl(new {
client_id = ConfigurationProvider.FacebookAppId,
client_secret = ConfigurationProvider.FacebookAppSecret,
redirect_uri = redirectUri,
response_type = "code",
scope = "manage_pages, publish_actions, user_photos, publish_stream" // Add other permissions as needed
});
return new RedirectResult(loginUrl.AbsoluteUri, permanent: false);
}
public ActionResult AuthorizeCallback(string code, string eventCode, UserProfile userProfile)
{
var #event = this.eventRepository.Find(eventCode);
if (string.IsNullOrWhiteSpace(code) == true)
{
// means user clicked "cancel" when he was prompted to authorize the app
// todo: show some error message? or just redirect back?
return this.RedirectToAction("Event", "Dashboard", new { eventCode = #event.Code, feature = FeatureType.Update });
}
var redirectUri = ConfigurationProvider.HostingEndpoint + this.Url.Action("AuthorizeCallback", new { eventCode = #event.Code });
var fb = new FacebookClient();
dynamic result = fb.Post("oauth/access_token", new
{
client_id = ConfigurationProvider.FacebookAppId,
client_secret = ConfigurationProvider.FacebookAppSecret,
redirect_uri = redirectUri,
code = code
});
var accessToken = result.access_token;
// update the facebook client with the access token so
// we can make requests on behalf of the user
fb.AccessToken = accessToken;
// Get the user's information
dynamic me = fb.Get("me");
return this.RedirectToAction("Event", "Dashboard", new { eventCode = #event.Code, feature = FeatureType.Update });
}

Facebook C# SDK Post to Page as Page

I have been struggling to get the Facebook C# SDK to post to my page, as the page for a couple of days.
From my googling, I have found that the process should be as follows:
Authorize application for manage_pages and publish_stream with my user account (done through this URL: https://graph.facebook.com/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=publish_stream,manage_pages)
Get user access token
Exchange user access token for long lived access token
Get me/accounts with the long lived user access token
Get the page access token from the result
Post to the page_id/feeds endpoint using the long lived token
I can follow this process through the graph explorer, and it works. A post is created on the page with the generated token.
How do I do this using the C# SDK?
I tried:
dynamic userTokenResult = client.Get("oauth/access_token", new
{
client_id = appid,
client_secret = appsecret,
grant_type = "client_credentials"
});
dynamic longLivedResult = client.Get("oauth/access_token", new
{
client_id = appid,
client_secret = appsecret,
grant_type = "fb_exchange_token",
fb_exchange_token = userTokenResult.access_token;
});
client.AccessToken = longLivedResult.access_token;
// Post the message
dynamic messagePost = new
{
link = message.LinkUrl,
name = message.LinkName,
caption = message.LinkCaption,
description = message.LinkDescription,
message = message.Message
};
// Set the status
var postId = client.Post("pagename/feed", messagePost);
However, I suspect that this is returning the application access_token, not the user access_token (it fails at GET: me/accounts).
you cannot get user token from server-side code (even if you know login/password). You should either:
copy/paste it from Graph API Explorer
Get it from JS SDK client side
Use GetLoginUrl function from FacebookClient to get login URL and redirect the user to that page. After login is completed, facebook will call your function back - and in that function you will be able to the the token. Below are 2 functions (authorize and callback) from my MVC project - but I think you will get the idea.
public ActionResult Authorize(Guid eventId)
{
var redirectUri = ConfigurationProvider.HostingEndpoint + this.Url.Action("AuthorizeCallback", new { eventCode = eventId });
var service = new FacebookClient();
var loginUrl = service.GetLoginUrl(new {
client_id = ConfigurationProvider.FacebookAppId,
client_secret = ConfigurationProvider.FacebookAppSecret,
redirect_uri = redirectUri,
response_type = "code",
scope = "manage_pages, publish_actions, user_photos, publish_stream" // Add other permissions as needed
});
return new RedirectResult(loginUrl.AbsoluteUri, permanent: false);
}
that will redirect user to the Facebook login page. When user enters credentials and presses login, this function will be called (note the code parameter - it will be used to get the token):
public ActionResult AuthorizeCallback(string code, string eventCode)
{
var redirectUri = ConfigurationProvider.HostingEndpoint + this.Url.Action("AuthorizeCallback", new { eventCode = eventId });
var fb = new FacebookClient();
dynamic result = fb.Post("oauth/access_token", new
{
client_id = ConfigurationProvider.FacebookAppId,
client_secret = ConfigurationProvider.FacebookAppSecret,
redirect_uri = redirectUri,
code = code
});
var accessToken = result.access_token;
// update the facebook client with the access token so
// we can make requests on behalf of the user
fb.AccessToken = accessToken;
// now get externded app Token
dynamic extendedToken = fb.Get("oauth/access_token", new
{
client_id = ConfigurationProvider.FacebookAppId,
client_secret = ConfigurationProvider.FacebookAppSecret,
grant_type = "fb_exchange_token",
fb_exchange_token = fb.AccessToken
});
// Get the user's information
dynamic me = fb.Get("me");
}
After that you should call "/me/accounts", find your page and get its token from there.
If you're just trying to post to your own page, an alternative is to use Windows PowerShell and http://facebookpsmodule.codeplex.com. This reduces the operation to a few lines of PowerShell script.

Categories