App maker override server side error message - google-app-maker

I have a problem with the server-side error management on Google App Maker.
Here an exemple of my code
Server-side
function serverSideFn() {
// Consider the error to be throw.
if ( anError ) {
throw new Error("A specific error message");
}
}
Client-side
function clientSideFn() {
google.script.run
.withSuccessHandler(function(result) {
// Success code...
})
.withFailureHandler(function(error) {
console.log(error.message); // The message error here is not the same if I have or not the Admin role.
showErrorPopup(error.message);
})
.serverSideFn();
}
When I execute the "clientSideFn" function with default role Admin, I have the good message ("A specific error message"), but if I don't have the Admin role, I have a "Server Error" message instead of the expected.
I've tried to use the developer account option, and set Admin role to this account and execute the server side scripts, but the error is still present for users without Admin role.
I've also tried to throw a custom Exception, but the error is still changed on client side.
What I can change to got the expected message when the user don't have the Admin role ?

The relevant documentation to your question is located here https://developers.google.com/appmaker/scripting/api/server. The basics is that you use:
throw new app.ManagedError('Your custom message here');

Related

Firebase user.delete() method works but with error

I am using angular, and have an application that stores user details, and login info. When trying to delete a user, I am first deleting all the user related information. Then asking the user to re-authenticate themselves, after authentication, user gets logged out, and their basic details fetched to show profile id deleted followed by their sign-in info using user.delete().
All this works as expected, but at the end I am getting an error. Why am I am getting this error even when I have already logged out the user of the application.
Error Message: {code: "auth/user-token-expired", message: "The user's credential is no longer valid. The user must sign in again.", a: null}
My code -
deleteAccount(){
var userToDelete = firebase.auth().currentUser;
this.logout();
this.store.dispatch(UI.StartAppLoad({status:'Deleting User Details...'}));
this.userService.DeleteUser(userToDelete.uid)
.then((res)=>{
console.log(res);
}).catch(this.HandleError.bind(this));
userToDelete.delete().then(
(res)=>{
console.log(res);
this.uiService.showSnackbar('User Account Deleted',null,3000);
this.store.dispatch(UI.LoadApp());
}
).catch(this.HandleError.bind(this));
}
logout() {
this.afAuth.signOut();
}
where, HandleError is used to display the Error Message in a snackbar.
deleteAccount() is called after the user successfully authenticates themselves.
Instead of getting the error message displayed, I want to display the message 'User Account Deleted'.
Entire Flow -
onDeleteAccount(){
const confirmResult = this.uiService.showConfirm({
isDanger:true,
title:'Delete Account?',
content:'All your user account data will be permamnently deleted.'+
' You will need to create a new account later. Are you sure you want to continue?',
okText:'Delete'
});
confirmResult.subscribe(async isDelete=>{
if(isDelete){
this.store.dispatch(UI.StartAppLoad({status:'Deleting Excercise Data...'}));
const isResetDone = await this.trainingService.resetPastExercise();
if(isResetDone){
this.store.dispatch(UI.StartAppLoad({status:'Deleting Follow list...'}));
this.userService.clearFollowList();
this.authService.actionToPerform.next(actions.Delete_Account);
this.store.dispatch(UI.LoadApp());
this.router.navigate([AppRoutes.ReAuthenticate]);
}
}
});
}
Authenticate Page's submit() method:
this.authService.reauthenticate({
email:form.value.email,
password:form.value.password
});
this.authService.deleteAccount();
AuthService:
reauthenticate(authdata: AuthData) {
this.store.dispatch(UI.StartLoading());
var credential = firebase.auth.EmailAuthProvider.credential(
authdata.email,
authdata.password
);
this.afAuth.currentUser.then((user) => {
user.reauthenticateWithCredential(credential)
.then((res)=>{
this.prevPwd = authdata.password;
console.log(res);
this.store.dispatch(UI.StopLoading());
})
.catch(this.HandleError.bind(this))
});
}
And then the above method deleteAccount()
Please suggest.
As explained in the doc, this happens because:
delete() is a security-sensitive operation that requires the user to
have recently signed in.
The doc also indicates that:
If this requirement isn't met, ask the user
to authenticate again and then call firebase.User.reauthenticateWithCredential.
So, you need to handle this specific error and do as indicated by the doc, i.e. "ask the user to authenticate again and then call reauthenticateWithCredential."

Meteor: Resend email verification link

I want to be able to resend an email verification link to users of my Meteor application in case they accidentally delete their email verification email.
I have a link with the id "resentEmailVerificationLink"
I have the following code in my client for when the link is click (alerts are just there to show myself how far the function gets before an error):
Template.dashboard.events({
'click #resentEmailVerificationLink' : function(event) {
event.preventDefault();
var id = Meteor.userId();
alert('clicked: ' + id);
Accounts.sendVerificationEmail(id);
alert('Verification Email resent');
return false; // Stops page from reloading
}
)};
I know the sendVerificationEmail is a server function but I have no idea how to call this function in the server upon clicking the verify email link (I'm a bit of a Meteor newbie).
Any idea of how to accomplish this, because currently it doesn't work with the following error: Uncaught TypeError: Accounts.sendVerificationEmail is not a function
Note: Meteor.Accounts.sendVerificationEmail(id); doesn't work either (it does however produce a different error.
You can try with server side method just create one pass the attrs and call http://docs.meteor.com/#/full/accounts_sendverificationemail on the server. More about meteor methods: http://docs.meteor.com/#/full/meteor_methods

Log out from restricted area without triggering security settings

I have a section that requires login and a certain role ("higher than 19"). Whenever I logout from it, I get
Error: permission_denied: Client doesn't have permission to access the desired data.
Reason for this are my security settings:
".read": "root.child('users').child(auth.uid).child('data').child('role').val() > 19",
My logout function:
// Sign out functionality
App.controller('SignOutCtrl', function($scope, $state, Auth) {
$scope.logout = function() {
// Go to landing page
$state.go('home');
// Log user out
Auth.$unauth();
};
});
App.factory('Auth', ['$firebaseAuth', function($firebaseAuth) {
var firebase = new Firebase('https://mysite.firebaseio.com/');
return $firebaseAuth(firebase);
}]);
The $state 'home' doesn't have any reading restrictions, looks like the log out actually happens before I'm being redirected to the 'home'-$state, which triggers the security restrictions.
I guess I'm struggling to understand the very basic concept of how to log out from a restricted site without triggering the security setting, can anyone please explain me how to do that properly?
I would suggest unauthenticating when the logout page is loaded instead of before leaving the restricted page.

Can't authenticate using email and password

I'm trying to authenticate a user with a custom login form. I designed the form, and I'm trying to authenticate users like this:
Template['account.login'].events
'submit #login-form': (e, t) ->
e.preventDefault()
Meteor.loginWithPassword(
t.find('#login_username').value,
t.find('#login_password').value,
(err)->
console.log(err)
)
return false
Relevant Template(in JADE - sorry):
form.smart-form.client-form#login-form(novalidate)
fieldset
section
label.input
input#login_username(type="text", name="username")
section
label.input
input#login_password(type="password", name="password")
footer
button.btn.btn-primary(type="submit", id="login.btn.login") Login
From the resource I found online, this would be correct. However, I get this exception thrown on the server:
Exception while invoking method 'login' Error: Match error: Failed Match.Where validation in field username in field user.username
You forgot to add a tab and Meteor.loginWithPassword() is running outside the Template event. Paste your coffeescript code at http://js2.coffee/ and you'll see it right away.

Meteor.js google account : filter email and force account choser

In my Meteor.js application, I'm using the accounts-google package in order to be connected with a google account. I have two questions about it.
First, is there a simple way to filter the account used? I would like that the users can connect only with google accounts belonging to my company. Our google account mails end with #mycompany.com. So it would be a simple mail filtering.
I already done that with some post log in hooks but I was wondering if there was a simpler way for doing it.
My second question is how to force the opening of the google account choser. For now, if I try to connect with a wrong google account, and if I only added this account (like in gmail, drive, etc), the google choser doesn't pop and automatically connect with this wrong account. So, in this case, the user is totally blocked (my application disconnect him if he tries to log in with a wrong account but the google account module doesn't propose him to connect with another account).
Thank you for your help.
In order to restrict signup/login to your domain, simply do on the server:
var checkEmailAgainstAllowed = function(email) {
var allowedDomains = ['mycompanydomain.com'];
var allowedEmails = ['otheruser#fromotherdomain.com','anotheruser#fromanotherdomain.com'];
var domain = email.replace(/.*#/,'').toLowerCase();
email = email.toLowerCase();
return _.contains(allowedEmails, email) || _.contains(allowedDomains, domain);
};
Accounts.config({
restrictCreationByEmailDomain: function(email) {
if (!email) {
throw new Meteor.Error(403,'This email address is not allowed');
}
if (!checkEmailAgainstAllowed(email)) {
throw new Meteor.Error(403,'This email domain is not allowed');
}
return true;
}
});
And to login, you'll need on the client:
Meteor.loginWithGoogle({
forceApprovalPrompt: true, //this is what you want, to rerequest approval each time that prompts the google login prompt
loginStyle : "redirect", //or not, depending on your need
requestPermissions : ['profile', 'email'],
requestOfflineToken: true
}, function (err) {
if (err)
// set a session variable to display later if there is a login error
Session.set('loginError', 'reason: ' + err.reason + ' message: ' + err.message || 'Unknown error');
});
Side note:
Alternatively, you can set up your routes so that every time a new route is called, you login, and every time a route is destroyed or on windows's unload, you call logout. This causes login/logout roundtrip everytime the route changes, but you'll make sure that the new user always has a fresh session
Edit:
When you log out of your meteor app, you don't log out of google. That's how oauth works. So, basically, if you want a meteor log out to also log the user out of their google account, so that the next time they come back, they need to provide credentials again, you should do:
Meteor.logout(function(e) {
if (e) {
console.log("Could not log the user out")
} else {
window.location.replace('https://accounts.google.com/Logout');
}
});
This uses the callback of Meteor.logout() so that when the logout is successfull, the user is redirected to google's central account logout url where the user is also logged out of all google services.

Resources