catch errors for firebase login and display to the user with two-way binging not working - firebase-authentication

I have a very simple straight-forward login template, and I'm using the Firebase to centralize login options.
[part1] Problem is, for some reason, most of the browser except Chrome - receiving a error:
This browser is not supported or 3rd party cookies and data may be disabled
Do I have any firebase configuration issues or something ? because I don't see any posts about these issues - and I can't be the only one experiencing this... ( only Chrome works w/o issues )
I can't start telling people to configure their browser?! ...
[part 2] at least, I'm trying to catch those errors - and display them to the user, otherwise, he has no idea what's the problem and it seems that the app isn't doing anything ( popup opens then close and that's it )
login.ts
verifyErrorMsg() {
console.log("this.loginError:", this.loginError);
}
loginWithGoogle() {
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
.then(userData => {
console.log("success - moving to main page");
}).catch(error => {
this.loginError = error;
this.verifyErrorMsg(); // show the real error, but in console
});
}
^^^ there's no this issue here .. verified that this is the real object.
template.html
<button ion-button (click)="verifyErrorMsg()">
Check login error value
</button>
<p color="danger">
{{ loginError.message }}
</p>
The funny/strange part here is, when I click on my test button to verify what's the real value of this very moment - then suddenly the <p> gets update, even though the function verifyErrorMsg only prints to console ...
I guess it's a cycle or digest kind of thing .. but I'm not sure what needs to be done here ... ?

Related

API Post from Vue to Cake Backend fails on reload

API Post from Vue to Cake Backend fails in certain conditions of reloading FrontEnd, BackEnd, browser and/ or Computer..
I am developing an app with Vue.js as Frontend Framework and CakePHP 3.8 as Backend Framework. I have set up a few Forms in Vue from which I am making POST Requests to several Cake Backend Endpoints. My GET requests are working fine..
In certain conditions I get an error message. This error message disappears once I remove some code, try again, and then put back the original code and try again.. Error message says there is a CORS block, but it says so with all errors, also with spelling errors. So the error message is totally in this case. So with exactly the same code the request works after removing, retrying, and putting back..
Root Cause & Solution
Important thing helping me in finding the solution was realising that the Console was giving the wrong error (CORS). The error log in CakePHP was a lot clearer. This one gave an error Error: [PDOException] SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null (Cake error.log). So the data written to database could not be saved to the database.. The error was in handling the POST data on the Cake Backend.
The solution of the problem was to first perform the request data check before making the save.. if($data){}
***What I think is the problem is connected to the time lag in establishing first connection with the Back-End.. Wants to immediately make the save without having established the data.. Apparently this only happens when starting up the system. ***
Does anyone have a better explanation for this?
Vue Front-End Code:
<template>
<div id="addpacklist">
{{post_data}}
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<form v-on:submit.prevent="addPackinglist">
<h2>Add PackingList</h2>
<label for="name">Name:</label>
<input type="text" id="name" v-model="name"><br>
<input type="submit" value="Submit">
</form>
</div>
</template>
<script>
var data = [];
import $ from 'jquery';
// you will use v-model & data on edits..
export default{
name :'addpacklist',
data(){
return{
errors: [],
name: '',
post_data: '',
}
},
methods: {
addPackinglist(){
this.errors = [];
// form validation
if(!this.name){
this.errors.push('Name required.');
}
if (this.name.length > 10){
this.errors.push('Name maximum 10 Characters');
}
if(this.name && this.name.length < 11){
// cannot use associative arrays in Javascript : does not exist!!
data[0] = this.name;
console.log(data);
fetch('http://wampprojects/holidays_backend/packinglists/add', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(json_data => this.post_data = json_data)
.catch(error => {
console.log("error");
});
}
},
},
}
</script>
Cake Back-End Code:
public function add(){
$data = $this->request->data;
$sendback = "";
// build up data object and save to database
$packinglist = $this->Packinglists->newEntity();
// if you do not perform this check you will get an error
// I think it has something to do with establishing the first link from Front-End to Back-End (Session)
// THIS IS WHAT I ADDED TO MAKE IT WORK!!
if($data){
$packinglist->name = $data[0];
// something wrong with saving entity
if($this->Packinglists->save($packinglist)){
$sendback = "save success";
}else{
$sendback = "save failed";
};
}
// no automatic view, only data returned
$this->autoRender = false;
return $this->response
->withType('application/json')
->withStringBody(json_encode($data));
}
Some scenario’s I logged..
When stopping one element and keeping browser on..
**When stopping & restarting the local dev server for Vue it still works. (ok)
**When stopping & restarting internet still works. (ok)
**When stopping & restarting wamp server still works.. (ok)
**When stopping & starting wamp server still works.. (ok)
When stopping one element & shutting browser down..
**Stop Wamp & restart (ok)
**Stop Wamp & start (ok)
**Stop Vue & restart (ok)
**Stop Internet & restart (ok)
When stopping & (re)starting Vue & Wamp & keeping browser on..
**When first restarting Wamp, then Vue.. (ok)
**When first starting back up Wamp, then Vue. (ok)
**When first restarting Vue with “npm run serve”.. Then start all services wamp.. (nok)
**When first restarting Vue.. Then restart all services wamp.. (ok)
When stopping & (re)starting Vue & Wamp & shutting browser off..
**First restarting Wamp, then Vue, then browser.. (nok)
**First start Wamp, then Vue, then browser.. (ok)
**First start Vue, then restart Wamp, then browser (ok)
**First start Vue, then start Wamp, then browser (nok)
When shutting down computer
**First start up Vue, then Wamp, then browser (nok)
**First start up Wamp, then Vue, then browser (nok)
Root Cause & Solution
Important thing helping me in finding the solution was realising that the Console was giving the wrong error (CORS). The error log in CakePHP was a lot clearer. This one gave an error Error: [PDOException] SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null (Cake error.log). So the data written to database could not be saved to the database.. The error was in handling the POST data on the Cake Backend.
The solution of the problem was to first perform the request data check before making the save.. if($data){}
Thank you -ndm- for the in detail explanation
The resulting error can/will (depending on your PHP installation's output_buffering setting) immediately produce output, in which case PHP cannot send any headers anymore, ie no CORS realated headers that would be required for CORS request to work can be sent. Depending on the exact error message and the additional context that is attached to it (stacktrace etc), the output buffer might not overflow on every single request (a single byte can already make the difference), hence that kind of unrealiable behavior.

Login page rendering value even when the user in not logged in

I am working on an angular app and implementing registration and logging functionality in it using Firebase. Both functionalties are working fine.
Now the trouble that I am having is in onAuthStateChanged() function. What I want is once a user is logged in, he/she must be able to see a message saying that they are logged in. I am even able to see "Hi {{currentUser.firstname}}" that I have mentioned in the code once I log in but as soon as I refresh the page and try to log in again, I see that "Hi {{currentUser.firstname}}" even when I do not click the login button. I don't know how my login page is rendering $rootScope.currentUser value even when I haven't logged in. I have written the following code for it
JS
auth.onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var data = firebase.database().ref('users/' + user.uid);
data.on('value', function(snapshot) {
var userobj=snapshot.val();
$rootScope.currentUser=userobj;
})} else {
// No user is signed in.
$rootScope.currentUser=' ';
}
});
html
<div class="userinfo" ng-show="currentUser">
<span class="userinfo">Hi {{currentUser.firstname}}</span>
</div>
The behaviour is by-design. From the docs:
The Firebase Auth instance persists the user's state, so that refreshing the page (in a browser) or restarting the application doesn't lose the user's information.
If you do not want the users's auth state to be persisted, you will need to sign out the user.

Meteor IronRouter onBeforeAction causes exception in defer callback

I'm trying to secure my meteor app with iron-router, here's my onBeforeAction function:
this.route('manageUsers', {
path: '/panel/user_management',
layoutTemplate: 'panel',
onBeforeAction: function(){
if((Meteor.user() === null)||(Meteor.user().role !== 'superAdmin')){
Router.go('signIn');
throwAlert('You dont have access to see this page', 'notification');
}
}
});
When I'm trying to go to /panel/user_management subpage by pressing a link button everything goes fine (user is redirected etc.), but when I type the path directly in my browser (localhost:3000/panel/user_management) and hit enter user is not getting redirected and I receive in console Exception in defer callback error. Anyone know what I'm doing wrong?
For additional information, this view lists me all users registered. When I go to this path normally (without error) I see complete user list. When I receive error template doesn't appear in > yield.
Finally, I've solved this - the problem was wrong if statement, here's the correct one:
if((!Meteor.user())||(Meteor.user().role !== 'superAdmin')){}

Preventing automatic sign-in when using Google+ Sign-In

I am in the process of integrating Google+ sign in with my site, which also lets users sign in with Twitter and Facebook. The sign in page of the site therefore has 3 buttons, one for each of the services.
The issue I am having is in the following scenario:
user goes to the sign in page
user signs in successfully with G+
user signs out of my site (but the account is still associated with G+, signing out of the site does not disconnect the G+ account)
user visits the sign in page again
at this stage the Sign in with G+ button is rendered and automatically signs the user into the account associated with G+ without the user having to click the button
The problem is that on revisiting the sign in page, I want the user to have the option of signing in with another service, rather than automatically being signed in with G+. If the user wants to sign in with G+ then they can do so by clicking the button - the user will then be signed in automatically.
Is it possible to prevent this automatic sign in on button render? I can simulate it by using the data-approvalprompt="force" as an attribute on the button, but I don't think this is an ideal solution (the user then has to go through the confirmation process, which I would ideally would like to prevent)
Update
The best supported way to prevent automatic sign-in is to use the API method gapi.auth2.getAuthInstance().signOut() which will prevent automatic sign-in on your site after it has been called. Demo here.
In the demo, the user is signed out when they leave the page as shown in the following code:
window.onbeforeunload = function(e){
gapi.auth2.getAuthInstance().signOut();
};
Now, whenever the user exits the site (e.g. closes the window, navigates away), they will be signed out and the sign in button will not trigger sign-in until the user clicks it.
I don't recommend you do this in your own implementation but instead allow the user to explicitly sign out when they no longer desire want to be signed in. Also, please note that my example is a demo, you probably do not want to sign the user out automatically any time they leave your site.
Original Post
First, you should not be using data-approvalprompt="force" as this will cause extra authorized subtokens to be issued to your application / client and is designed to be used in scenarios where the user needs to be reauthorized after credentials have been lost server-side.
Second, you probably do not want to have the behavior where the user needs to click to sign in because they are already "signed in" to their Google account and it could be confusing to need to sign in (or trigger sign-in) again, separately, for your site.
If you really wanted to do this, you would perform an explicit render for the signin button but would not make the call to gapi.signin.render as documented in the Google+ sign-in documentation until you are aware that the user will not automatically get signed in.
The following code shows how to enable explicit render of the sign-in button:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{"parsetags": "explicit"}
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<head>
<script type="text/javascript">
var token = "";
function onSigninCallbackVanilla(authResponse){
// in a typical flow, you show disconnect here and hide the sign-in button
}
The following code shows you how to explicitly render the button:
<span id="signinButton">
<button id = "shim" onclick="gapi.signin.go(); $('#shim').hide();">Show the button</button>
<span
class="g-signin"
data-callback="onSigninCallbackVanilla"
data-clientid="YOUR_CLIENT_ID"
data-cookiepolicy="single_host_origin"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/plus.login">
</span>
</span>
How you're communicating that the user is signed out of your site is probably going to vary from site to site, but one approach could be to set a cookie indicating the "signed out" state for a user and then using this as the trigger for blocking explicit load. The behavior gets a little trickier when a user visits your site and has disabled cookies or uses a separate, signed-in, browser. To address this, you could do something complicated like querying the user state from your server over XHR on the sign-in callback and pretending not to know the user is signed in to Google+.
Just check for g-auth-window in the callback function:
function google_sign_callback(authResult){
if(authResult['g-oauth-window']){
}else if(authResult['error']) {
}
}
I had this issue and used auth2.disconnect()
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var auth2 = gapi.auth2.getAuthInstance();
auth2.disconnect();
//do other stuff
}
Edit:
you need to store the token before you disconnect because in some cases id_token will become null after disconnect:
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var idToken=profile.id_token;
googleUser.disconnect()
//use idToken for server side verification
}
If i'm correct you have your own sign in mechanism for your site and just need google sign in to sign up a user on verified email. in this case you can easily disconnect after you get the profile info.
Next time you load the page you will see "sign in" button instead of "signed in " button.
Unfortunately calling gapi.auth.signOut() made the app to log-in again when I'm requesting user data (neither it is persistent)
So the solution, as suggested by #class is to revoke the token:
$.ajax({
type: 'GET',
url: 'https://accounts.google.com/o/oauth2/revoke?token=' +
gapi.auth.getToken().access_token,
async: false,
contentType: 'application/json',
dataType: 'jsonp',
success: function(result) {
console.log('revoke response: ' + result);
$('#authOps').hide();
$('#profile').empty();
$('#visiblePeople').empty();
$('#authResult').empty();
$('#gConnect').show();
},
error: function(e) {
console.log(e);
}
});
I too has same issue this how i fixed it.I may not sure this is a stander way to do it but still it works fine with me...
add this Google JS from google developer
<script src="https://apis.google.com/js/platform.js" async defer></script>
<script>
function onSuccessG(googleUser) {
var profile = googleUser.getBasicProfile();
console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.
console.log('Name: ' + profile.getName());
console.log('Image URL: ' + profile.getImageUrl());
console.log('Email: ' + profile.getEmail());
}
function onFailureG(error) {
console.log(error);
}
function renderGmail() {
gapi.signin2.render('my-signin2', {
'scope': 'https://www.googleapis.com/auth/plus.login',
'width': 0,
'height': 0,
'longtitle': true,
'theme': 'dark',
'onsuccess': onSuccessG,
'onfailure': onFailureG
});
}
Now add html link and onClick call this renderGmail() function.
SignUp with Gmail
I hope this works...
I am using https://developers.google.com/identity/sign-in/web/build-button to build the sign in button for my web app which gives the user a choice to log in through either Facebook or Google.
This code is pretty easy for obtaining the Id_token.
However it also came with automatic signing in of the user if the user is already signed in.
Thus, adding the following snippet in the beginning of the script helped me control the signup procedure.
window.onbeforeunload = function(e){
gapi.auth2.getAuthInstance().signOut();
};
Thanks!
Our AngularJS solution was:
$scope.$on('event:google-plus-signin-success', function (event, authResult) {
if( authResult.status.method !== "AUTO"){
onGoogleLogIn(authResult[settings.configKeys.googleResponseToken]);
}
});
I have been struggling with this for a while and could not find a way to prevent automatic sign in to Google using the "easy implementation" of the Sign-in
I ended up using the custom integration which does not attempt to auto sign in (also allowed me to change the appearance in the same time)
The accepted answer no longer works when you start to use both Google Sign In and OAuth access tokens for other Google services. The access tokens expire immediately when the user is signed out. Instead, I would recommend the answer from this SO post, which involves attaching a click event handler to the Google sign in button. Only once the user clicks the sign in button and successfully logs into their Google account will the callbacks events fire.
I solved this by adding a click handler to my Google sign-in button. The click handler sets a global Javascript variable google_sign_in to true. When the onSuccess() handler fires (whether automatically on page load, or manually when the user clicks the sign-in button), it first checks whether google_sign_in == true and only then does it continue signing the user in:
<div id="google-signin2" onclick="return do_click_google_signin();"></div>
<script>
var google_sign_in = false; // assume
function do_click_google_signin() {
google_sign_in = true;
}
function onSuccess( googleUser ) {
if ( google_sign_in ) {
// Process sign-in
}
}
// Other redundant login stuff not shown...
</script>

Changing url for the pages with onbeforeunload

This question is mostly particularly about phpunit_selenium2 extension (though the general ideas are welcome as well):
Let's say I have a page that fires an alert on the browser/tab closing event with something like this:
window.onbeforeunload = function() {
return 'Hello world!';
};
The test opens this page, performs some actions and according to the test scenario I need to open another url.
The issue is that the command
$this->url('/another/page/url');
waits for the page url to be changed and fails because it doesn't - since it's locked by the just appeared alert window: RuntimeException: Navigation failed with error code=3.
How would one solve that?
The ugly but the only solution I could think of:
try {
$this->url('/another/page/url');
} catch (RuntimeException $e) {}
$this->acceptAlert();