Check if user phone_number already exist in aws cognito userpool - flutter

I am using serverless lambda functions on aws for user authentication in cognito user-pool. I am asking the user only for his phone_number and sending him otp for verification. The problem arises, when the user signs out and sign-in again. I am unable to decide on when to call signUp or sign-in for the user.
I am guessing that, I need a lambda call to be triggered before the pre-signup, which verifies that the user phone number already exists in the user-pool, and I need to call the Amplify.Auth.signIn api from the client. If not then call Amplify.Auth.signUpapi from the client. But I am unable to find any document for that. I am using flutter as my front-end. Please help.
My pre-signup lambda functions looks something like this:
exports.handler = async (event) => {
console.log('Received EVENT', JSON.stringify(event, null, 2));
event.response.autoConfirmUser = true;
event.response.autoVerifyPhone = true;
return event;
};

You can use the CognitoIdentityServiceProvider.listUsers for this.
const cognitoIdentityServiceProvider = new CognitoIdentityServiceProvider();
const result = await this.cognitoIdentityServiceProvider.listUsers({
UserPoolId: process.env.USER_POOL_ID,
Filter: `phone_number = \"${phoneNumber}\"`
}).promise();
return result.Users.length > 0 ? result.Users[0].Username : undefined;

Related

Could a callable cloud function that delete users be abused so it can delete users by only id?

I have a callable cloud function on the frontend, that gets a sub-user id from front-end pass it to the cloud function, and then the cloud function delete that user and also deletes his doc from the collection...
my question is could someone get the id of some user and use that function and start popping requests using this function to delete users left and right ?
it make sense that this could function won't follow any rules, so I consider this to be a major security risk if implemented in the wrong way any idea how to improve security on this and guard against any abuse attempts.
Front end callable function
const functions = getFunctions();
const deleteClient = httpsCallable(functions, 'deleteClient');
deleteClient({ uid: 'clientId' })
.then((result: any) => {
// Read result of the Cloud Function.
/** #type {any} */
// const data = result.data;
// const sanitizedMessage = data.text;
console.log(result);
})
.catch((err: any) => {
alert(err);
});
Cloud Function
export const deleteClient = functions.https.onCall((data, context) => {
admin
.auth()
.deleteUser(data.uid)
.then(() => {
console.log('Successfully deleted user');
})
.catch((error: any) => {
console.log('Error deleting user:', error);
});
db.collection('ClientsData').doc(data.uid).delete();
});
It indeed sounds like you created a security risk, and is also precisely why Firebase Authentication only allows deleting the currently signed-in user in its client-side SDKs.
You'll have to implement some sort of authorization scheme in your Cloud Functions code. This takes a two step process:
Pass the identity of the signed-in user making the call from the client to the server, and use it there to establish who is making the call. Since you're using Callable Cloud Functions, this is already done for you and the user is available in the context.auth variable in your Cloud Functions code.
Determine whether the user is authorized to perform the operation. This is typically done by having a list of authorized users, and then checking of the context.auth.uid who made the call is in that list. The list could be stored in your database too of course, so that you can update it without making changes to the code.

How to get Firebase UID knowing email user?

I am building a Flutter app where the administrator profile can create users to access their company. The code works right, unless the new user was previously created for another company. In this case an error of type ERROR_EMAIL_ALREADY_IN_USE appears from FIREBASE AUTH. What I want to do is simply retrieve the assigned UID from FIREBASE AUTH, which is necessary to assign the user within my database to an additional company.
It's my code...
_register(LoginBloc bloc, BuildContext context) async{
final usuarioBloc = Provider.usuarioBloc(context);
if (!formKey.currentState.validate() ) return;
final info = await usuarioProvider.crearUsuarioFirebase(bloc.email, bloc.password, true);
if (info['ok']) {
final keyUserId = info['localId'];
usuarioProvider.crearUsuarioRaiz(keyUserId, _prefs.idEmpresa, bloc.email);
usuario.idUsuario = info['localId'];
usuario.correo = bloc.email;
usuarioBloc.crearUsuarioEmpresa(usuario, usuario.idUsuario, usuario.idEmpresa); //to create user in the Company
print('******* User was Created *************');
} else { //info['ok'] is false
switch (info['mensaje'].code) {
case 'ERROR_EMAIL_ALREADY_IN_USE':
usuario.correo = bloc.email;
// usuario.idUsuario = ????????
// Here I would like to retrieve the UID to assign it to their additional Company
usuarioBloc.crearUsuarioEmpresa(usuario, usuario.idUsuario, usuario.idEmpresa); //to create user in the Company
print('*** User already in use, the user can use his/her usual password ***');
break;
default:
print(info['mensaje'].message); //If it was a different error
}
}
}
In Provider, I have...
Future <Map<String, dynamic>> crearUsuarioFirebase(String email, String password, [bool desdeAdmin = false]) async {
try {
AuthResult result = await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return {'ok' : true, 'localId':user.uid, 'email' : user.email};
} catch (e) {
print(e);
return {'ok': false, 'mensaje': e};
}
}
How can I programmatically obtain the UID knowing its user email?
There is no way to look up a user's UID from their email address using the Firebase Authentication client-side APIs. Since this lookup is considered a trusted operations, it is only available in the Admin SDK for Firebase Authentication.
The two most common solutions are:
Create a custom server-side API in a trusted environment (such as Cloud Functions) that performs the lookup, and then call that API from your client-side application. You will have to make sure that only authorized users can perform this lookup.
Store the information about each user into a database (like the Realtime Database that you tagged your question with) when their account is created, or whenever they sign in. Then you can look up the UID from the email in the database. Here too, you will have to ensure that the data is only available in ways that fit with your application's data privacy requirements.
Note that if you just need to know whether an email address is in use (and not the specific UID that uses it), you can call the fetchSignInMethodsForEmail method.

Can I update a user document whenever a user updates his authentication profile?

I am working with flutter and I have a AuthenticationProvider. Whenever my user signs in with his phone I update his profile as well. But my problem is that auth users can't be queried. So I read that I should keep a separate user collection. Now my question is, is it possible to update a user document in my user collection whenever a user updates his auth profile? I would like to do this with cloud functions but I noticed that there is only a create and delete? So how can I do this?
This is what I currently have
Authentication Provider
Future<void> _verificationComplete(BuildContext context, AuthCredential authCredential, userInfo.UserInfo userInfo) async {
AuthResult authResult = await FirebaseAuth.instance.signInWithCredential(authCredential);
final userUpdateInfo = UserUpdateInfo();
userUpdateInfo.displayName = userInfo.name;
userUpdateInfo.photoUrl = userInfo.photoUrl;
await authResult.user.updateProfile(userUpdateInfo);
await authResult.user.reload();
user = UserModel.fromFirebase(authResult.user);
_status = AuthenticationStatus.authenticated;
notifyListeners();
}
Cloud function
export const onUserCreated = functions.region('europe-west1').auth.user().onCreate(async user => {
const privateUserData = {
activeGroup: '',
cloudMessagingToken: '',
}
const publicUserData = {
name: '',
photoUrl: '',
}
const promises = [];
promises.push(firestore.collection('users').doc(user.uid).collection('private').doc('data').set(privateUserData));
promises.push(firestore.collection('users').doc(user.uid).collection('public').doc('data').set(publicUserData));
return await Promise.all(promises);
});
There is no Cloud Functions trigger for when a user updates their Firebase Authentication profile. I'd highly recommend filing a feature request for that, as it's much missed.
For now, the closest you can get is with a Cloud Function that you call directly from the application code. The two options there are:
Have your application code call the Firebase Authentication API first, then when that completes, have it call your custom Cloud Function to update the database too.
Have your application code call the Cloud Function immediately, and then have the Cloud Function update both the user profile and the database.
I somehow often do the first one, but see more developers take the second approach. I think their approach is probable simpler, but I just haven't gotten around to it yet. :)

MongoDB Stitch REST API - Payload Signature Verification

I am working on a SANDBOX Cluster & a new app created by me in MongoDB Stitch.
I need to understand "Payload Signature Verification" in MongoDB Stitch App. Lets say, I need to make a REST GET API, which will fetch me a list of products, but this API call must be authenticated ie. only registered/authenticated users will be able to make this call. MongoDB Stitch suggests below to do that:
https://docs.mongodb.com/stitch/services/webhook-requests-and-responses/#webhook-verify-payload-signature
But, i need to understand:
(1) Where to add this BODY & SECRET ? As per my knowledge, it must be kept in the stitch app, as you must not expose any of your secret keys in client side javascript.
(2) { "message":"MESSAGE" } is this configurable? if yes, what value should we add here?
This function must be coded in MongoDB Stitch App. That is clear. This function returns "hash" based on the "body" & "secret" you pass in earlier step.
And now, you must pass this hash in your API Request:
Now, the question is:
You can easily see any request which is being passed to server in developer tools, anybody can easily copy it & pass it same through POSTMAN. So:
-> How do i secure my requests? (FYI: I have also added "RULES", saying this request must execute only if the domain name contains lets say, www.mysite.com. But i am able to execute the request successfully from localhost.)
-> If, anybody can copy & paste my request in POSTMAN & run it. SO, what is the use of generating that HASH ?
-> How do i keep my request(s) tokens alive/valid for limited period of time, lets say request is valid only for next 5 minutes ? (i mean how do i do this in Stitch APP ? Where is that Option ?)
-> How do i get the refresh token ? & even if i get it somehow, how do i re-pass it to the request ?
All such queries are UN_ANSWERED in MongoDB Stich Documentation : https://docs.mongodb.com/stitch/
Basically i want to understand the full life-cycle of any GET/POST/PUT/PATCH/DELETE request of MongoDB Stitch App / Stitch REST APIs.
If anybody have used MongoDB Stich, please explain me.
I don't know your specific use-case, though I also had issues with creating an Authenticated HTTP REST API. My idea was: I already have all security rules and schemas defined in Stitch, now I want to access the data over HTTP still using the logic defined in Stitch and not rewriting everything.
I wasn't able to create such API with Stitch functions and Webhooks, though I created an API server in (literally) 1 hour with NodeJS Koa (express or any other framework would do) and Stitch server SDK:
// app.js
const Koa = require('koa')
const app = module.exports = new Koa()
const auth = require('./auth')
const router = require('./router')
app.use(auth())
app.use(router.routes())
app.use(router.allowedMethods())
// listen
if (!module.parent) {
app.listen(3000)
}
// auth.js
const { loginWithApiKey } = require('./stitch')
function auth () {
return async function auth (ctx, next) {
const apiKey = ctx.query.api_key
try {
await loginWithApiKey(apiKey)
} catch (e) {
ctx.throw(401, 'Not Authorized')
}
await next()
}
}
module.exports = auth
// router.js
const router = require('koa-router')()
const { BSON } = require('mongodb-stitch-server-sdk')
const { db } = require('./stitch')
router.get('/', async (ctx) => {
ctx.body = { message: 'Nothing to see, but you\'re good!' }
})
const COLLECTIONS_WHITELIST = [
'activities',
'expenses',
'projects',
'resources'
]
// List
router.get('/:collection', async (ctx) => {
const collection = ctx.params.collection
isValidCollection(ctx, collection)
ctx.body = await db
.collection(collection)
.find()
.toArray()
})
function isValidCollection (ctx, collection) {
// check if the collection is allowed in the API
if (!COLLECTIONS_WHITELIST.includes(collection)) {
ctx.throw(404, `Unknown API entity ${collection}`)
}
}
module.exports = router
I hope it helps

verify email using accounts.ui package

I want to send a verification email when some user is created. I use the accounts-password package, so any Accounts methods are called in my code.
I read in documentation that I need to call:
Accounts.sendVerificationEmail(userId, [email])
but the problem is that I don't know when to call it.
I tried to call in the callback function of Accounts.onCreateUser(func) but the user had not been created yet in the database.
Any ideas?
on the serverside:
Accounts.config({sendVerificationEmail: true, forbidClientAccountCreation: false});
got the answer from the comments above.
sendVerificationEmail is only available server-side. What I usually do is to use a setInterval inside onCreateUser to wait for Meteor to create the user before sending an email.
Read More: Verify an Email with Meteor Accounts.
// (server-side)
Accounts.onCreateUser(function(options, user) {
user.profile = {};
// we wait for Meteor to create the user before sending an email
Meteor.setTimeout(function() {
Accounts.sendVerificationEmail(user._id);
}, 2 * 1000);
return user;
});
You need specify mail in enviroment variables.
Then use Accounts.sendVerificationEmail(userId, [email]) in callback of Account.onCreateUser sorry for mistake and delay.
Like this (below is full example js file):
Template.register.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#account-email').value
, password = t.find('#account-password').value;
// Trim and validate the input
Accounts.onCreateUser({email: email, password : password}, function(err){
if (err) {
// Inform the user that account creation failed
} else {
// Success. Account has been created and the user
// has logged in successfully.
Accounts.sendVerificationEmail(this.userId, email);
}
});
return false;
} });
if(Meteor.isServer){
Meteor.startup(function(){
process.env.MAIL_URL='smtp://your_mail:your_password#host:port'
}
}
I refered to this pages :
http://blog.benmcmahen.com/post/41741539120/building-a-customized-accounts-ui-for-meteor
http://sendgrid.com/blog/send-email-meteor-sendgrid/
How come my Meteor app with accounts package is not sending a verification email?