Parse.com cloud code check user before allowing login - parse-platform

I am using the following code to allow login from my iOS and Android apps. One of our paying customers has hundreds of users (who all use the same domain in their email i.e #mycompany.com) who need to be stopped from using the app immediately.
I cannot re-publish the client app as it takes time for review etc. How can I prevent login from a certain group of users from cloud code.
Parse.User.logIn(user.username.toLowerCase(), user.password, {
success: function(user) {
user.set("platform", ionic.Platform.device());
user.set("os", ionic.Platform.platform());
user.save(null, {
success: function(user) {
console.log('ok');
},
error: function(err) {
console.log('error setting device');
}
});
Is there is beforeSave on the session class?

Related

Single sign on in Teams application between tabs and the bot

Using the Bot Framework w/ Microsoft.Bot.Builder v4.6.3
Is it possible to have users sign in only once using the web-based authentication flow, doesn't matter if they sign in via tabs or via bot conversation? If they sign in via a link from a tab, I'd like to have the bot know about this.
I have tried the following for test, omitting any security checks:
All pages are with the following js files imported:
https://statics.teams.microsoft.com/sdk/v1.4.2/js/MicrosoftTeams.min.js
https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.9.1/oidc-client.min.js
On load, the tab page executes microsoftTeams.initialize();
Add a button to the tab page:
<button onclick="authenticate()">Authenticate</button>
The authenticate function contains the following:
function authenticate() {
microsoftTeams.authentication.authenticate({
url: window.location.origin + "/tabs/tabAuthStart",
width: 600,
height: 535,
successCallback: function (result) {
// The debug function just displays what's sent to it using document.write()
debug(result);
},
failureCallback: function (reason) {
debug(reason);
}
});
}
The tabAuthStart page contains the following script which is executed on page load:
microsoftTeams.initialize();
const mgr = new Oidc.UserManager({
userStore: new Oidc.WebStorageStateStore(),
authority: '<my-identity-server>',
client_id: '<my-id-srv-client>',
redirect_uri: window.location.origin + '/tabs/tabAuthCallback',
response_type: 'id_token token',
scope: '<my-requested-scopes>',
accessTokenExpiringNotificationTime: 10,
automaticSilentRenew: true,
filterProtocolClaims: true,
loadUserInfo: true
});
mgr.signinRedirect();
After a successful sign in at the identity provider, I'm redirected back to /tabs/tabAuthCallback
On load, the /tabs/tabAuthCallback executes the following code:
microsoftTeams.initialize();
var mgr = new Oidc.UserManager({ userStore: new Oidc.WebStorageStateStore(), loadUserInfo: true, filterProtocolClaims: true });
mgr.signinRedirectCallback().then(function (user) {
// I expected something involving a bot to happen after calling this
microsoftTeams.authentication.notifySuccess({
idToken: user.id_token,
accessToken: user.access_token,
tokenType: user.token_type,
expiresIn: user.expires_at
})
}).catch(function (err) {
microsoftTeams.authentication.notifyFailure("UnexpectedFailure: " + err);
});
The pop-up window is closed and the successCallback function from the tab is executed successfully with the user information that I have sent. However, the bot is not in any way notified about this (as far as I know). I have set a breakpoint in the bot controller action resolved by POST /api/messages but it's never hit.
Do I need to handle this manually? I.e. pass the user info to the back-end? But even if so, how do I know which Teams user to associate this user info (i.e. access token) to?
If this is possible to do in a reliable and secure way, would it also be possible in the opposite direction, i.e. having the user token available to the tab if they have already been authenticated from a bot conversation or a messaging extension? Is there a reliable way to identify a Teams user who's navigating tabs, in order to obtain their access token from the back-end, assuming the back-end already obtained them via the authentication mechanism?

Meteor-Validate multi OAuth account in 'onCreateUser'

I am trying to login to single user with multi OAuth (facebook, google) login service. Here is what I try.
In Client:
'click #signInByFacebook': function (e) {
e.preventDefault();
Meteor.loginWithFacebook({requestPermissions: ['public_profile', 'email', 'user_about_me', 'user_photos']}, function (err, res) {
if (err) {
showError($('.alert'), err, 'login');
return;
}
showSuccess($('.alert'), 'login');
Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
Router.go('/');
});
},
'click #signInByGoogle': function (e) {
e.preventDefault();
Meteor.loginWithGoogle({requestPermissions: ['profile', 'email', 'openid']}, function (err, res) {
if (err) {
showError($('.alert'), err, 'login');
return;
}
showSuccess($('.alert'), 'login');
Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
Router.go('/');
});
}
In Server:
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
var sameuser = Meteor.users.findOne({$or: [{'emails.address': getEmail(user)}, {'services.facebook.email': getEmail(user)}, {'services.google.email': getEmail(user)}]});
console.log(sameuser);
if (sameuser) {
if (user.services.facebook) {
console.log("facebook");
Meteor.users.update({_id: sameuser._id}, {$set: {'services.facebook': user.services.facebook}});
}
if (user.services.google) {
console.log("google");
Meteor.users.update({_id: sameuser._id}, {$set: {'services.google': user.services.google}});
}
return;
}
console.log('register success');
return user;
});
This code will check if any user logined with facebook/google has the
same email or not with current sign in. If they are the same, just
update information to old account. If not, create new user.
This works great, but there is a problem with the 'return ;' in server code. I dont know what should I return to stop create user and auto login to the user that has same email. Anybody can help this issue ? Thank you.
The only way to stop creation of the new user is to throw an exception, but that will also prevent logging in as the existing user.
However, your general approach is insecure. Consider a user who has a Google account with a strong password and a Facebook account with a weak one. When he uses the Google account to authenticate with your app, he doesn't (and shouldn't) expect that someone who gains access to his Facebook account will be able access your app as him.
A better approach is to require that the user be logged into both services simultaneously before merging the services. The good news is that this also means that you don't need to worry about logging in after preventing the creation of the new user, because the user will already be logged in. Something like this might work:
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
var currentUser = Meteor.user();
console.log(currentUser);
if (currentUser) {
if (user.services.facebook) {
console.log("facebook");
Meteor.users.update({_id: currentUser._id}, {$set: {'services.facebook': user.services.facebook}});
}
if (user.services.google) {
console.log("google");
Meteor.users.update({_id: currentUser._id}, {$set: {'services.google': user.services.google}});
}
throw new Meteor.Error(Accounts.LoginCancelledError.numericError, "Service added to existing user (or something similar)");;
}
console.log('register success');
return user;
});
There are still a couple loose ends. First, I think Meteor expects OAuth credentials to be "pinned" to the user that they are associated with, so you probably need to repin the credentials you are copying.
Second, the above approach bypasses the validateLoginAttempt() callbacks. If you, or any package you are using, has registered any such callbacks, they won't be called when logging in using the second service, so they won't be able to prevent any such logins that they might consider invalid.
You can address both of these issues and skip the onCreateUser() callback as well, by just adding my brettle:accounts-add-service package to your app.

Is possible to use Stripe.com managed accounts with Parse.com Stripe module?

I saw that Parse.com has module for Stripe payments, but I am not sure do they support Stripe.com managed accounts, since I am creating mobile apps for marketplaces and Stripe support told me that I should use managed accounts for marketplaces.
Parse.com Stripe module: https://parse.com/docs/js/guide#cloud-code-modules-stripe
Stripe.com managed accounts: https://stripe.com/docs/connect/managed-accounts
Anyone with experience with Parse.com Stripe module?
Parse unfortunately hasn't updated their Stripe API library in a long time and have stated they don't have plans to update it. Their API library is built against the 2012-07-09version of the Stripe API which is a few years before Connect and Managed Accounts entered the Stripe API. Thus the Parse API library does not support managed accounts.
You need to make sure that your account with Stripe has been set up to allow for connected accounts. By adding var Buffer = require('buffer').Buffer; to you stripe cloud code outside the scope of the request, you can create an account using this code:
var Buffer = require('buffer').Buffer;
var bankAccountNumber = request.params['bankAccountNumber']
Parse.Cloud.httpRequest({
method:"POST",
url: "https://" +"api.stripe.com/v1" + "/accounts",
headers: {
'Authorization': 'Basic ' + new Buffer('YOUR_STRIPE_SECRET_KEY' + ':' + '').toString('base64')
},
body:{
"managed": false,
"country": "US",
"email": "example#mailinator.com"
},
success: function(httpResponse) {
response.success(httpResponse.text);
},
error: function(httpResponse) {
response.error('Request failed with response code: ' + httpResponse.status);
}
If you create a standalone account, you will receive an email upon creation. Mailinator is a service that allows you create working email addresses with any name, but there is no privacy.
Edit: I am working with connected accounts. I've noticed that when testing this, if you successfully create an account that subsequent attempts to create an account with the same email will fail. To transfer funds, Stripe requires certain properties that you can view under the connected account in the platform's dashboard. I've found the syntax for several of these (all except bank account).
Edit2: To complete the required fields for Stripe to verify your account you need to include a bank account field. My project is running in Swift and the parameter passed into my cloud code was a token created in my app using STPAPIClient.sharedClient().createTokenWithBankAccount and the test account information given at https://stripe.com/docs/testing#how-do-i-test-sending-transfers.
body:{
"managed": true,
"country": "US",
"email": "yourUniqueemail#mailinator.com",
"legal_entity[first_name]": "AnyName",
"legal_entity[last_name]": "AnyName",
"legal_entity[dob[day]]": "20",
"legal_entity[dob[month]]": "11",
"legal_entity[dob[year]]": "1993",
"legal_entity[type]": "individual",
"tos_acceptance[date]": "1438198036",
"tos_acceptance[ip]": "123.34.56.789",
"bank_account": bankAccountNumber,
},
Edit 3:
The way my code works is that customers are created and are charged. The charge amount is sent to the platform with an application fee. Then the connected account is payed out from the platform.(I am not completely sure this is the proper way to use Stripe; however, this method seems to work in test mode and allows us to store information about customers and the people we pay out.) Fortunately, the charge method exists in Parse's api, so we do not have to make a POST request. Here is the charge method that does this functionality.
Parse.Cloud.define("chargeAccount", function(request, response) {
var amount2 = request.params['amount']*100;
Stripe.Charges.create({
amount: amount2,
currency: request.params["currency"],
customer: request.params['customerId'],
description: request.params['description'],
application_fee: amount2/5,
destination: request.params['destinationAccountId']
},{
success: function(charge) {
response.success("There was a charge of $" + amount2/100 + " The application fee is: $" +amount2/500 +".");
},
error: function(error) {
response.error("\nThis is the error:\n" +error);
}
})
});
I found this question on the parse questions section. I believe you should be able to change this code around to meet your needs. I haven't tested it, but by looking at the stripe api, I came up with this:
here is the what the curl request looks like:
curl https://api.stripe.com/v1/accounts \
-u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
-d managed=false \
-d country=US \
-d email="bob#example.com"
So your javascript would be something like this I believe:
Parse.Cloud.httpRequest({
//STRIPE_SECRET_KEY will be your stripe secret key obviously, this is different from the public key that you will use in your iOS/Android side.
// STRIPE_API_BASE_URL = 'api.stripe.com/v1'
method:"POST",
url: "https://" + STRIPE_API_BASE_URL + "/accounts/",
headers: {
"keys[secret]" : STRIPE_SECRET_KEY
},
body:{
"managed="+true,
"country="+"US",
"email="+"bob#example.com"
},
success: function(httpResponse) {
response.success(httpResponse.text);
},
error: function(httpResponse) {
response.error('Request failed with response code ' + httpResponse.status);
}
});
This may not be 100% because I haven't been able to test it, but look at the following questions for some more resources:
https://parse.com/questions/cloud-httprequest-unable-to-take-dictionary-object-as-parameter-cant-form-encode-an-object-error
https://www.parse.com/questions/stripe-store-multiple-cards-on-a-customer

Parse: Session Not Created on Login

I'm having a slight issue when using the logIn class method. According to the docs, I should see a Session automatically created when a user logs in successfully.
Sessions are automatically created when users log in or sign up. They
are automatically deleted when users log out.
I'm logging in successfully, and am returning a success message to the console.
$("#login").submit(function(event) {
event.preventDefault();
var name = $("#login-name").val();
var pass = $("#login-password").val();
Parse.User.logIn(name, pass, {
success: function(user) {
console.log("Logged in successfully!");
}, error: function(user, error) {
console.log("Login error: " + error.message);
}
});
});
But when I jump into the Parse.com Data Browser, I can't see the Session under the Data Tab.
What am I doing wrong?
Any help is appreciated. Thanks in advance!
Seem to have fixed this by enabling "Require Revocable Sessions" in the Parse.com app settings page.

No callback when calling yam.platform.login

When using Yammer SDK and using yam.platform.login method, I don't get any callback when authentication fails or when the user closes dialog window. Is this a bug or something you have seen in your Yammer integration tasks?
My code
yam.platform.getLoginStatus(function (response) {
if (response.authResponse) {
}
else {
yam.platform.login(function (response) {
if (response.authResponse) {
console.dir(response);
}
else {
### CODE NEVER EXECUTED IF LOGIN FAILS OR USER CLOSE POPUP###
}
});
}
});
Make sure to add your web application url to "Javascript Origins" of your registered yammer app.
Make sure you added your web app url to "Trusted Sites" and other Yammer urls.
We get this problem (no callback on yam.platform.login) when the user is currently logged into a network other than the home network (network where app is registered). If your users use multiple networks, you may need to add your app to the global app register.
An alternative (hacky) way is to 'try' the approach below. This worked for us as it only needed to happen once (to get the auth token).
yam.getLoginStatus(function(resp){
if (resp.authResponse) {
//success
} else {
// not logged in
var yamLoginSuccess=0;
try {
yam.platform.login( function (response) { //prompt login
console.log('no response here if user in another network');
if (response.authResponse) {
//success
yamLoginSuccess=1;
}
});
}
catch(err) {
// does not throw an error so this bit is not helpful
}
finally{
if(yamLoginSuccess===0){
alert('Need to be logged into the home yammer first :-/ /n '
+ 'Redirecting now, hit back to come back');
window.location='https://www.yammer.com/YOURNETWORK/';
}
}
}
});

Resources