I am currently building a custom widget which has links in it. If a link is clicked, the newly opened tab has no authorization. It says the unauthorized access is forbidden. Client authorization required.
I am building the link via javascript.
var newLinkButton = (url, text) => $('<a>', {
class: 'btn btn-default btn-xs',
href: url,
role: 'button',
text: text
});
and set the base target in the header to _blank
<base target="_blank" />
The TFS sets access right equal to the ones from the iframe in which it was called. That is anonym. How do I tell TFS to take an already existing authorization?
You need to redirect to another page (not open a new page) from current page. Change _blank to _top.
<base target="_top" />
Related
I was able to connect with to Intuit using the Minimul/QboApi gem and get the "Connect to Quickbooks" button working with oauth2 based on the example provided on Github. However neither the gem nor the samples show how to implement single sign on with Intuit. In the example provided by Minimul, the Connect To Quickbooks button is produced by intuit's javascript found at https://appcenter.intuit.com/Content/IA/intuit.ipp.anywhere-1.3.5.js
and a setup script and the tag . The tag appears to have been deprecated. Or at least, it doesn't appear to do anything other than produce the button with the right text and logo on it.
But bottom line, I have been unable to find any documentation on the ipp.anywhere.js package, and not even sure if i's meant to used with oauth2 since it's not mentioned anywhere. I believe that the connect to intuit button does the right things, but the guidelines seem pretty strict about what that the button needs to say the right thing and have th eright logo or they will reject it in the store. They also seem to suggest that users are much more likely to try something if an SSO with Intuit workflow is enabled. Any help appreciated.
After some further work, I figured out a solution that can create a 'log in with Inuit button' , although it's a bit of a javascript hack. First, I determined that the only thing I really needed to change was the button image. In other respects the code behind ` works fine for either a "login with intuit" or "connect to intuit work flow" . The only problem is the button image.
Here is the code (adapted from Minimul/QboApi) to get access and oauth2 refresh tokens via a "Connect to Quickbooks" button.
Setup in the controller code in login or sessions controller:
def new
#app_center = QboApi::APP_CENTER_BASE # "https://appcenter.intuit.com"
state= SecureRandom.uuid.to_s
intuit_id = ENV["CLIENT_ID"]
intuit_secret = ENV["CLIENT_SECRET"]
client = Rack::OAuth2::Client.new(
identifier: intuit_id,
secret: intuit_secret,
redirect_uri: ENV["OAUTH_REDIRECT_URL"],
uthorization_endpoint:"https://appcenter.intuit.com/connect/oauth2",
token_endpoint: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
response_type: "code"
)
#make sure to include at least "openid profile email"
#in the scope to you can retrieve user info.
#uri = client.authorization_uri(scope: 'com.intuit.quickbooks.accounting openid profile email phone address', state: state)
end
Here is the code required to generate the button on the view. (The view needs to load jquery as well in order for the script to work.)
<script type="text/javascript" src="<%= #app_center %>/Content/IA/intuit.ipp.anywhere-1.3.5.js">
</script>
<script>
intuit.ipp.anywhere.setup({
grantUrl: "<%== #uri %>",
datasources: {
quickbooks: true,
payments: false
}
});
</script>
<div>
<ipp:connecttointuit></ipp:connecttointuit>
This code produces the following html on the page delivered to the client:
<ipp:connecttointuit>
Connect with QuickBooks
</ipp:connecttointuit>
This code produces a button with the Connect with QuickBooks image, and an event handler inside intuit.ipp.anywhere-1.3.5.js attaches itself to the click event.
The problem is that the button is styled by the class=intuitPlatformConnectButton attribute inside the generated <a> tag, so if you want a "login with intuit button instead of a connect with intuit button the class on the anchor needs to be changed to class='intuitPlatformLoginButtonHorizontal' but still needs to attach to the event handler defined for <ipp:connecttointuit>. The best solution that doesn't require mucking with intuit.ipp.anywhere is to create the connect button and hide it, and then create another tag styled with class=intuitPlatformLoginButtonHorizontal whose click event calls click on the hidden connect button. I use AngularJs on my login page, so I handle the click with ng-click, but it could be done simply with jquery alone.
new.html.erb:
<div>
</div>
<div>
<ipp:connecttointuit id="connectToIntuit" ng-hide="true">< </ipp:connecttointuit>
</div>
and the controller code:
$scope.intuit_login = function() {
let el = angular.element("#connectToIntuit:first-child")
el[0].firstChild.click();
}
This will result in a redirect upon authentication to the supplied redirect url, where you can use openid to get the user credentials.
I am including GTM in my <head> with the following:
<!-- Google Tag Manager -->
<script>
document.addEventListener('turbolinks:load', function(event) {
var url = event.data.url;
dataLayer.push({
'event':'pageView',
'virtualUrl': url
});
});
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');
</script>
<!-- End Google Tag Manager -->
And in the <body> with
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
On my page, I have a link_to helper that deletes an image from the database using rails-ujs.
<%= link_to "Delete", destroy_image_path(image), remote: true, method: "delete" %>
When I remove the GTM code from the page, the Delete link performs as expected.
However, with the GTM code in place, clicking the link results in a Routing Error as it attempts to make a GET request.
No route matches [GET] "/..."
It seems to me that GTM is interfering with rails-ujs but I'm unsure of the best way to run this down.
Edit: I have tried this code with and without the turbolinks:load listener with the same failed result.
I had to do the following:
(function($) {
var handlerElement = document.body;
var selector = 'a[data-method=put],a[data-method=patch],a[data-method=post],a[data-method=delete]';
var events = ['click', 'mousedown', 'mouseup'];
var namespacedEvents = $.map(events, function(event) {
return [event, 'disable_method_links'].join('.');
});
var handlers;
$(handlerElement).on(namespacedEvents.join(' '), selector, function(e) {
e.preventDefault();
});
})(jQuery);
Basically, GTM adds handlers for all of the target anchor tags, delegated from the document node, and wraps the existing handlers, checking whether they prevent the default action / stop propagation, I'm not sure exactly what's causing it, but I think it's that GTM is loading before my events are registered, and not detecting that the default action is prevented, so delegating from the body tag and preventing the default action there ensures that your event is hit before any GTM events (which, as mentioned, are registered on the document node).
I'm not sure whether the mousedown/mouseup events are necessary, but I'm posting this in a hurry before I leave for a long weekend so don't have time to check.
Also, I added a defer tag to the script tag that GTM generates and injects into the document, but I don't think that is necessary either, again, haven't tested.
Also see: https://www.simoahava.com/gtm-tips/fix-problems-with-gtm-listeners/
I'm working on adding a Google Sign-In Button to a website. In the docs, Google offers two options, a "basic" button:
https://developers.google.com/identity/sign-in/web/sign-in
and a "custom" button using signin2.render():
https://developers.google.com/identity/sign-in/web/build-button
The problem I'm encountering is that the two buttons exhibit different behavior. If I sign in with either button, the button's "title" changes from "Sign In" to "Signed In" to reflect the sign-in status. However, if I now refresh the page, then the basic button retains the title "Signed In", whereas the custom button changes its title back to "Sign In", suggesting (incorrectly) that the sign-in status has changed through the page refresh.
If I manually check the sign-in status post-refresh in the browser console by running:
auth = gapi.auth2.getAuthInstance()
auth.isSignedIn.get()
I get true as a return, showing that the refresh indeed did not alter the sign-in status, contrary to the change in the button's title.
So my question is: how can I get the custom button to behave like the basic button, so that its title does not change on refresh? Another (related, I assume) behavior of the basic button that I like is that the button's "onsuccess" callback gets called on each page load (if the user is signed in), whereas the custom button does not do this. So I'd also like to change this behavior on the custom button to match that of the basic button. Any suggestions would be greatly appreciated!
The parameters I'm passing to render look as follows:
function renderButton() {
gapi.signin2.render('mybutton', {
'scope': 'profile email',
'width': 125,
'height': 40,
'longtitle': false,
'theme': 'light',
'onsuccess': onSuccess,
'onfailure': onFailure
});
}
Could you provide params that you're passing to the button? Could you confirm that there aren't any errors in JS console and there aren't any 400/403/404/4xx requests?
I've tested this functionality using following code and it seems to work perfectly fine (you have to replace YOUR_CLIENT_ID with your actual client_id).
<head>
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID">
</head>
<body>
<script>
function onSuccess(googleUser) {
console.log('onSuccess!');
}
function onCustomSuccess(googleUser) {
console.log('onCustomSignIn!');
}
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
function onLoad() {
gapi.signin2.render('custom_signin_button', {
'onsuccess': onCustomSuccess
});
}
</script>
<div class="g-signin2" data-onsuccess="onSuccess"></div>
<div id="custom_signin_button"></div>
Sign out
<script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
</body>
</html>
Edit:
Declaring base scope as head's meta tag is the best solution.
<meta name="google-signin-scope" content="profile email">
On my landing page I want users to be able to Share on Facebook and Share on Twitter that page with a particular message. I would like to be able to know when they have actually post it on Facebook and/or Twitter (any way to get the callback).
What is the correct way to add a Facebook Share link in Rails? I want to have my own button.
I have taken a look at Koala but I am wondering if it is too much because I just want to be able to Share that landing page. I don't want to provide any kind of authentication or anything more complex. The only tricky part is that I need to know when they have actually shared.
Thanks
You can use external service like sharethis,addthis with some option,
For facebook you can use own way of sharing for this by registering with facebook app
consider following example
<a href="#" onclick='postToFeed("<%= image_url%>","<%= somte text %>"); return false;'>
<%= image_tag("btn_fb.png" , :alt=>"Facebook") %>
</a>
<script type="text/javascript">
FB.init({appId: APP_ID", status: true, cookie: true});
function postToFeed(img,name) {
// calling the API ...
var obj = {
method: 'feed',
// redirect_uri: 'http://localhost:3000',
link: 'http://url/',
picture: img,
name: 'NAME',
caption: 'CAPTION',
description: DESCRIPTION
};
function callback(response) {
document.getElementById('msg').innerHTML = "Post ID: " + response['post_id'];
}
FB.ui(obj, callback);
}
</script>
In Ryan's Railscast on Facebook authorization, he adds some Facebook SDK javascript at the end to "degrade facebook client side authorization with server side authorization." However, I do not see the use of it. If we already set up the authorization from the server side using omniauth, why do we have to add the client-side authorization again? What difference does it make?
The referenced javascript code is (From the linked Railscast):
jQuery ->
$('body').prepend('<div id="fb-root"></div>')
$.ajax
url: "#{window.location.protocol}//connect.facebook.net/en_US/all.js"
dataType: 'script'
cache: true
window.fbAsyncInit = ->
FB.init(appId: '<%= ENV["FACEBOOK_APP_ID"] %>', cookie: true)
$('#sign_in').click (e) ->
e.preventDefault()
FB.login (response) ->
window.location = '/auth/facebook/callback' if response.authResponse
$('#sign_out').click (e) ->
FB.getLoginStatus (response) ->
FB.logout() if response.authResponse
true
UPDATE:
One of the reasons we need to integrate FB.login authorization with the server-side authorization might be that the Omniauth server-side authorization does NOT work if it's accessed within the Facebook iFrame. If the user accesses the application for the first time, the application must ask for permissions; however, oAuth permission dialog cannot be loaded within the iFrame to prevent clickjacking. Calling FB.login can avoid such problem, because it will show the permission box as a popup(Omniauth popup option will not work).
So now I have a genuine reason to integrate client-side authorization, but the code from Railscasts does not work with my current settings. I've chosen to do it the following way.
Right now, I have the following script in my application.html.erb:
<script>
// Additional JS functions here
window.fbAsyncInit = function() {
FB.init({
appId : <%= ENV['FACEBOOK_KEY'] %>, // App ID
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
};
// 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>
And in my view, I have the following link invoking the Facebook log in action:
<%= link_to 'log in with facebook', '/auth/facebook', id: 'fb_log_in_link' %>
I add the following script to the view page where I have the login link.
function login() {
FB.login(function(response) {
if (response.authResponse) {
window.location = '/auth/facebook/callback'
}
});
}
Also, I need to change the link to call the function instead of directing to /auth/facebook/
<%= link_to_function 'log in with facebook', 'login()' %>
Done! The server-side and client-side authorization are fully integrated. Since I was still confused after watching Ryan's Railscast, I want to add a little bit of explanation for those who might be also confused.
The way this works:
Facebook SDK is initailized when the while the page is loaded.
The user clicks the "log in with Facebook" link.
FB.login function is called by the link, and the user goes through all the permissions process (e.g. permission dialog showing up asking for the user's permissions).
Then, the user is directed to /auth/facebook/callback. From routes.rb we have the line match 'auth/:provider/callback', to: 'sessions#create'. Therefore, now the server will either create a new user or simply create a session if the user has already registered before.
Done! The user is logged in.
Merging server-side and client-side authorization has two major advantages:
1. If the user is logged into the application either inside Facebook(via appcenter) he will be logged into the application outside Facebook as well. Vice versa, if the user logs in outside Facebook, he will be logged in automatically if he accesses it within Facebook after.
2. Logging in with /auth/facebook does not work if the user logs in within Facebook iFrame. To prevent clickjacking Facebook prohibits prompting users to auth permissions dialog within Facebook iFrame. The only way to avoid this is to open the dialog in a separate popup, and logging in with FB.login solves the problem.
the short answer is - you don't.
you can choose between client side login (via javascript SDK) and server side login using omniauth.
the disadventage of server-side login is overloading the server for a call you can do from the client.
the advantage is that usually the token is longer (3 months token and not 1-2 hours like client side).
i suggest combine the two. use the client side for initial login, once you do that have an async call from the server side for extended token (only if you have to).
It just says,
Facebook provides a JavaScript SDK that we can use to authenticate a user on the client-side so that it doesn’t look to them like they’ve left our application then returned.
It means that this is for the client side understanding that when user returned from the application, it doesn't look like that they have indeed left it.