Facebook "fan gate" with C#/asp.net - c#

The problem is that 1) Facebook seems so fluid with how it allows developers to interact with it (FBML, iFrame, different versions of SDKs) and 2) Everything I find is either PHP or Javascript and I have NO experience with those. What I am trying to do seems sooo simple, and I can't believe there isn't an easy way to do this.
What I have:
I used Visual Studio 2010 to create a simple web application (asp.net/C#) that asks the user for some info (first name, last name, email, etc.). I have a button on there called "Submit" that, when clicked, saves the entered data into a database. I have this hosted on GoDaddy (I know, I know...heh) and it works just fine. No problem here.
I created a "Facebook App" that uses the iFrame thingy so that basically I have a new tab on Facebook that displays my web app mentioned above. This works fine too. The tab is there, the web app is there, and users can enter the data and it is saved to the database. No problem here.
What I WANT:
I want the web app (the thing displayed by the facebook app) to only show the data entry part if the user currently "likes" the facebook entity. I DO NOT want to have to ask permission. I just want to know if they are a fan of the company's facebook "page" that has this app. So I need two things here, shown in my pseudo code below:
Part 1 (check if user is already a fan):
If (user is fan)
{
Show data entry area (unhide it)
}
else
{
Show "Click the like button to see more options"
}
Part 2 (listen for "like" event)
WhenLikeButtonPressed()
{
Show data entry area (unhide it)
}
I've seen stuff about "visible to connection", C# sdk, edge.create, etc. but I just can't make heads or tails of it. I don't mind putting in Javascript or PHP if someone could please give me exact, "Fan Gate for Dummies" steps. Please, I'm going crazy over here :-(

The key is is the signed_request that Facebook posts to your app when the user accesses the page. It contains the data on whether or not the user likes the page. You shouldn't need to worry about catching edge events on an actual tab FB page as it get's reloaded when the user likes/unlikes the page.
You'll need to decode the signed request with your app secret to get the like info. There are examples provided for PHP but I'm sure with a little google help you can find decode info for the signed_request for asp.net/c#.
Here's the php decode for reference:
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}
// check sig
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
and the link https://developers.facebook.com/docs/authentication/signed_request/ the like info will be contained in the page variable

Related

repeat runs of URI google.navigation fail

I've been developing a small asp.net website in which I require to open the android (and iPhone) native map application with directions to a pre-specified address, to do this I'm using this link
protected void anLogic()
{
string street = "my address here";
string formattedstreet = street.Replace(" ", "+");
string address = "google.navigation:q=" + formattedstreet;
Label1.Text = address;
Response.Redirect(address);
}
However, if the user has already clicked the link before and pressed the return button on their phone and tries to reclick, or already has google maps open the button does nothing.
I have also tried the format:
<a href="https://www.google.com/maps/dir/?
api=1&destination=Pike+Place+Market+Seattle+WA&travelmode=bicycling">Text</a>
But I've had similar results.
Basically the only method I've found to get these to work again is for the user to completely close their mobile web browser and google maps and start from the beginning again, which is less than ideal.
Noticed that google uses intents on the google mobile site which does not share this issue:
So I've been modifying my link to use intents rather than universal links:
<a href="intent://www.google.com.au/maps/dir//(ADDRESS+HERE)
#Intent;scheme=http;package=com.google.android.apps.maps;end"> test </a>
I've still had no luck, link still only works once, anyone know a method of overcoming this issue?
Edit: 16/10/20118 not solved yet, anyone looking over this, note the google reference documents from most useful to me to least:
https://developer.chrome.com/multidevice/android/intents
and
https://developers.google.com/maps/documentation/urls/guide
and
https://developer.android.com/guide/components/intents-filters

Parse page using AngleSharp

I want to parse website using c# with AngleSharp, it's easy to do with static pages, but there is a problem, I can't parse info avalible only for authorized users. What should I do to autorize programmatically into website and parse all info avalible for me?
Depending on the used authorization scheme this may either be super simple or ultra hard / impossible.
So let's first visit what can be done with AngleSharp:
Any kind of requests incl. their manipulation (on request, but also before response)
General cookie management (and their manipulation, of course)
Querying the DOM and perform "simple" actions (e.g., clicking a button, submitting a form)
Running trivial JavaScript files
Here trivial means: Scripts that do not need any capabilities beyond what AngleSharp offers, e.g., rendering tree information, advanced CSSOM access, ... - or scripts that require non-ES5 compliant parsers (e.g., make use of ES6 or some special non-standard capabilities).
Now since I do not know what is the authorization scheme or exact problem that you are hitting (some code / MWE would be helpful!) I'll just go for a simple click example.
var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader().WithCookies());
var loginPage = await context.OpenAsync("http://yourpage.com");
var loginForm = loginPage.QuerySelector<IHtmlFormElement>("form");
var profilePage = await loginForm.SubmitAsync(new { userName = "myUser", password = "password" });
// get something on profilePage
Note that in this example the form field names for the login form are userName and password - they may be different for your login page. Also note that your page may contain multiple forms and the selector could be more sophisticated than a simple form.
HTH!

Sign in Card with username/password fields

I'd like to create a sign-in card that is not a link to a webpage to sign in to. Something that behaves something like this
All of the resources I've found online only show the button link like
Whose code looks something like this
var replytoconversation = context.MakeMessage();
replytoconversation.Text = "Authorize your Account";
replytoconversation.Attachments = new List<Attachment>();
var cardbuttons = new List<CardAction>();
cardbuttons.Add(new CardAction
{
Title = "connect",
Value = "http://mail.google.com",
Type = ActionTypes.Signin
});
SigninCard plcard = new SigninCard("Log in to your account", cardbuttons);
Attachment plattachment = plcard.ToAttachment();
replytoconversation.Attachments.Add(new SigninCard("Log in to your account", cardbuttons).ToAttachment());
await context.PostAsync(replytoconversation);
Any help would be appreciated thanks
What you are asking is not possible using the card system in the way you are asking. The only way I can think of at the moment would be to create a login web page alongside your web interface chat (if that's what you are using) and embed the web page in the chat bot (I've done something like this for a similar reason). This is a useful way of dealing with a number of things, such as connecting to a system that does not have an api. I know it's not the exact answer you want, but it's really the only way to my knowledge.
Just updating this as ive been working on it lately. If you use Adaptive-Cards, you can add in Input boxes using AdaptiveTextInput. Link these to a submit function and you can have the user input their details in,then pass them to another function. Here, you can either cast out of the bot to an auth protocol (not sure about how, havent tried yet), or you can do what I'm doing, and use a http/ftp database to authenticate data. Not that secure, but depends on what you are using it for.

Users time tracking on websites/web application

I have a requirement to track how much time the user spending time on web sites/web application. Its like a time tracking tool for websites, I have wpf application which will open the websites on IE/Chrome/Firefox. I need to track the how much time user works on the website.
Chrome and firefox provides the history which i can get from sqlite database that stores in the user system but IE doesn't provide any information about the history.
Is there a better way i can track all browser activity with the user spending time on each websites?
I have wpf application which will open the websites on
IE/Chrome/Firefox. I need to track the how much time user works on the
website
It's a very important Feature Request by many for web 2.0 applications. So, I write a detailed, working and simple solution on the subject here.
You are requesting a feature already created for this workflow. It's a plugin you can include in any website. It's none other than time-on-site tracking in timeonsite.js
Look at the following code (Use latest version; don't just copy/paste),
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/timeonsite/1.2.0/timeonsitetracker.js"></script>
<script>
var config = {
// track page by seconds. Default tracking is by milliseconds
trackBy: 'seconds',
trackHistoryChange: true, //Single-page React/Angular app
callback: function(data) { /* callback denotes your data tracking is real-time */
console.log(data);
var endPointUrl = 'http://example.com' //Replace with your actual backend API URL http://localhost/tos
if (data && data.trackingType) {
if (data.trackingType == 'tos') {
if (Tos.verifyData(data) != 'valid') {
console.log('Data abolished!');
return;
}
}
// make use of sendBeacon if this API is supported by your browser.
if (navigator && typeof navigator.sendBeacon === 'function') {
data.trasferredWith = 'sendBeacon';
var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
navigator.sendBeacon(endPointUrl, blob);
}
}
}
};
var Tos;
if (TimeOnSiteTracker) {
Tos = new TimeOnSiteTracker(config);
}
</script>
</head>
Then, when you refresh, reload or navigate the React app page,
You'll see following object directly saved into your table/printed in browser console. Select "persist" in logs,
{
TOSId: 1129620185532,
TOSSessionKey: "14802525481391382263",
TOSUserId: "anonymous",
title: "Test application - Home page",
URL: "http://nature-blogs/pages/home.php"
entryTime: "2021-11-27 13:15:48.663",
currentTime: "2021-11-27 13:17:31.663",
timeOnPage: 103,
timeOnSite: 103,
timeOnPageTrackedBy: "second",
timeOnPageByDuration: "0d 00h 01m 43s",
timeOnSiteByDuration: "0d 00h 01m 43s",
trackingType: "tos",
}
As you can see, the actions
"entryTime" is captured
"exitTime" is captured in seconds/milliseconds depending upon configuration
"type:time_on_site" is captured
"timeOnPage" is captured // individual page time
"timeOnSite" is captured // session overall page time
What else you need? Since it's stored in SQL DB table, you can do analysis/reporting queries yourself. This works in any RDBMS DB smoothly.
On top of it, 1.Minimize tab, 2.Inactive tab and 3.Switch tab's idle time are all computed and ignored automatically by the tracker itself.
The only thing to note in configuration part is,
trackHistoryChange: true
If the page routing depends on Location Hash or also known as single-page app, include this setting. On the other hand if your web application is a normal page like Wikipedia, avoid setting this line. You are done. For showing the real-time stay time on screen, check this SO post. It's using Jquery to show the results. You can customize it for your React app.
This tracker can be plugged-in in any library VueJs, React, Angular, Jquery, MooTools etc. since it's plain vanilla JS library.
Let me know if you need more input on the subject. I can assist you on this.

IAuthenticationResponse.GetExtension<ClaimsResponse>() always returning null

Update
Thanks to a comment by #IvanL, it turns out that the problem is Google specific. I have since tried other providers and for those everything works as expected. Google just doesn't seem to send claims information. Haven't yet been able to figure out why or what I need to differently to get Google to send it.
A wild stab in the dark says it may be related to the realm being defaulted to http://:/ as I have seen an answer by Andrew Arnott that Google changes the claimed identifier for the same account based on the realm passed with the authentication request.
Another possibly important tidbit of information: unlike many of the examples that can be found around the web for using dotnetopenauth, I am not using a "simple" textbox and composing the openIdIdentifier myself, but I am using the openID selector and that is providing the openIdIdentifier passed to the ValidateAtOpenIdProvider. (As per the Adding OpenID authentication to your ASP.NET MVC 4 application article.)
Question is: why is IAuthenticationResponse.GetExtension() always returning null when using Google as the openId provider, when otherwise all relevant gotcha's with regard to Google (Email requested as required, AXFetchAsSregTransform, etc) have been addressed?
Original
I am struggling with getting DotNetOpenAuth to parse the response returned from the provider. Followed the instructions of Adding OpenID authentication to your ASP.NET MVC 4 application up to the point where the login should be working and a login result in a return to the home page with the user's name (nick name) displayed at the top right. (That is up to "The user should at this point see the following:" just over half way down the article).
I am using Visual Studio Web Developer 2010 Express with C#. DotNetOpenAuth version is 4.0.3.12153 (according to the packages.config, 4.0.3.12163 according to Windows Explorer).
My web.config was modified following the instructions in Activating AXFetchAsSregTransform which was the solution for DotNetOpenId - Open Id get some data
Unfortunately it wasn't enough to get it working for me.
The openid-selector is working fine and resulting in a correct selection of the openid provider. The authentication request is created as follows:
public IAuthenticationRequest ValidateAtOpenIdProvider(string openIdIdentifier)
{
IAuthenticationRequest openIdRequest = openId.CreateRequest(Identifier.Parse(openIdIdentifier));
var fields = new ClaimsRequest()
{
Email = DemandLevel.Require,
FullName = DemandLevel.Require,
Nickname = DemandLevel.Require
};
openIdRequest.AddExtension(fields);
return openIdRequest;
}
This all works. I can login and authorize the page to receive my information, which then results in a call to GetUser:
public OpenIdUser GetUser()
{
OpenIdUser user = null;
IAuthenticationResponse openIdResponse = openId.GetResponse();
if (openIdResponse.IsSuccessful())
{
user = ResponseIntoUser(openIdResponse);
}
return user;
}
openIdResponse.IsSuccessful is implemented as an extension method (see linked article):
return response != null && response.Status == AuthenticationStatus.Authenticated;
and always is successful as the ResponseIntoUser method is entered:
private OpenIdUser ResponseIntoUser(IAuthenticationResponse response)
{
OpenIdUser user = null;
var claimResponseUntrusted = response.GetUntrustedExtension<ClaimsResponse>();
var claimResponse = response.GetExtension<ClaimsResponse>();
// For this to work with the newer/est version of DotNetOpenAuth, make sure web.config
// file contains required settings. See link for more details.
// http://www.dotnetopenauth.net/developers/help/the-axfetchassregtransform-behavior/
if (claimResponse != null)
{
user = new OpenIdUser(claimResponse, response.ClaimedIdentifier);
}
else if (claimResponseUntrusted != null)
{
user = new OpenIdUser(claimResponseUntrusted, response.ClaimedIdentifier);
}
else
{
user = new OpenIdUser("ikke#gmail.com;ikke van ikkenstein;ikke nick;ikkeclaimedid");
}
return user;
}
My version above only differs from the code in the linked article by my addition of the final else block to ensure that I always get the home page with a user name and a logoff link displayed (which helps when trying to do this several times in succession).
I have tried both Google and Yahoo. Both authenticate fine, both return an identity assertion as logged by the WebDev server. However, GetUntrustedExtenstion and GetExtension always return null. I always get to see "ikke nick" from the last else, never the name I actually used to authenticate.
I am at a loss on how to continue to try and get this to work. It probably is some oversight on my part (I am an experienced developer but just started dipping my toes in C# and web front-end development), and I can't see it.
Any and all suggestions on how to proceed / debug this are very much welcome.
Are you using Google as OpenId provider to test your solution against? Because Google has/had the habit of including the Claims only the first time you authenticate the application. So perhaps try using a fresh google account and see if that works?
Sorry for the slow response, doing a big migration at a client this week :-) Glad that this little comment resolved your issue.

Categories

Resources