SFSafariViewController notification when token received - c#

Converting the code in the following swift sample to C# (Xamarin) to notify the App when Paypal Token is received inside SFSafariViewController but it does not fire the method.
https://github.com/paypal/paypal-here-sdk-ios-distribution/blob/master/SampleApp/PPHSDKSampleApp/InitializeViewController.swift
Converted the swift to C# as following but after user login to PayPal and receives the token, Safari is not closing to fire SetupMerchant()
UrlSchemes is also set to retailsdksampleapp to match the sample swift app from PayPal.
SafariDelegate safariDelegate = new SafariDelegate(this);
NSNotificationCenter.DefaultCenter.AddObserver(new NSString("kCloseSafariViewControllerNotification"), safariDelegate.SetupMerchant);
void loadBrowser()
{
var url = new NSUrl("https://paypalauth.herokuapp.com/toPayPal/" + Application.paypalEnvironment + "?returnTokenOnQueryString=true");
var svc = new SFSafariViewController(url);
svc.Delegate = safariDelegate;
this.PresentViewController(svc, true, null);
}
public class SafariDelegate : SFSafariViewControllerDelegate
{
ViewController _controller = null;
public SafariDelegate(ViewController controller)
{
_controller = controller;
}
public void SetupMerchant(NSNotification notification)
{
// Dismiss the SFSafariViewController when the notification of token has been received.
this._controller.PresentedViewController?.DismissViewController(true, () => { });
// Grab the token(s) from the notification and pass it into the merchant initialize call to set up
// the merchant. Upon successful initialization, the 'Connect Card Reader' button will be
// enabled for use.
var accessToken = notification.Object.ToString();
}
}
When running the swift sample app from Paypal, it closes the browser (SFSafariViewController) after login and fires the SetupMerchant() but not in the C# code. There is possibly a missing step or invalid code conversion.

If the Safari controller is not closing and you have the proper UrlScheme set (), then you are missing the OpenUrl override in your AppDelegate class that listening for your app's url schemes and posts the notification that your view controller is listening for.
Example:
[Export("application:openURL:sourceApplication:annotation:")]
public bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
if (sourceApplication == "com.apple.SafariViewService")
{
var dict = HttpUtility.ParseQueryString(url.Query);
var token = dict["access_token"];
NSNotificationCenter.DefaultCenter.PostNotificationName("kCloseSafariViewControllerNotification", new NSString(token));
};
return true;
}

Related

Subscribe to Outlook/Office 365 API Status Code NotFound in C#

I am getting an issue when creating subscription.
My steps are:
Register app at https://apps.dev.microsoft.com
Update permissions
Read Mail and User's info
Then update code, do same steps at https://learn.microsoft.com/en-us/outlook/rest/dotnet-tutorial
After login, I can get access token
Screen after login
Then I try to create a subscription for Inbox
var newSub = new Subscription
{
Resource = "me/mailFolders{'Inbox'}/messages",
ChangeType = "created,updated",
NotificationUrl = notificationUrl,
ClientState = clientState,
ExpirationDateTime = DateTime.Now.AddMinutes(15)
};
var result = await graphClient.Subscriptions.Request().AddAsync(newSub);
Implement for notification in notification URL - I can get validation token and return in plain text.
public async Task<ActionResult> Listen()
{
if (Request.QueryString["validationToken"] != null)
{
var token = Request.QueryString["validationToken"];
return Content(token, "plain/text");
}
}
But I always get this error.
Is there anyone know problem?
You must expose a public HTTPS endpoint to create a subscription and receive notifications from Microsoft Graph.

FCM - Android Xamarin NotRegistered error when sending message after re-debugging the App

I am developing an App in Xamarin Android, for notifications I am using FCM the Pre-Release package: https://www.nuget.org/packages/Xamarin.Firebase.Messaging/
Now everything works fine if I clean the App data, the OnTokenRefresh event is fired and a new token is generated - when I send a new notification on this Token the notification is sent and received by the device in OnMessageReceived() -
The problem is when I make changes to the code and run the application again, if I use the old token I get the NotRegistered Error when sending a notification, but if I go and clean the App Data, then the OnTokenRefresh() is fired a new token is generated - the new token works.
Similar issue here, but this is GCM (I am using FCM):
Google cloud message 'Not Registered' failure and unsubscribe best practices?
https://stackoverflow.com/a/36856867/1910735
https://forums.xamarin.com/discussion/65205/google-cloud-messaging-issues#latest
My FCMInstanceIdService
[Service, IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FCMInstanceIdService : FirebaseInstanceIdService
{
private string Tag = "FCMInstanceIdService";
public override void OnTokenRefresh()
{
var fcmDeviceId = FirebaseInstanceId.Instance.Token;
if (Settings.DeviceId != fcmDeviceId)
{
var oldDeviceId = Settings.DeviceId;
Settings.DeviceId = fcmDeviceId;
//TODO: update token on DB - Currently OnTokenRefresh is only called when: 1. App data is cleaned, 2. The app is re-installed
//_usersProvider.UpdateUserDeviceId(oldDeviceId, fcmDeviceId);
}
base.OnTokenRefresh();
}
}
My Message Receive Service:
[Service, IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FCMListenerService : FirebaseMessagingService
{
private string Tag = "FCM_Listener_Service";
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
var notification = message.GetNotification();
var data = message.Data;
var title = notification.Title;
var body = notification.Body;
SendNotification(title, body);
}
private void SendNotification(string title, string body)
{
//TODO: Display notification to user
}
}
Manifest:
<application android:label="TBApp" android:theme="#style/TBAppTheme">
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
</application>
How do I force refresh the FCM Token in DEBUG mode so I don't have to delete the App Data every time I run the application?
As this issue only happens when running the Application from Visual Studio while debugging the application (not in the version deployed to PlayStore), what I did to solve the issue temporarily is I created the following service:
[Service]
public class FCMRegistrationService : IntentService
{
private const string Tag = "FCMRegistrationService";
static object locker = new object();
protected override void OnHandleIntent(Intent intent)
{
try
{
lock (locker)
{
var instanceId = FirebaseInstanceId.Instance;
var token = instanceId.Token;
if (string.IsNullOrEmpty(token))
return;
#if DEBUG
instanceId.DeleteToken(token, "");
instanceId.DeleteInstanceId();
#endif
}
}
catch (Exception e)
{
Log.Debug(Tag, e.Message);
}
}
}
then in my launch Activity (the activity that loads whenever the Application is opened is do the following:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
#if DEBUG
if (!IsMyServiceRunning("FCMRegistrationService"))
{
var intent = new Intent(this, typeof(FCMRegistrationService));
StartService(intent);
}
// For debug mode only - will accept the HTTPS certificate of Test/Dev server, as the HTTPS certificate is invalid /not trusted
ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
#endif
}
This will unregister your existing FCMToken and will refresh the Token, so the OnTokenRefresh method will be called, then you will have to write some logic to update the FCMToken on the server.
[Service, IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FCMInstanceIdService : FirebaseInstanceIdService
{
// private string LogTag = "FCMInstanceIdService";
public override void OnTokenRefresh()
{
var fcmDeviceId = FirebaseInstanceId.Instance.Token;
// Settings (is Shared Preferences) - I save the FCMToken Id in shared preferences
// if FCMTokenId is not the same as old Token then update on the server
if (Settings.FcmTokenId != fcmDeviceId)
{
var oldFcmId = Settings.FcmTokenId;
var validationContainer = new ValidationContainer();
// HERE UPDATE THE TOKEN ON THE SERVER
TBApp.Current._usersProvider.UpdateFcmTokenOnServer(oldFcmId, fcmDeviceId, validationContainer);
Settings.FcmTokenId = fcmDeviceId;
}
base.OnTokenRefresh();
}
}
i'm manualy trying to initialize like this
var options = new FirebaseOptions.Builder()
.SetApplicationId("YOURAPPID")
.SetApiKey("YOURAPIKEY")
//.SetDatabaseUrl(Keys.Firebase.Database_Url) //i'M not using it
.SetGcmSenderId("YOURSENDERID")
//.SetStorageBucket(Keys.Firebase.StorageBucket)//i'M not using it
.Build();
try
{ //try to initilize firebase app to get token
FirebaseApp.InitializeApp(Forms.Context, options);//initializeto get token
}
catch
{ //if app already initialized it will throw exception, so get the previous active token and send to your server-database etc
var instanceId = FirebaseInstanceId.Instance;
var token = instanceId.Token;
Service.MyFirebaseMessagingService.RegisterForAndroid(token); //this method sends the token to my server app, you have to write your own
}
So when user opens the app, I'm trying to reinitialize the Firebase app. If it is already initalized it will throw an exception :) I'm taking the token there so it gives me the active registered token. If app is not initialized everything will work on smoothly and so your OnTokenRefresh method will be fired as expected. Hope this helps you.

Bing Ads OAuth Automation using only .NET?

How can I log onto Microsoft Live (with .NET WebClient?) and automate the OAuth process to get a token to make Bing Ads API calls?
My question is similar to How do I get an OAuth request_token from live.com?. However, I am building (C#, .NET 4.5.2) a headless Windows Service using the context of a Bing Ads super admin account that is linked to multiple other Bing Ads accounts. The idea is to authenticate, get the auth bits, and then make calls using the bits at 3:00am. Some of the accounts "compete" so for example group A should not see data from group B, so having an application get data for everyone and filter it and distribute it overnight solves many business problems.
I am concerned that if Live experiences problems, or our application is down for an extended time for any reason, we will have to re-authenticate manually to get data again. Maintenance and management of the credentials is now additional overhead (this is for an enterprise environment) that will have to take the form of an intranet web site/page to allow junior/uninitiated folks to do the work if needed (lets not forget testing and documentation). To contrast, Google provides an option to use key pairs for groups that need to work in a fully automated manner. It appears that Twitter's OAuth2 implementation can be automated without a GUI logon. It appears that other Bing services (eg Translation) can also be automated with WebClient.
I have the Microsoft account name and password already, and have a callback URL of "local-mydomain.com" set in the Bing Ads app GUI (and have a HOSTS entry for local-mydomain.com).
The Microsoft sample appears to work, but it automates the MS Web Browser Control, expects a user to input credentials in the GUI, and then the token is given. Giving the super admin account to users to do this is not an option. Expecting a user to get up at 3:00am to authenticate to upload/download data is not an option. Expecting a user to get desktop access to a server in the farm to "run something" is not an option.
All OAuth ideas appreciated.
Thanks.
Here is the launching code:
partial class OAuthForm : Form
{
private static OAuthForm _form;
private static WebBrowser _browser;
private static string _code;
private static string _error;
// When you register your application, the Client ID is provisioned.
private const string ClientId = "000redacted000";
// Request-related URIs that you use to get an authorization code,
// access token, and refresh token.
private const string AuthorizeUri = "https://login.live.com/oauth20_authorize.srf";
private const string TokenUri = "https://login.live.com/oauth20_token.srf";
private const string DesktopUri = "https://login.live.com/oauth20_desktop.srf";
private const string RedirectPath = "/oauth20_desktop.srf";
private const string ConsentUriFormatter = "{0}?client_id={1}&scope=bingads.manage&response_type=code&redirect_uri={2}";
private const string AccessUriFormatter = "{0}?client_id={1}&code={2}&grant_type=authorization_code&redirect_uri={3}";
private const string RefreshUriFormatter = "{0}?client_id={1}&grant_type=refresh_token&redirect_uri={2}&refresh_token={3}";
// Constructor
public OAuthForm(string uri)
{
InitializeForm(uri);
}
[STAThread]
static void Main()
{
// Create the URI to get user consent. Returns the authorization
// code that is used to get an access token and refresh token.
var uri = string.Format(ConsentUriFormatter, AuthorizeUri, ClientId, DesktopUri);
_form = new OAuthForm(uri);
// The value for "uri" is
// https://login.live.com/oauth20_authorize.srf?client_id=000redacted000&scope=bingads.manage&response_type=code&redirect_uri=https://login.live.com/oauth20_desktop.srf
_form.FormClosing += form_FormClosing;
_form.Size = new Size(420, 580);
Application.EnableVisualStyles();
// Launch the form and make an initial request for user consent.
// For example POST /oauth20_authorize.srf?
// client_id=<ClientId>
// &scope=bingads.manage
// &response_type=code
// &redirect_uri=https://login.live.com/oauth20_desktop.srf HTTP/1.1
Application.Run(_form); // <!---------- Problem is here.
// I do not want a web browser window to show,
// I need to automate the part of the process where
// a user enters their name/password and are
// redirected.
// While the application is running, browser_Navigated filters traffic to identify
// the redirect URI. The redirect's query string will contain either the authorization
// code if the user consented or an error if the user declined.
// For example https://login.live.com/oauth20_desktop.srf?code=<code>
// If the user did not give consent or the application was
// not registered, the authorization code will be null.
if (string.IsNullOrEmpty(_code))
{
Console.WriteLine(_error);
return;
}
Whatever you do, the "super admin" will have to log on at least once, using a browser. You can do that by hosting a simple web page in your service, or you could do it as part of the setup process. The Live samples show you how to do that.
Once the "super admin" has logged on using the code grant, you receive an access token and a refresh token. I'm not sure how long the Live access token is valid, but it is probably log enough for one nightly run. Save the refresh token in a safe place. The following night, you start by exchanging that refresh token by a new access token and a new refresh token. Again, you save this new refresh token for the following night.
You can keep this process running forever, as long as the "super admin" does not revoke the authorization he gave to your app.
UPDATE:
Some OAuth 2.0 servers support the "Resource Owner Password Credentials Grant", see the RFC at https://www.rfc-editor.org/rfc/rfc6749. If the Live server supports that, it would be an alternative to the Code Grant that does not require a browser. However, even of the server supports it, I would recommend against it for security reasons, as it requires storing your "super admin" password on the server. If someone grabs the password, they have full access to the account, and all resources protected by it. It will also break down if you change the password. The code grant does not have these problems.
Your question states that you want or need to run as this "super admin". An other option might be to use the "Client Credentials Grant". However, this also requires a client secret to be stored on the server (as with the password credentials grant). Furthermore, it still requires the super admin to authorize the client, and that in itself requires a code grant using a browser.
You ask why the code grant requires a browser, why you can not use some kind of screen scraping to simulate a browser interaction. First of all, you cannot predict the screens that will be shown to the user. These screens change without notice. More importantly, depending on user options and history, the server shows different screens. For example, the user may have turned on two-factor authentication. Last but not least, why do you object to opening a browser? It will probably be easier than trying to emulate it.
Finally, these "super admin" users might object to giving their password to your application, as they don't really know what you are doing with it (you might be sending to a server of your own, as far as they know). Using the Code Grant with a browser, they know your application never gets to see their password (kind of - you could listen in on browser events or something, unless the browser control is run in a separate process not under your control, such as the Windows 8 WebAuthenticationBroker). Your application only gets a token with the scopes they authorize.
After spending a few hours on this problem for myself and finding absolutely no solution to automate connecting to Bing from a service. Here is what will work using the wonderful WatiN
First grab WatiN and add it to your solution via Nuget.
Then use the following code (my example works in a console application as an Example) to automate the whole grabbing of a token from Microsoft. It's not perfect as this is a sample but it will work.
You should double check the element ID's I'm using in case they changed, they are hard coded - generally remove all the hard coding if your going to use this in a production environment.
I didn't want anyone else to have to go through this.
First it grabs a Code that is then used to grab a Token, just like the OAuth 2.0 specification requires.
using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using WatiN.Core.Native;
using WatiN.Core;
namespace LouiesOAuthCodeGrantFlow
{
// Using access tokens requires that you register your application and that
// the user gives consent to your application to access their data. This
// example uses a form and WebBrowser control to get the user's consent.
// The control and form require a single-threaded apartment.
partial class LouiesBingOAuthAutomation
{
private static LouiesBingOAuthAutomation _form;
private static string _code;
private static string _error;
//your going to want to put these in a secure place this is for the sample
public const string UserName = "your microsoft user name";
public const string Password = "<your microsoft account password";
// When you register your application, the Client ID is provisioned.
//get your clientid https://developers.bingads.microsoft.com/Account
private const string ClientId = "<your client id>";
// Request-related URIs that you use to get an authorization code,
// access token, and refresh token.
private const string AuthorizeUri = "https://login.live.com/oauth20_authorize.srf";
private const string TokenUri = "https://login.live.com/oauth20_token.srf";
private const string DesktopUri = "https://login.live.com/oauth20_desktop.srf";
private const string RedirectPath = "/oauth20_desktop.srf";
private const string ConsentUriFormatter = "{0}?client_id={1}&scope=bingads.manage&response_type=code&redirect_uri={2}";//&displayNone
private const string AccessUriFormatter = "{0}?client_id={1}&code={2}&grant_type=authorization_code&redirect_uri={3}";
private const string RefreshUriFormatter = "{0}?client_id={1}&grant_type=refresh_token&redirect_uri={2}&refresh_token={3}";
// Constructor
public LouiesBingOAuthAutomation(string uri)
{
InitializeForm(uri);
}
[STAThread]
static void Main()
{
var uri = string.Format(ConsentUriFormatter, AuthorizeUri, ClientId, DesktopUri);
_form = new LouiesBingOAuthAutomation(uri);
if (string.IsNullOrEmpty(_code))
{
Console.WriteLine(_error);
return;
}
uri = string.Format(AccessUriFormatter, TokenUri, ClientId, _code, DesktopUri);
AccessTokens tokens = GetAccessTokens(uri);
Console.WriteLine("Access token expires in {0} minutes: ", tokens.ExpiresIn / 60);
Console.WriteLine("\nAccess token: " + tokens.AccessToken);
Console.WriteLine("\nRefresh token: " + tokens.RefreshToken);
uri = string.Format(RefreshUriFormatter, TokenUri, ClientId, DesktopUri, tokens.RefreshToken);
tokens = GetAccessTokens(uri);
Console.WriteLine("Access token expires in {0} minutes: ", tokens.ExpiresIn / 60);
Console.WriteLine("\nAccess token: " + tokens.AccessToken);
Console.WriteLine("\nRefresh token: " + tokens.RefreshToken);
}
private void InitializeForm(string uri)
{
using (var browser = new IE(uri))
{
var page = browser.Page<MyPage>();
page.PasswordField.TypeText(Password);
try
{
StringBuilder js = new StringBuilder();
js.Append(#"var myTextField = document.getElementById('i0116');");
js.Append(#"myTextField.setAttribute('value', '"+ UserName + "');");
browser.RunScript(js.ToString());
var field = browser.ElementOfType<TextFieldExtended>("i0116");
field.TypeText(UserName);
}
catch (Exception ex)
{
Console.Write(ex.Message + ex.StackTrace);
}
page.LoginButton.Click();
browser.WaitForComplete();
browser.Button(Find.ById("idBtn_Accept")).Click();
var len = browser.Url.Length - 43;
string query = browser.Url.Substring(43, len);
if (query.Length == 50)
{
if (!string.IsNullOrEmpty(query))
{
Dictionary<string, string> parameters = ParseQueryString(query, new[] { '&', '?' });
if (parameters.ContainsKey("code"))
{
_code = parameters["code"];
}
else
{
_error = Uri.UnescapeDataString(parameters["error_description"]);
}
}
}
}
}
// Parses the URI query string. The query string contains a list of name-value pairs
// following the '?'. Each name-value pair is separated by an '&'.
private static Dictionary<string, string> ParseQueryString(string query, char[] delimiters)
{
var parameters = new Dictionary<string, string>();
string[] pairs = query.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
foreach (string pair in pairs)
{
string[] nameValue = pair.Split(new[] { '=' });
parameters.Add(nameValue[0], nameValue[1]);
}
return parameters;
}
// Gets an access token. Returns the access token, access token
// expiration, and refresh token.
private static AccessTokens GetAccessTokens(string uri)
{
var responseSerializer = new DataContractJsonSerializer(typeof(AccessTokens));
AccessTokens tokenResponse = null;
try
{
var realUri = new Uri(uri, UriKind.Absolute);
var addy = realUri.AbsoluteUri.Substring(0, realUri.AbsoluteUri.Length - realUri.Query.Length);
var request = (HttpWebRequest)WebRequest.Create(addy);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (var writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(realUri.Query.Substring(1));
}
var response = (HttpWebResponse)request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream != null)
tokenResponse = (AccessTokens)responseSerializer.ReadObject(responseStream);
}
}
catch (WebException e)
{
var response = (HttpWebResponse)e.Response;
Console.WriteLine("HTTP status code: " + response.StatusCode);
}
return tokenResponse;
}
}
public class MyPage : WatiN.Core.Page
{
public TextField PasswordField
{
get { return Document.TextField(Find.ByName("passwd")); }
}
public WatiN.Core.Button LoginButton
{
get { return Document.Button(Find.ById("idSIButton9")); }
}
}
[ElementTag("input", InputType = "text", Index = 0)]
[ElementTag("input", InputType = "password", Index = 1)]
[ElementTag("input", InputType = "textarea", Index = 2)]
[ElementTag("input", InputType = "hidden", Index = 3)]
[ElementTag("textarea", Index = 4)]
[ElementTag("input", InputType = "email", Index = 5)]
[ElementTag("input", InputType = "url", Index = 6)]
[ElementTag("input", InputType = "number", Index = 7)]
[ElementTag("input", InputType = "range", Index = 8)]
[ElementTag("input", InputType = "search", Index = 9)]
[ElementTag("input", InputType = "color", Index = 10)]
public class TextFieldExtended : TextField
{
public TextFieldExtended(DomContainer domContainer, INativeElement element)
: base(domContainer, element)
{
}
public TextFieldExtended(DomContainer domContainer, ElementFinder finder)
: base(domContainer, finder)
{
}
public static void Register()
{
Type typeToRegister = typeof(TextFieldExtended);
ElementFactory.RegisterElementType(typeToRegister);
}
}
// The grant flow returns more fields than captured in this sample.
// Additional fields are not relevant for calling Bing Ads APIs or refreshing the token.
[DataContract]
class AccessTokens
{
[DataMember]
// Indicates the duration in seconds until the access token will expire.
internal int expires_in = 0;
[DataMember]
// When calling Bing Ads service operations, the access token is used as
// the AuthenticationToken header element.
internal string access_token = null;
[DataMember]
// May be used to get a new access token with a fresh expiration duration.
internal string refresh_token = null;
public string AccessToken { get { return access_token; } }
public int ExpiresIn { get { return expires_in; } }
public string RefreshToken { get { return refresh_token; } }
}
}

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
}

Redirecting a user from within a SignalR Hub class

I have a button that users can click to bid on something. Each bid, broadcasts the latest bid to every other client. That's the reason why I'm using SignalR.
Now, the user needs to have active credits, and if he doesn't have credits I want to redirect him somewhere.
The more obvious approach fails me, so any suggestion is welcome.
//Does the user have credits to spend?
if (user.LanceCreditBalance >= 1)
{
//populate the "ar" object and send it out to everybody.
var result = Json.Encode(ar);
Clients.addMessage(result);
}
else
{
//And this isn't working as expected. Doesn't redirect
//And causes a 302 error when viewing the Firebug console in Firefox.
HttpContext.Current.Response.Redirect(#"http://www.google.com");
}
The above code is all within the Chat class which inherits from the SignalR.Hub class.
Server:
if(user.LanceCreditBalance >= 1)
{
var result = Json.Encode(ar);
// send Message to all clients
Clients.addMessage(result);
}
else
{
// Invoke a js-Function only on the current client
Caller.redirectMe("http://www.google.com");
}
Client:
$(function () {
var chat = $.connection.chat;
chat.addMessage = function(message) {
// do something
};
// function the server can invoke
chat.redirectMe = function(target) {
window.location = target;
};
$.connection.hub.start();
});

Categories

Resources