I'm having issues logging out using the Koala Gem, and wondering whether they're related.
Below is my Javascript code:
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '310521258992725', // App ID
channelUrl : '//localhost:3000/channel', // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
// Additional initialization code here
// whenever the user logs in, we refresh the page
FB.Event.subscribe('auth.login', function() {
setTimeout('document.location.reload()',0);
});
FB.logout(function(response) {
FB.Auth.setAuthResponse(null, 'unknown');
setTimeout('document.location.reload()',0);
});
};
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
}(document));
</script>
Below is my Facebook tag:
<div id="fb-root"></div>
My logout code:
Logout
The login works perfectly and I can execute api calls no problem. However, after I logout, I get the following error:
OAuthException: Error validating access token: The session is invalid because the user logged out. app/controllers/application_controller.rb:58: in 'fbookinvite_check'
Below is my fbookinvite_check code:
def fbookinvite_check
unless #facebook_cookies.nil?
#access_token = #facebook_cookies["access_token"]
#graph = Koala::Facebook::GraphAPI.new(#access_token)
if !#graph.nil? == true
#friends = #graph.get_object("/me/friends")
end
end
end
The problem seems to be that the cookie is that the access token is invalidated, #graph is not showing as nil after the redirect. If I refresh, then the page loads no problem, but I get the error when I log out.
Perhaps there's a way to catch the #graph.get_object error without shutting down the application?
Any suggestions would be greatly appreciated!!
Yes, just wrap your fbookinvite_check in a begin/rescue where you rescue from OAuthException and then return something sane for your application.
You answered your own question:
Perhaps there's a way to catch the #graph.get_object error without
shutting down the application?
Wrap your logic in a begin/rescue block like so:
def fbookinvite_check
begin
unless #facebook_cookies.nil?
#access_token = #facebook_cookies["access_token"]
#graph = Koala::Facebook::GraphAPI.new(#access_token)
if !#graph.nil? == true
#friends = #graph.get_object("/me/friends")
end
end
rescue OAuthException => ex
# handle the exception if you need to, or just ignore it if thats ok too
end
end
Related
I have implemented PayPal checkout API in my rails application by using the SmartButtons and by creating the order in the server-side.
I have used the payouts-ruby-sdk gem and my code is as follows:-
index.html.erb
<!-- Set up a container element for the button -->
<div id="paypal-button-container"></div>
<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id=xyz¤cy=USD"></script>
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Call your server to set up the transaction
createOrder: function(data, actions) {
return fetch('/orders', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
return orderData.orderID;
});
},
// Call your server to finalize the transaction
onApprove: function(data, actions) {
return fetch('/orders/' + data.orderID + '/capture', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(orderData) {
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show a success / thank you message
// Your server defines the structure of 'orderData', which may differ
var errorDetail = Array.isArray(orderData.details) && orderData.details[0];
if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
// Recoverable state, see: "Handle Funding Failures"
// https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
return actions.restart();
}
if (errorDetail) {
var msg = 'Sorry, your transaction could not be processed.';
if (errorDetail.description) msg += '\n\n' + errorDetail.description;
if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
// Show a failure message
return alert(msg);
}
// Show a success message to the buyer
alert('Transaction completed');
});
}
}).render('#paypal-button-container');
</script>
orders_controller.rb
class OrdersController < ApplicationController
skip_before_action :verify_authenticity_token
def index
end
def create
# Creating Access Token for Sandbox
client_id = 'xyz'
client_secret = 'abc'
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
request = PayPalCheckoutSdk::Orders::OrdersCreateRequest::new
request.request_body({
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "10.00"
}
}
]
})
begin
# Call API with your client and get a response for your call
# debugger
response = client.execute(request)
puts response.result.id
render json: {success: true, orderID: response.result.id}
rescue PayPalHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
end
def execute_payment
client_id = 'xyz'
client_secret = 'abc'
# Creating an environment
environment = PayPal::SandboxEnvironment.new(client_id, client_secret)
client = PayPal::PayPalHttpClient.new(environment)
request = PayPalCheckoutSdk::Orders::OrdersCaptureRequest::new(session[:orderID])
begin
# Call API with your client and get a response for your call
response = client.execute(request)
# If call returns body in response, you can get the deserialized version from the result attribute of the response
order = response.result
puts order
rescue PayPalHttp::HttpError => ioe
# Something went wrong server-side
puts ioe.status_code
puts ioe.headers["debug_id"]
end
end
end
Now I want to implement the Paypal's Payouts API and I know that paypal-ruby-sdk is available for it but I am confused where to fit this code and how to integrate it with the front end. Any ideas? Thanks in advance :)
Your code above is Checkout code, for both front-end (JavaScript), and back-end (Ruby).
Payouts has nothing to do with Checkout, neither front-end Checkout nor back-end Checkout.
Payouts is strictly a backend API operation, where you send money from your account to another account.
Payouts does not connect to any front-end UI. You can build your own UI to trigger a payout, if you need one. Presumably you know who you want to send money from your account to, and what process should trigger this action.
I'm using angularjs for the front end and rails + devise for authentication on the backend.
On the front end I have added a responseInterceptor to redirect to the /#/sign_in page upon any 401 response from any xhr request and display a growl style pop-up message using toastr.
App.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('securityInterceptor');
}]);
App.factory('securityInterceptor', ['$injector', '$location', '$cookieStore', function ($injector,$location,$cookieStore) {
return function(promise) {
var $http = $injector.get('$http');
return promise.then(null, function(response){
if (response.status === 401) {
$cookieStore.remove('_angular_devise_user');
toastr.warning('You are logged out');
$location.path('/#/sign_in');
}
});
};
});
My problem is, when I click on a page that loads several xhr requests during the controllers initialization, for example:
var products = Product.query();
var categories = Category.query();
var variations = Variation.query();
These are needed for various navigation components and they all fire off in parallel, resulting in several duplicate growl-style messages.
Is there a way to make angular quit on the first 401 and stop execution of the rest of the controller from within the interceptor? In a traditional rails app, there would be a "before_filter" that stops regular execution, preventing the page and queries from loading... what's the best way to do this in angular?
I've been pondering about this problem for my own apps too. A sketch of my thoughts (NOT REAL IMPLEMENTATION, SO BEWARE):
A userData service keeps track of whether the user is logged in + other information (e.g. user name, real user name etc):
App.service("userData", function() {
var currentData = {
loggedIn: false
};
function getCurrent() {
return currentData;
}
// called when the user logs in with the user data
function loggedIn(userData) {
// the object is REPLACED to avoid race conditions, see explanation below
currentData = angular.extend({loggedIn: true}, userData);
}
return {
getCurrent: getCurrent,
loggedIn: loggedIn
};
});
The interceptors keep track of the currentData. If an interceptor receives HTTP 401 and the loggedIn flag is true, it changes the flag to false and redirects to the login view. If an interceptor receives HTTP 401 and the loggedIn flag is false, it does nothing besides rejecting the request, because another interceptor has done the view redirection.
When the user logs in, the currentData is replaced, so as to avoid situations with delayed responses (e.g. call1 and call2 are initiated, call1 responds 401; call2 also results in 401, but the delivery of the actual response is delayed; then the user logs in again; then call2 receives its 401; the second 401 should not overwrite the current state)
App.config(["$provide", "$httpProvider", function($provide, $httpProvider) {
$provide.factory("myHttpInterceptor", ["$q", "userData", "$cookieStore", "toastr", "$location",
function($q, userData, $cookieStore, toastr, $location) {
return {
request: function(config) {
config.currentUserData = userData.getCurrent();
return config;
},
responseError: function(rejection) {
if( rejection && rejection.status === 401 && rejection.config && rejection.config.currentUserData && rejection.config.currentUserData.loggedIn ) {
rejection.config.currentUserData.loggedIn = false;
$cookieStore.remove('_angular_devise_user');
toastr.warning('You are logged out');
$location.path('/#/sign_in');
}
return $q.reject(rejection);
}
};
}
]);
$httpProvider.interceptors.push("myHttpInterceptor");
});
Also note I am using the newer way to register interceptors, as $httpProvider.responseInterceptors seems to be deprecated.
What is the Authentication Strategy for opening new (tab) Window for Oauth Authentication and returning to previous tab (logged In) upon successful authentication?
I am using passportjs authentication strategies for Twitter, Facebook and Google. But all of that does authentication in the same window-tab. Is there a predefined strategy which I can follow to do the above?
I can open the permission window in new account using a(target="_blank"), but it does not return to previous tab upon account authentication (by user).
Edit (based on following answer)
Login Page looks like:-
!!!
html
head
script.(type='text/javascript')
function newWindow (auth_target) {
authWindow = window.open(auth_target);
var authenticationSuccessUrl = "/home"
authWindow.onClose = function () {
alert('onClose called.');
authWindow.close();
window.location = authenticationSuccessUrl;
}
}
body
button(type="button", onclick="newWindow('auth/google');").btnLogin with Gmail
For New Window (home) i wrote:-
!!!
html
head
script(type='text/javascript').
function onAuthSuccess() {
authWindow=window.location;
window.onClose();
}
body(onload='onAuthSuccess();')
Its not a full code i was suppose to write, but even this does not work. If the Home page is called after an intermediate authentication window (by google, for password entry) the above code does not work, and window does not close.
// just as some more information, ignore if not necessary
Also please note, that,
window.close()
works only if the parent window that triggered it open is open. If the current window is a standalone the above window.close() will not close the window. Likewise, the above Home page code fails to close current window.
When creating the new window for authentication you can get the reference to the window object.
var authWindow = window.open("location","");
In the authentication tab you on authentication success you can call a method which would close the window and set a property or call a method on the source window suggesting that authentication is complete.
var authenticationSuccessUrl = "http://auth-sucess.url"
authWindow.onClose = function () {
authWindow.close();
window.location = authenticationSuccessUrl;
}
On the authentication window javascript:
var onAuthSuccess = function (){
window.onClose();
}
Be sure to remove the reference to the authWindow at the end of processing. Having window objects as references could potentially lead to memory leaks in the application.
Here is how I see the code can be changed as: This would just create one popup window and it will get closed after authentication.
You have Login page code as :
!!!
html
head
script.(type='text/javascript')
function newWindow (auth_target) {
authWindow = window.open(auth_target);
authWindow.onClose = function (authenticationSuccessUrl) {
alert('onClose called.');
authWindow.close();
window.location = authenticationSuccessUrl;
}
}
body
button(type="button", onclick="newWindow('auth/google');").btnLogin with Gmail
The newWindow('auth/google') should have the following code.
!!!
html
head
script(type='text/javascript').
function onAuthSuccess(authSuccessUrl) {
window.onClose(authSuccessUrl);
}
function ajaxPost() {
var uname = $("#name");
var pass = $("#password");
$.ajax({
url: "/auth/google/",
type: "POST",
data: {username : uname, password: pass}
}).success(function(data){
var redirectUrl = data.url; // or "/home" as you had mentioned
onAuthSuccess(redirectUrl);
});
}
body
p UserName:
input#name(type="text", name="username")
p Password:
input#password(type="text", name="password")
p: button(type="submit", onclick="ajaxPost()").btnLogin
If you can post through ajax to the google authentication service and get the the success response you can use this method.
Hope this works out well for you.
I'm trying to create a facebook service for Angular so I can more easily test code that needs to use the Facebook JS SDK and Graph API for stuff.
Here's what I have so far:
app.factory('facebook', function() {
return FB;
});
window.fbAsyncInit = function () {
FB.init({
appId: 'SOME_APP_ID_HERE', // App ID
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
xfbml: true, // parse XFBML
oauth: true
});
};
// Load the SDK Asynchronously
(function (d) {
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) { return; }
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
})(document);
Now, I know that the actual Facebook SDK part is working... but in my controller the reference is always null.
in my controller I just have something like this:
function FooCtrl($scope, facebook) {
facebook.getLoginStatus(function(response) {
if (response.status === 'connected') {
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
// do something
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
// the user isn't logged in to Facebook.
}
});
}
Angular then gripes that it can't find a facebookProvider. Any ideas on how I can accomplish this?
Enclose your factory function with array brackets like below
app.factory('facebook', [function() {
return FB;
}]);
API docs are not clear enough. Point of having array brackets is that you can specify dependencies. It will be injected on creation of your service with AUTO.$inject. But since you don't have dependencies it will skip that task :)
Anyway, if you need dependencies you can request them like this
app.factory('facebook', ["$log", function($someCrazyLoggerService){
$someCrazyLoggerService.log("I'm Auto Injected crazy Logger");
}]);
you should take a look at this Facebook module I wrote.
First use the FacebookProvider on your app config call, something as FacebookProvider.init('yourFacebookAppIdHere');, you could also configure other settings too, and then on your controllers use the Facebook service and register to events and call methods asyncrhonously ;)
https://github.com/ciul/angularjs-facebook
I'm running a sample jQueryMobile app that runs in PhoneGap. The problem is that it doesn't run the getJSON callback to retrieve data, shown below:
$( function()
{
$('#searchButton').click(function()
{
alert("search clicked"); <== this alert works
var url = "http://api.alternativeto.net/software/firefox?callback=?";
$.getJSON(url, function(data) <== this should be, but isn't being called
{
alert("function data called"); <== so this alert doesn't show.
} // end function (data)
); // end getJSON
alert("getJSON call completed"); <== and this alert works
} // end search click.function()
); // end $(
The "search clicked" and "getJSON call completed" alerts both work. The returned JSON I get from entering the url in a browser is valid.
The test code is from this URL:
http://wiki.phonegap.com/w/page/36868306/UI%20Development%20using%20jQueryMobile
Is there anything else I can check?
I don't believe you need the ?callback=? in your url since you can make cross-domain requests from an app.
I have had success with:
$.getJSON('http://google.com/', function (data) {console.log(data);});
..which logged the HTML of Google's homepage.