PHP Oauth inside page TAB - oauth

My tab needs to know the date when a user becomes fan, so i've to ask for permissions. Despite facebook official documentation, i made it successfully and the user is redirected to the oauth dialog and after he confirms, back to the tab.
$user = $facebook->getUser();
if ($user)
{
$access_token = $facebook->getAccessToken();
$user_profile = $facebook->api('/me');
try
{
$likes = $facebook->api("/me/likes/pageid");
}
catch (FacebookApiException $e)
{
$user = null;
}
}
else
{
?>
<script type="text/javascript">
var oauth_url = 'https://www.facebook.com/dialog/oauth/';
oauth_url += '?client_id=<?=$app_id?>';
oauth_url += '&redirect_uri=' + encodeURIComponent('https://www.facebook.com/<?=$pageid?>?sk=app_<?=$app_id?>');
oauth_url += '&scope='
window.top.location = oauth_url;
</script>
<?
die();
}
All works, but i'm not able to parse the error response in case users don't accept the dialog.
The returning url is:
https://www.facebook.com/page?sk=app_appid&error_reason=user_denied&error=access_denied&error_description=The+user+denied+your+request.#_=_
But the iframe inside tab doesn't have any parameters.
What can i do?

You can only pass data to the iframe on the tab using the app_data parameter in the URL. You will have to redirect unsuccessful login attempts to a URL like:
https://www.facebook.com/{your_page}?v=app_{app_id}&app_data={your_string}
E.g. https://www.facebook.com/{your_page}?v=app_{app_id}&app_data=login_failed
The login_failed still will appear in the decoded signed_request

Related

OAuth implicit grant - Can't get the URL fragment, which contains the access token

I'm trying to implement the implicit grant OAuth flow using AWS Cognito. In particular, after having already logged in to my website, I'm trying to make a GET request to Cognito's AUTHORIZATION endpoint; the response from this request should redirect me to a URL of my choosing - let's call this the callback URL - and provide the desired access token in the fragment.
If I make this request by entering into the browser's address bar the appropriate URL for the AUTHORIZATION endpoint, everything happens as expected: The browser gets redirected to the callback URL, and the access token appears in the fragment of this URL.
However, if I make this same request asynchronously from a script in my website using XMLHttpRequest, I am unable to access the fragment returned in the callback URL (and Chrome's network tab shows that the token-containing fragment is in fact returned, just like in the address bar scenario described above). How can I access this fragment?
My code is as follows:
let xhr = new XMLHttpRequest();
let method = options.method.toUpperCase();
let extractFrom = ['url', 'code'];
xhr.open(options.method, options.url, true);
xhr.withCredentials = true;
for (const key in options.headers) {
xhr.setRequestHeader(key, options.headers[key]);
}
xhr.onreadystatechange = function () {
const status = this.status;
const respUrl = this.responseURL;
const respHeaders = this.getAllResponseHeaders();
const respBody = this.response;
if (this.readyState === XMLHttpRequest.DONE) {
if (status === 200) {
let val = extractParameter(extractFrom[0], respUrl, extractFrom[1]);
resolve(val);
} else {
console.error('Other Response Text: ' + this.statusText);
reject(this.statusText);
}
}
};
xhr.onerror = function () {
console.error('Error: ' + xhr.statusText);
reject(this.statusText);
};
xhr.send(null);
The fragment is client site stuff, only stays in browser. You will need use javascript to pull it explicitly, see https://openid.net/specs/openid-connect-core-1_0.html#FragmentNotes. You could avoid fragment by using response_mode=form_post if OpenID Connect server supports it, see https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html.

Passing values from login form to dashboard

What would be the best way to retrieve the Username entered on a login form in dart polymer to be read in the next page to which it is redirected?
The login component is as below -
#CustomTag('alfresco-login-form')
class LoginFormComponent extends FormElement with Polymer, Observable {
LoginFormComponent.created() : super.created();
#observable String username = "";
#observable String password = "";
#observable Map loginData = toObservable({
'username' : '',
'password' : ''
});
#observable String serverResponse = '';
HttpRequest request;
void submitForm(Event e, var detail, Node target) {
e.preventDefault(); // Don't do the default submit.
request = new HttpRequest();
request.onReadyStateChange.listen(onData);
// POST the data to the server.
var url = 'http://127.0.0.1/alfresco/service/api/login';
request.open("POST", url);
request.send(_loginDataAsJsonData());
}
void onData(_) {
if (request.readyState == HttpRequest.DONE &&
request.status == 200) {
// Data saved OK.
serverResponse = 'Server Sez: ' + request.responseText;
Map parsedMap = JSON.decode(request.responseText);
var currentTicket = new Ticket(parsedMap["data"]["ticket"]);
//keeps the back history button active
//window.location.assign('dashboard.html');
//doesn't keep the back history button active
//doesn't put the originating page in the session history
window.location.replace('dashboard.html');
} else if (request.readyState == HttpRequest.DONE &&
request.status == 0) {
// Status is 0...most likely the server isn't running.
serverResponse = 'No server';
}
}
String _loginDataAsJsonData(){
return JSON.encode(loginData);
}
}
I need to have access to that loginData['username'] & parsedMap["data"]["ticket"] to be available in the page dashboard.html.
Not really an answer, but to long for a comment:
Your code shows how you send the credentials to the server. So my previous comment still fits. You can't just pass variables to a new page. When a page is loaded this is like a application restart. You can pass values in the URL you redirect to, as cookies if both pages are loaded from the same domain or you can just reload them from the server where you stored them previously. To know that the new page is was requested by the same user you have to use some session handling (like the previously mentioned session cookie). This has nothing to do with Dart or Polymer this is more about how the web works.

How can I login to Meteor with native device Facebook?

Suppose I logged into my device's Facebook authentication, like system Facebook on iOS. I obtain an access token.
How can I use the access token to login to Meteor's Facebook Oauth provider?
To login with Facebook using an access token obtained by another means, like iOS Facebook SDK, define a method on the server that calls the appropriate Accounts method:
$FB = function () {
if (Meteor.isClient) {
throw new Meteor.Error(500, "Cannot run on client.");
}
var args = Array.prototype.slice.call(arguments);
if (args.length === 0) {
return;
}
var path = args[0];
var i = 1;
// Concatenate strings together in args
while (_.isString(args[i])) {
path = path + "/" + args[i];
i++;
}
if (_.isUndefined(path)) {
throw new Meteor.Error(500, 'No Facebook API path provided.');
}
var FB = Meteor.npmRequire('fb');
var fbResponse = Meteor.sync(function (done) {
FB.napi.apply(FB, [path].concat(args.splice(i)).concat([done]));
});
if (fbResponse.error !== null) {
console.error(fbResponse.error.stack);
throw new Meteor.Error(500, "Facebook API error.", {error: fbResponse.error, request: args});
}
return fbResponse.result;
};
Meteor.methods({
/**
* Login to Meteor with a Facebook access token
* #param accessToken Your Facebook access token
* #returns {*}
*/
facebookLoginWithAccessToken: function (accessToken) {
check(accessToken, String);
var serviceData = {
accessToken: accessToken
};
// Confirm that your accessToken is you
try {
var tokenInfo = $FB('debug_token', {
input_token: accessToken,
access_token: Meteor.settings.facebook.appId + '|' + Meteor.settings.facebook.secret
});
} catch (e) {
throw new Meteor.Error(500, 'Facebook login failed. An API error occurred.');
}
if (!tokenInfo.data.is_valid) {
throw new Meteor.Error(503, 'This access token is not valid.');
}
if (tokenInfo.data.app_id !== Meteor.settings.facebook.appId) {
throw new Meteor.Error(503, 'This token is not for this app.');
}
// Force the user id to be the access token's user id
serviceData.id = tokenInfo.data.user_id;
// Returns a token you can use to login
var loginResult = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});
// Login the user
this.setUserId(loginResult.userId);
// Return the token and the user id
return loginResult;
}
}
This code depends on the meteorhacks:npm package. You should call meteor add meteorhacks:npm and have a package.json file with the Facebook node API: { "fb": "0.7.0" }.
If you use demeteorizer to deploy your app, you will have to edit the output package.json and set the scrumptious dependency from "0.0.1" to "0.0.0".
On the client, call the method with the appropriate parameters, and you're logged in!
In Meteor 0.8+, the result of Accounts.updateOrCreateUserFromExternalService has changed to an object containing {userId: ...} and furthermore, no longer has the stamped token.
You can get the accessToken in the Meteor.user() data at Meteor.user().services.facebook.accessToken (be aware this can only be accessed on the server side as the services field is not exposed to the client.
So when a user logs in with facebook on your meteor site these fields would be populated with the user's facebook data. If you check your meteor user's database with mongo or some other gui tool you could see all the fields which you have access to.
Building on DrPangloss' most excellent answer above, combining it with this awesome post: http://meteorhacks.com/extending-meteor-accounts.html
You'll run into some issues using ObjectiveDDP in trying to get the client persist the login. Include the header:
#import "MeteorClient+Private.h"
And manually set the required internals. Soon I'll make a meteorite package and an extension to MyMeteor (https://github.com/premosystems/MyMeteor) but for now it's manual.
loginRequest: {"accessToken":"XXXXXb3Qh6sBADEKeEkzWL2ItDon4bMl5B8WLHZCb3qfL11NR4HKo4TXZAgfXcySav5Y8mavDqZAhZCZCnDDzVbdNmaBAlVZAGENayvuyStkTYHQ554fLadKNz32Dym4wbILisPNLZBjDyZAlfSSgksZCsQFxGPlovaiOjrAFXwBYGFFZAMypT9D4qcZC6kdGH2Xb9V1yHm4h6ugXXXXXX","fbData":{"link":"https://www.facebook.com/app_scoped_user_id/10152179306019999/","id":"10152179306019999","first_name":"users' first name","name":"user's Full Name","gender":"male","last_name":"user's last name","email":"users#email.com","locale":"en_US","timezone":-5,"updated_time":"2014-01-11T23:41:29+0000","verified":true}}
Meteor.startup(
function(){
Accounts.registerLoginHandler(function(loginRequest) {
//there are multiple login handlers in meteor.
//a login request go through all these handlers to find it's login hander
//so in our login handler, we only consider login requests which has admin field
console.log('loginRequest: ' + JSON.stringify(loginRequest));
if(loginRequest.fbData == undefined) {
return undefined;
}
//our authentication logic :)
if(loginRequest.accessToken == undefined) {
return null;
} else {
// TODO: Verfiy that the token from facebook is valid...
// https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.0#checktoken
// graph.facebook.com/debug_token? input_token={token-to-inspect}&access_token={app-token-or-admin-token}
}
//we create a user if not exists, and get the userId
var email = loginRequest.fbData.email || "-" + id + "#facebook.com";
var serviceData = {
id: loginRequest.fbData.id,
accessToken: loginRequest.accessToken,
email: email
};
var options = {
profile: {
name: loginRequest.fbData.name
}
};
var user = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, options);
console.log('Logged in from facebook: ' + user.userId);
//send loggedin user's user id
return {
userId: user.userId
}
});
}
);
This answer could be improved further as we can now directly debug the token from a REST http request using futures. Credit still goes to #DoctorPangloss for the principal steps necessary.
//Roughly like this - I removed it from a try/catch
var future = new Future();
var serviceData = {
accessToken: accessToken,
email: email
};
var input = Meteor.settings.private.facebook.id + '|' + Meteor.settings.private.facebook.secret
var url = "https://graph.facebook.com/debug_token?input_token=" + accessToken + "&access_token=" + input
HTTP.call( 'GET', url, function( error, response ) {
if (error) {
future.throw(new Meteor.Error(503, 'A error validating your login has occured.'));
}
var info = response.data.data
if (!info.is_valid) {
future.throw(new Meteor.Error(503, 'This access token is not valid.'));
}
if (info.app_id !== Meteor.settings.private.facebook.id) {
future.throw(new Meteor.Error(503, 'This token is not for this app.'));
}
// Force the user id to be the access token's user id
serviceData.id = info.user_id;
// Returns a token you can use to login
var user = Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, {});
if(!user.userId){
future.throw(new Meteor.Error(500, "Failed to create user"));
}
//Add email & user details if necessary
Meteor.users.update(user.userId, { $set : { fname : fname, lname : lname }})
Accounts.addEmail(user.userId, email)
//Generate your own access token!
var token = Accounts._generateStampedLoginToken()
Accounts._insertLoginToken(user.userId, token);
// Return the token and the user id
future.return({
'x-user-id' : user.userId,
'x-auth-token' : token.token
})
});
return future.wait();
Use this instead of the JS lib suggested by #DoctorPangloss. Follow the same principles he suggested but this avoids the need to integrate an additional library

Zend Gdata Youtube and auto login

Hello guys I need help in auto login to youtube.com to upload videos "browser-based" (and later get them data to show in a site by api). So basicly I downloaded extension from here http://framework.zend.com/downloads/latest Zend Gdata. And make it work.
It works fine (demos/.../YouTubeVideoApp). But how can i do auto login to youtube without confirmation page ("grant access" \ "deny access")? Currently I use developer key to work with youtube api.
The message of confirmation is
An anonymous application is requesting access to your Google Account for the product(s) listed below.
YouTube
If you grant access, you can revoke access at any time under 'My Account'. The anonymous application will not have access to your password or any other personal information from your Google Account. Learn more
This website has not registered with Google to establish a secure connection for authorization requests. We recommend that you continue the process only if you trust the following destination:
http://somedomain/operations.php
In general I need create connection to youtube (by api) and upload there (using my own account) video without any popups and confirmation pages.
i think all you need is to get a access token and set it to a session value "$_SESSION['sessionToken']". Combination of javascript and PHP will need to do this. previously i always have to grant access or deny it while using Picasa web API but after changes that i described below, grant or access page is no longer needed.
I have not integrated youtube with zend Gdata but have integrated Picasa web Albums using it
make a login using javascript popup and get a token for a needed scope. below is a javascript code. change your scope to youtube data as in this scope for picasa is used.. click function "picasa" on your button onclick.
var OAUTHURL = 'https://accounts.google.com/o/oauth2/auth?';
var VALIDURL = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=';
var SCOPE = 'https://picasaweb.google.com/data';
var CLIENTID = YOUR_CLIENT_ID;
var REDIRECT = 'http://localhost/YOUR_REDIRECT_URL'
var LOGOUT = 'http://accounts.google.com/Logout';
var TYPE = 'token';
var _url = OAUTHURL + 'scope=' + SCOPE + '&client_id=' + CLIENTID + '&redirect_uri=' + REDIRECT + '&response_type=' + TYPE;
var acToken;
var tokenType;
var expiresIn;
var user;
var loggedIn = false;
function picasa() {
var win = window.open(_url, "windowname1", 'width=800, height=600');
var pollTimer = window.setInterval(function() {
console.log(win);
console.log(win.document);
console.log(win.document.URL);
if (win.document.URL.indexOf(REDIRECT) != -1) {
window.clearInterval(pollTimer);
var url = win.document.URL;
acToken = gup(url, 'access_token');
tokenType = gup(url, 'token_type');
expiresIn = gup(url, 'expires_in');
win.close();
validateToken(acToken);
}
}, 500);
}
function validateToken(token) {
$.ajax({
url: VALIDURL + token,
data: null,
success: function(responseText){
//alert(responseText.toSource());
getPicasaAlbums(token);
loggedIn = true;
},
dataType: "jsonp"
});
}
function getPicasaAlbums(token) {
$.ajax({
url: site_url+"ajaxs/getAlbums/picasa/"+token,
data: null,
success: function(response) {
alert("success");
}
});
}
//credits: http://www.netlobo.com/url_query_string_javascript.html
function gup(url, name) {
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\#&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( url );
if( results == null )
return "";
else
return results[1];
}
Here i am making a ajax call in function "getPicasaAlbums" and setting token to a $_session there and after it i am able to get a album listing using zend queries. here is a some code of php file that i am calling using ajax in function "getPicasaAlbums".
function getAlbums($imported_from = '',$token = '') {
//echo $imported_from; //picasa
//echo $token;
$_SESSION['sessionToken'] = $token;// set sessionToken
$client = getAuthSubHttpClient();
$user = "default";
$photos = new Zend_Gdata_Photos($client);
$query = new Zend_Gdata_Photos_UserQuery();
$query->setUser($user);
$userFeed = $photos->getUserFeed(null, $query);
echo "<pre>";print_r($userFeed);echo "</pre>";exit;
}
i think this will help you a little in your task. relpace above "getAlbums" function's code with your youtube zend data code to retrieve data.
good example & referene of a login popup is here
http://www.gethugames.in/blog/2012/04/authentication-and-authorization-for-google-apis-in-javascript-popup-window-tutorial.html

How to post to user wall only 1 time when a user allows my app?

Currently my app posts to the users wall every time they access the app. I only want it to post to the wall one time when they first authorize the app. Then every time they access it afterward it only updates the news feed status.
here is my current code:
// Get User ID
$user = $facebook->getUser();
if ($user) {
try {
// Proceed knowing you have a logged in user who's authenticated.
$access_token = $facebook->getAccessToken();
$vars = array(
'message' => "Message goes here",
'picture' => "image",
'link' => "link here",
'name' => "Name here",
'caption' => "Caption here"
);
$result = $facebook->api('/me/feed', 'post', $vars);
} catch (FacebookApiException $e) {
error_log($e);
$user = null;
}
}
// Login or logout url will be needed depending on current user state.
if ($user) {
$logoutUrl = $facebook->getLogoutUrl();
} else {
$loginUrl = $facebook->getLoginUrl(array('redirect_uri'=> $app_url));
echo "<script type='text/javascript'>";
echo "top.location.href = '{$loginUrl}';";
echo "</script>";
}
What do I need to change in order to make that happen?
You have 2 choices of methods to achieve this behavior.
Utilize the Feed Dialog on the landing page for your users. This will popup a Facebook window prompting your users to share something on their wall. This method requires that you implement the JavaScript SDK as well.
Utilize the PHP SDK and programatically posting a feed story to the /me/feed endpoint. (As you have done in the try-catch block of your code sample).
With regard to only posting on the users first visit you should store in your database a boolean value. When you create a new record for the new user in your database you should include a field called something like first_visit and populate it with a "true" value.
Then when you detect a returning user (that means he is already in your database) you can check to see that the first_visit field is set to "false". Then your post via the PHP SDK can be the result of a conditional expression to test the first_visit value :
...
...
if ($first_visit == 'true'){
$result = $facebook->api('/me/feed', 'post', $vars);
}
An additional solution (not requiring a database) could be something similar to this :
When you so cunningly generate the login URL with the $facebook->getLoginUrl() method for your un-authorized users, you can add a temporary GET parameter to the redirect_uri parameter. Something like :
$redirect_uri = 'https://apps.facebook.com/waffle-ville?new_user=true';
Then your conditional expression for posting to the users wall would look something like this :
...
...
if ($_GET['new_user'] == 'true'){
$result = $facebook->api('/me/feed', 'post', $vars);
}
Don't forget to redirect the user back to the original URL after you have made the post :
var app_url = "https://apps.facebook.com/waffle-ville";
echo "<script type='text/javascript'>";
echo "top.location.href = app_url;";
echo "</script>";
The redirect is also possible with PHP :
$app_url = "https://apps.facebook.com/waffle-ville";
header("Location: {$app_url}");
IMO - Posting to a users wall automagically is a little bit annoying. There is a parameter in your application settings that is called Social Discovery. When this is set to "enabled" a story is automagically created as soon as a user installs your application. I recommend leaving posting to a users wall as an optional user initiated action.
I've figured it out. I created a database to store info and it checks to see if the User ID already exists or not. If it doesn't, then they are placed in the database and a post is made to their wall. If they are in the database, then nothing happens.
<?php
require 'facebook.php';
require 'dbconnect.php';
// Create our application instance
// (replace this with your appId and secret).
$app_id = "APP_ID";
$secret = "APP_SECRET";
$app_url = "APP_URL";
$facebook = new Facebook(array(
'appId' => $app_id,
'secret' => $secret,
'cookie' => true,
));
// Get User ID
$user = $facebook->getUser();
// We may or may not have this data based on whether the user is logged in.
//
// If we have a $user id here, it means we know the user is logged into
// Facebook, but we don't know if the access token is valid. An access
// token is invalid if the user logged out of Facebook.
if ($user) {
try {
// Proceed knowing you have a logged in user who's authenticated.
$user_profile = $facebook->api('/me');
$access_token = $facebook->getAccessToken();
$name = $user_profile['name'];
$birthday = $user_profile['birthday'];
} catch (FacebookApiException $e) {
error_log($e);
$user = null;
}
}
// Login or logout url will be needed depending on current user state.
if ($user) {
$logoutUrl = $facebook->getLogoutUrl();
} else {
$loginUrl = $facebook->getLoginUrl(array('redirect_uri'=> $app_url));
echo "<script type='text/javascript'>top.location.href = '$loginUrl';</script>";
}
//DO NOT EDIT BELOW
$db=mysql_connect($hostname, $dbuser, $pass);
mysql_select_db($database, $db);
//check if user has already signed up before
$insert = true;
$result = mysql_query("SELECT * FROM table_name") or die(mysql_error());
while($row = mysql_fetch_array($result)){
//if user id exists, do not insert
if(( $row['UID'] == $user)){
$insert = false;
}
}
// if new user, insert user details in your mysql table
if($insert){
mysql_query("INSERT INTO table_name (UID, username, userbirthday) VALUES ('$user','$name','$birthday') ") or die(mysql_error());
$access_token = $facebook->getAccessToken();
$vars = array(
'message' => "message goes here",
'picture' => "image",
'link' => "Link here",
'name' => "Name here",
'caption' => "Caption here",
);
$result = $facebook->api('/me/feed', 'post', $vars);
}
?>

Resources