When I submit an auth call to google to I get the popup from the google window, then when I submit my credentials and press submit it forwards on to something like
https://auth.firebase.com/v2/FIREBASEREF/auth/google/callback?state= etc
And then all I get is a blank (blue background) screen.
$('#loginButton').click(function() {
myFirebaseRef.authWithOAuthPopup("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
alert("Login Failed, please try again.")
} else {
console.log("Authenticated successfully with payload:", authData);
myUserID = authData.uid;
}
});
});
The same is also happening when trying to auth with Github, I can press "submit" or "login" etc. and then it just loads a blank page at auth.firebase.com
Any ideas?
Thanks
FWIW if anyone ends up on this issue: I was getting the exact same thing... the oauth popup was blank and not doing anything. I fixed it by removing the async/await from my form handler:
<form onSubmit={this.onSubmit}>
<button type="submit">Sign In with Google</button>
</form>
...
onSubmit = async event => {
await this.auth.signInWithPopup(this.googleProvider)
.then(....
Sometimes this should be an permissions issue related with the domain where you're trying to do the request like localhost or 127.0.0.1.
In that case, Go to Firebase Console -> Authentication -> Sign-In Method then scroll down to Authorized domains and add your current domain from you're trying to sign in e.g. 127.0.0.1.
Note: This is just for dev purposes, in case you want to deploy your app to Prod, you shouldn't have this configuration. For multiples environments you should check here
Another Note: If you're working with Javascript, you might add an event.preventDefault() before to call firebase.authWithOAuthPopup(...) to be able to obtain the result of the promise that return the authWithOAuthPopup function.
My solution was adding inAppBrowser from cordova and it no longer returned blank page.
Related
I have the Webchat (from the Microsoft Bot Framework) running embedded in another application that uses a browser under the hood.
When trying to add Authentication to the bot, I realized that the OAuthCard's Sign-in button doesn't work because is trying to open a blank window (about:blank) that is used to redirect the user to the login page of the identity provider. In the embedded context, the OS doesn't know how to handle the about:blank call. See the image below.
I'm following this example, that it is actually working on the browser:
Add authentication to a bot
Bot authentication example
I want to know if there is a way to change the behavior of the "Sign In" button of the OAuthCard to just open the sign-in URI directly without using the about:blank and redirect technique.
I was able to make it work after finding out that was possible to change the button type of the OAuthCard from "signin" to "openUrl" before the Webchat does the rendering.
There seems to exist a similar issue with Microsoft Teams. Here where I found the clue:
https://github.com/microsoft/botframework-sdk/issues/4768
According to the Webchat reference, it is possible to intersect and change the activities:
https://github.com/microsoft/BotFramework-WebChat/blob/master/docs/API.md#web-chat-api-reference
Here is the solution, that is more like a hack. I hope this would be configurable in the future:
// Change the button type in the OAuthCard to the type of OpenUrl
const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => action => {
if (action.type == "DIRECT_LINE/QUEUE_INCOMING_ACTIVITY" &&
action.payload.activity.hasOwnProperty("attachments") &&
action.payload.activity.attachments[0].contentType === "application/vnd.microsoft.card.oauth") {
action.payload.activity.attachments[0].content.buttons[0].type = "openUrl";
}
return next( action );
});
// Pass the store to the webchat
window.WebChat.renderWebChat({
...,
store
});
Using the instructions for Google sign in located here, We are implementing the "Sign in with Google" button.
This implementation has been successful, however, we want to set a default domain other than gmail.com as this will be used by our Gsuite for education users. This would make it so that users can click the sign in button and just type their username, instead of username#gSuiteDomain.com
When this button is clicked:
The user is redirected to:
How do we make it so the second image defaults to our gSuite domain instead of gmail.com?
I am pretty sure is you set the "hd" parameter to the G Suite domain name that it will work.
Please let us know if that works.
You can't use the simple directions at the location I linked in the question, as it is only for a basic minimum authentication.
For customization, you need to access the full client API here (server side validation can stay the same.)
This allows you to pass information to the init functions:
<script src="https://apis.google.com/js/platform.js?onload=onLoadCallback" async defer></script>
Then:
window.onLoadCallback = function () {
gapi.load('auth2', initSigninV2);
function initSigninV2() {
gapi.auth2.init({
client_id: 'Your_client_id',
hosted_domain: "YourHostedDomain.com"
}).then(function (authInstance) {
if (authInstance != null && authInstance != undefined) {
gapi.signin2.render("IDOfSignInButton", { onsuccess: onSignIn });
}
});
}
The rest is the same as in the original tutorial
So I've got an question about authentication and have been wondering how other people might handle this situation. I'm currently running an Angular app that is built on a Rails API.
So far for authentication I have a form that does a post to the Rails side which logs the user in and then sends them back to the Angular app on success. Once the cookie is set and the user is logged in, I'm able to access a user.json file which contains all the User information one might expect (Id, username, roles, rights, etc). Since verification all happens on Rails, if the user logs out then this information is removed. So the two states look like so...
Logged in
{
id: 99384,
name: "Username",
url: "//www.test.com/profiles/Username",
timezone: null,
rights: [ ],
roles: [
"admin"
],
}
Logged out
{
error: "You need to login or join before continuing."
}
So far I've seen all these millions of different ways to do auth for Angular, but it seems like nothing fits this type of method. So my question is, since the server is handling all of the verification, is there a way to just check if they user.json file is empty (displaying the error message) and if it is send the Angular app to the Rails login page? Is there really any point messing with Cookies, Tokens, etc when I can base it all on the JSON file?
You are already using cookies - the server is setting them. What you have done is a fairly standard way of doing things.
To check the json file, you can do something like this stub shows in your controller:
app.controller('AppControl', function($scope, $http, $location){
// Get the JSON file.
$http.get('/path/to/json/file')
.then(response){
if(response.data.error){
// redirect to login
$location.path('login');
}
else{
$scope.user = response.data;
// your app code here.
}
})
.catch(function (error){
// unable to reach the json file - handle this.
});
});
Of course, you should really move this out into a service so you can re-use it, and also cache the data, rather than getting the user every time you change route/page, but this gives you a vague idea.
EDIT Example factory:
.factory('User', function( $http ){
// Create a user object - this is ultimately what the factory will return.
// it's a singleton, so there will only ever by one instance of it.
var user = {};
// NOTE: I am assigning the "then" function of the login promise to
// "whenLoggedIn" - your controller code is then very easy to read.
user.whenLoggedIn = $http.get('user.json')
.then(function(response){
// Check to see if there is an error.
if (response.data.error !== undefined) {
// You could be more thorough with this check to determine the
// correct action (examine the error)
user.loggedIn = false;
}
else {
// the user is logged in
user.loggedIn = true;
user.details = response.data;
return user;
}
}).then; // <-- make sure you understand why that .then is there.
return user;
})
Usage in the controller
.controller('ExampleController', function($scope, User){
// It's handy to have the user on the scope - you can use it in your markup
// like I have with ng-show on index.html.
$scope.User = User;
// Do stuff only if the user is loggedin.
// See how neat this is because of the use of the .then function
User.whenLoggedIn( function (user){
console.log(user.details.name + " is logged in");
});
});
Because it's on the scope, we can do this in the html:
<body ng-controller="ExampleController">
<h1 ng-show="User.loggedIn == null">Logging in..</h1>
<h1 ng-show="User.loggedIn == true">Logged in as {{ User.details.name }}</h1>
<h1 ng-show="User.loggedIn == false">Not logged in</h1>
</body>
Here is an example on plunker where this is working.
Note the following:
If the user is/was already logged in, when you inject the service in the future, it won't check the file again. You could create other methods on the service that would re-check the file, and also log the user out, back in, etc. I will leave that up to you.
There are other ways to do this - this is just one possible option!
This might be obvious, but it's always worth saying. You need to primarily handle authentication and security on the server side. The client side is just user experience, and makes sure the user doesn't see confusing or conflicting screens.
I have a simple JQM site I'm working on. I'm trying to validate the availability of a username on the fly in a form. I'm using jquery $.get() ajax to return "success" or "fail" however the return data is being replace with the code of the previous page.
$(document).on('pageinit', function () {
// check to see if username is available
$("#username").change(function() {
$.get("controller.php", { action: "check_username", username: username }, function(data) {
console.log(data);
}
});
The controller.php is checking for availability of the username and return "pass" or "fail" When I do the console.log(data) which I'm expecting to be pass or fail, it's logging out the code from the previous page??
I'm thinking maybe it's a JQM caching issue so I tried to disable cache with no effect. I was orginally using a JQM dialog box to display the form. Thinking that had something to do with it I pulled that out and loaded a straight link. That didn't fix it so I tried to load the page directly using
$.mobile.changePage( "user-new.php", { reloadPage: true});
I am stumped. Why would a $.get ajax call return data be returning code from the previous page?
Here's a face palm! My controller was authenticating and kicking it back out to a login page. Apparently php redirects act funky with ajax return data. Rather then returning the login page code it was returning the previous page code. I Removed the authentication and it works fine. Unbelievable! I'm going to go work at a gas station or something :)
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.