How to get data passed to mongoose schema constructor - javascript

I am testing my application and need to verify that mongoose schema constructor is called with correct data.
let's say I do this:
const UserData = new User(user)
console.log(UserData.contructor.args)
I would expect log of the user object.
Probably the data is passed to constructor of mongoose schema?
Can some one please advise me how to access it?
Here is specific case I am trying to solve.
export const signup = async (req, res, next) => {
try {
//if user object is missing return error
if (!req.body.user)
return next(boom.unauthorized('No user data received.'))
//get user data
const user = req.body.user,
{ auth: { local: { password, password_2 } } } = user
//check if both passwords match
if (password !== password_2)
return next(boom.unauthorized('Passwords do not match.'))
//check if password is valid
if (!Password.validate(password)) {
const errorData = Password.validate(password, { list: true })
return next(boom.notAcceptable('Invalid password.', errorData))
}
//creates new mongo user
const UserData = new User(user)
//sets user password hash
UserData.setPassword(password)
//saves user to database
await UserData.save()
//returns new users authorization data
return res.json({ user: UserData.toAuthJSON() })
} catch(err) {
//if mongo validation error return callback with error
if(err.name === 'ValidationError') {
return next(boom.unauthorized(err.message))
}
// all other server errors
return next(boom.badImplementation('Something went wrong', err))
}
}
And part of test:
describe('Success', () => {
it('Should create new instance of User with request data', async () => {
const req = { body },
res = {},
local = { password: '1aaaBB', password_2: '1aaaBB'},
constructorStub = sandbox.stub(User.prototype, 'constructor')
req.body.user.auth.local = {...local}
await signup(req, res, next)
expect(constructorStub.calledOnceWith({...req.body.user})).to.be.true
})
})
EDIT: I can verify that is is called with expect(constructorStub.calledOnce).to.be.true
Just can't get to verify data passed.

Edit: After talking for some time sounds like what you need is to validate that you are creating a new user correctly.
My suggestion here is to create a new function createUserFromRequest that would take in request and return a new User.
You can then test this function easily as it's pure (no side effects, just input and output).
At this point, most of the logic in your handler is in this function so it would be probably not worth testing the handler itself, but you could still do it, for example by mocking the function above.
Example:
function createUserFromRequest(request) {
//get user data
const user = req.body.user,
{ auth: { local: { password, password_2 } } } = user
//check if both passwords match
if (password !== password_2)
return next(boom.unauthorized('Passwords do not match.'))
//check if password is valid
if (!Password.validate(password)) {
const errorData = Password.validate(password, { list: true })
return next(boom.notAcceptable('Invalid password.', errorData))
}
//creates new mongo user
const UserData = new User(user)
//sets user password hash
UserData.setPassword(password)
return UserData;
}
Please note: stubs and mocking are usually a code smell: there could either be a better way of testing, or it could be a sign of a need to refactor the code into something more easily testable. They usually point to tightly coupled or cluttered code.
Check out this great article on that topic: https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a

Related

Nestjs Testing in signup BadRequestException: email in use error

user.service.ts
async findWithMail(email:string):Promise<any> {
return this.userRepository.findOne({email});
}
auth.service.ts
async signup(email:string,password:string,name?:string,surname?:string,phone:string){
if(email) {
const users = await this.userService.findWithMail(email);
if(users) {
throw new BadRequestException('email in use');
}
}
if(!password) return {error:"password must be!"};
const salt = randomBytes(8).toString('hex');
const hash = (await scrypt(password,salt,32)) as Buffer;
const result = salt + '.' +hash.toString('hex');
password = result;
const user = await
this.userService.create(email,password,name,surname,phone);
return user;
}
auth.service.spec.ts
let service:AuthService;
let fakeUsersService: Partial<UserService>;
describe('Auth Service',()=>{
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) => Promise.resolve({email,password} as User),
}
const module = await Test.createTestingModule({
providers:[AuthService,{
provide:UserService,
useValue:fakeUsersService
}],
}).compile();
service = module.get(AuthService);
});
it('can create an instance of auth service',async()=> {
expect(service).toBeDefined();
})
it('throws an error if user signs up with email that is in use', async () => {
await service.signup('asdf#asdf.com', 'asdf')
});
})
When ı try to run my test its give me error even this email is not in database its give error: BadRequestException: email in use. I couldnt figure out how to solve problem
You can use isExists method instead of findOne.
Also you can add extra check for your findWithMail method. Check the length of db request result. Like if (dbReqResult.length === 0) return false; else true
please put your attention on your mocked user service, especially on findWithEmail function, this part
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) =>
Promise.resolve({email,password} as User),
}
...
try to resolve the promise to be null not [] (empty array) or change your if(users) on your auth.service to be if(users.length > 0), why? it because empty array means to be thruthy value so when run through this process on your auth.service
if(email) {
const users = await this.userService.findWithMail(email);
// on this part below
if(users) {
throw new BadRequestException('email in use');
}
}
the 'users' executed to be truthy value so it will invoke the error. I hope my explanation will help you, thank you

Code runs after catch statement catches and error and returns in react native firebase

I am having issue whenever I catch an error and return from a function, by code after the catch block still runs. Here is my two functions that I use:
usernameTaken: async (username) => {
const user = await firebase.firestore().collection("uniqueUsers").doc(username).get();
if (user.exists) {
alert("Username is taken. Try again with another username.");
throw new Error('Username is taken. Try again with another username.');
}
},
changeUsername: async (currentUsername, newUsername) => {
try {
var user = Firebase.getCurrentUser();
Firebase.usernameTaken(newUsername);
} catch (err) {
alert(err.message);
return;
}
await db.collection('uniqueUsers').doc(currentUsername).delete();
await db.collection("users").doc(user.uid).update({username: newUsername});
await db.collection("uniqueUsers").doc(newUsername).set({username: newUsername});
alert("Congratulations! You have successfully updated your username.");
}
I would greatly appreciate any help for this problem, as I have been struggling with it for over 2 days now and can't seem to find a solution.
In your original code, the usernameTaken() promise is floating, because you didn't use await. Because it was floating, your catch() handler will never catch it's error.
changeUsername: async (currentUsername, newUsername) => {
try {
const user = Firebase.getCurrentUser();
/* here -> */ await Firebase.usernameTaken(newUsername);
} catch (err) {
alert(err.message);
return;
}
/* ... other stuff ... */
}
Additional Points
usernameTaken should return a boolean
You should change usernameTaken to return a boolean. This is arguably better rather than using alert() (which blocks execution of your code) or throwing an error.
usernameTaken: async (username) => {
const usernameDoc = await firebase.firestore().collection("uniqueUsers").doc(username).get();
return usernameDoc.exists; // return a boolean whether the doc exists
}
Securely claim and release usernames
Based on your current code, you have no protections for someone coming along and just deleting any usernames in your database or claiming a username that was taken between the time you last checked it's availability and when you call set() for the new username. You should secure your database so that a user can only write to a username they own.
Add the owner's ID to the document:
"/uniqueUsers/{username}": {
username: "username",
uid: "someUserId"
}
This then allows you to lock edits/deletions to the user who owns that username.
service cloud.firestore {
match /databases/{database}/documents {
match /uniqueUsers/{username} {
// new docs must have { username: username, uid: currentUser.uid }
allow create: if request.auth != null
&& request.resource.data.username == username
&& request.resource.data.uid == request.auth.uid
&& request.resource.data.keys().hasOnly(["uid", "username"]);
// any logged in user can get this doc
allow read: if request.auth != null;
// only the linked user can delete this doc
allow delete: if request.auth != null
&& request.auth.uid == resource.data.uid;
// only the linked user can edit this doc, as long as username and uid are the same
allow update: if request.auth != null
&& request.auth.uid == resource.data.uid
&& request.resource.data.diff(resource.data).unchangedKeys().hasAll(["uid", "username"]) // make sure username and uid are unchanged
&& request.resource.data.diff(resource.data).changedKeys().size() == 0; // make sure no other data is added
}
}
}
Atomically update your database
You are modifying your database in a way that could corrupt it. You could delete the old username, then fail to update your current username which would mean that you never link your new username. To fix this, you should use a batched write to apply all these changes together. If any one were to fail, nothing is changed.
await db.collection("uniqueUsers").doc(currentUsername).delete();
await db.collection("users").doc(user.uid).update({username: newUsername});
await db.collection("uniqueUsers").doc(newUsername).set({username: newUsername});
becomes
const db = firebase.firestore();
const batch = db.batch();
batch.delete(db.collection("uniqueUsers").doc(currentUsername));
batch.update(db.collection("users").doc(user.uid), { username: newUsername });
batch.set(db.collection("uniqueUsers").doc(newUsername), { username: newUsername });
await batch.commit();
Usernames should be case-insensitive
Your current usernames are case-sensitive which is not recommended if you expect your users to type/write out their profile's URL. Consider how "example.com/MYUSERNAME", "example.com/myUsername" and "example.com/myusername" would all be different users. If someone scribbled out their username on a piece of paper, you'd want all of those to go to the same user's profile.
usernameTaken: async (username) => {
const usernameDoc = await firebase.firestore().collection("uniqueUsers").doc(username.toLowerCase()).get();
return usernameDoc.exists; // return a boolean whether the doc exists
},
changeUsername: async (currentUsername, newUsername) => {
const lowerCurrentUsername = currentUsername.toLowerCase();
const lowerNewUsername = newUsername.toLowerCase();
/* ... */
return lowerNewUsername; // return the new username to show success
}
The result
Combining this all together, gives:
usernameTaken: async (username) => {
const usernameDoc = await firebase.firestore().collection("uniqueUsers").doc(username).get();
return usernameDoc.exists; // return a boolean
},
changeUsername: async (currentUsername, newUsername) => {
const user = Firebase.getCurrentUser();
if (user === null) {
throw new Error("You must be signed in first!");
}
const taken = await Firebase.usernameTaken(newUsername);
if (taken) {
throw new Error("Sorry, that username is taken.");
}
const lowerCurrentUsername = currentUsername.toLowerCase();
const lowerNewUsername = newUsername.toLowerCase();
const db = firebase.firestore();
const batch = db.batch();
batch.delete(db.collection("uniqueUsers").doc(lowerCurrentUsername));
batch.update(db.collection("users").doc(user.uid), {
username: lowerNewUsername
});
batch.set(db.collection("uniqueUsers").doc(lowerNewUsername), {
username: lowerNewUsername,
uid: user.uid
});
await batch.commit();
return lowerNewUsername;
}
// elsewhere in your code
changeUsername("olduser", "newuser")
.then(
(username) => {
alert("Your username was successfully changed to #" + username + "!");
},
(error) => {
console.error(error);
alert("We couldn't update your username!");
}
);
Note: If you are using all of the above recommendations (like the security rules), one of the expected ways batch.commit() will fail is if someone takes the username before the current user. If you get a permissions error, assume that someone took the username before you.
Try this check if your values are empty or not defined throw some error in try block e.g.
cosnt user = Firebase.getCurrentUser();
const name = Firebase.usernameTaken(newUsername);
// throwing error
if(name == "") throw "is empty";
await db.collection('uniqueUsers').doc(currentUsername).delete();

how to create a user with firebase without signing in [duplicate]

So I have this issue where every time I add a new user account, it kicks out the current user that is already signed in. I read the firebase api and it said that "If the new account was created, the user is signed in automatically" But they never said anything else about avoiding that.
//ADD EMPLOYEES
addEmployees: function(formData){
firebase.auth().createUserWithEmailAndPassword(formData.email, formData.password).then(function(data){
console.log(data);
});
},
I'm the admin and I'm adding accounts into my site. I would like it if I can add an account without being signed out and signed into the new account. Any way i can avoid this?
Update 20161110 - original answer below
Also, check out this answer for a different approach.
Original answer
This is actually possible.
But not directly, the way to do it is to create a second auth reference and use that to create users:
var config = {apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com"};
var secondaryApp = firebase.initializeApp(config, "Secondary");
secondaryApp.auth().createUserWithEmailAndPassword(em, pwd).then(function(firebaseUser) {
console.log("User " + firebaseUser.uid + " created successfully!");
//I don't know if the next statement is necessary
secondaryApp.auth().signOut();
});
If you don't specify which firebase connection you use for an operation it will use the first one by default.
Source for multiple app references.
EDIT
For the actual creation of a new user, it doesn't matter that there is nobody or someone else than the admin, authenticated on the second auth reference because for creating an account all you need is the auth reference itself.
The following hasn't been tested but it is something to think about
The thing you do have to think about is writing data to firebase. Common practice is that users can edit/update their own user info so when you use the second auth reference for writing this should work. But if you have something like roles or permissions for that user make sure you write that with the auth reference that has the right permissions. In this case, the main auth is the admin and the second auth is the newly created user.
Update 20161108 - original answer below
Firebase just released its firebase-admin SDK, which allows server-side code for this and other common administrative use-cases. Read the installation instructions and then dive into the documentation on creating users.
original answer
This is currently not possible. Creating an Email+Password user automatically signs that new user in.
I just created a Firebase Function that triggers when a Firestore document is Created (with rules write-only to admin user). Then use admin.auth().createUser() to create the new user properly.
export const createUser = functions.firestore
.document('newUsers/{userId}')
.onCreate(async (snap, context) => {
const userId = context.params.userId;
const newUser = await admin.auth().createUser({
disabled: false,
displayName: snap.get('displayName'),
email: snap.get('email'),
password: snap.get('password'),
phoneNumber: snap.get('phoneNumber')
});
// You can also store the new user in another collection with extra fields
await admin.firestore().collection('users').doc(newUser.uid).set({
uid: newUser.uid,
email: newUser.email,
name: newUser.displayName,
phoneNumber: newUser.phoneNumber,
otherfield: snap.get('otherfield'),
anotherfield: snap.get('anotherfield')
});
// Delete the temp document
return admin.firestore().collection('newUsers').doc(userId).delete();
});
You can Algo use functions.https.onCall()
exports.createUser= functions.https.onCall((data, context) => {
const uid = context.auth.uid; // Authorize as you want
// ... do the same logic as above
});
calling it.
const createUser = firebase.functions().httpsCallable('createUser');
createUser({userData: data}).then(result => {
// success or error handling
});
Swift 5: Simple Solution
First store the current user in a variable called originalUser
let originalUser = Auth.auth().currentUser
Then, in the completion handler of creating a new user, use the updateCurrentUser method to restore the original user
Auth.auth().updateCurrentUser(originalUser, completion: nil)
Here is a simple solution using web SDKs.
Create a cloud function (https://firebase.google.com/docs/functions)
import admin from 'firebase-admin';
import * as functions from 'firebase-functions';
const createUser = functions.https.onCall((data) => {
return admin.auth().createUser(data)
.catch((error) => {
throw new functions.https.HttpsError('internal', error.message)
});
});
export default createUser;
Call this function from your app
import firebase from 'firebase/app';
const createUser = firebase.functions().httpsCallable('createUser');
createUser({ email, password })
.then(console.log)
.catch(console.error);
Optionally, you can set user document information using the returned uid.
createUser({ email, password })
.then(({ data: user }) => {
return database
.collection('users')
.doc(user.uid)
.set({
firstname,
lastname,
created: new Date(),
});
})
.then(console.log)
.catch(console.error);
I got André's very clever workaround working in Objective-C using the Firebase iOS SDK:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"GoogleService-Info" ofType:#"plist"];
FIROptions *secondaryAppOptions = [[FIROptions alloc] initWithContentsOfFile:plistPath];
[FIRApp configureWithName:#"Secondary" options:secondaryAppOptions];
FIRApp *secondaryApp = [FIRApp appNamed:#"Secondary"];
FIRAuth *secondaryAppAuth = [FIRAuth authWithApp:secondaryApp];
[secondaryAppAuth createUserWithEmail:user.email
password:user.password
completion:^(FIRUser * _Nullable user, NSError * _Nullable error) {
[secondaryAppAuth signOut:nil];
}];
Update for Swift 4
I have tried a few different options to create multiple users from a single account, but this is by far the best and easiest solution.
Original answer by Nico
First Configure firebase in your AppDelegate.swift file
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
FirebaseApp.configure(name: "CreatingUsersApp", options: FirebaseApp.app()!.options)
return true
}
Add the following code to action where you are creating the accounts.
if let secondaryApp = FirebaseApp.app(name: "CreatingUsersApp") {
let secondaryAppAuth = Auth.auth(app: secondaryApp)
// Create user in secondary app.
secondaryAppAuth.createUser(withEmail: email, password: password) { (user, error) in
if error != nil {
print(error!)
} else {
//Print created users email.
print(user!.email!)
//Print current logged in users email.
print(Auth.auth().currentUser?.email ?? "default")
try! secondaryAppAuth.signOut()
}
}
}
}
You can use firebase function for add users.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const cors = require('cors')({
origin: true,
});
exports.AddUser = functions.https.onRequest(( req, res ) => {
// Grab the text parameter.
cors( req, res, () => {
let email = req.body.email;
let passwd = req.body.passwd;
let role = req.body.role;
const token = req.get('Authorization').split('Bearer ')[1];
admin.auth().verifyIdToken(token)
.then(
(decoded) => {
// return res.status(200).send( decoded )
return creatUser(decoded);
})
.catch((err) => {
return res.status(401).send(err)
});
function creatUser(user){
admin.auth().createUser({
email: email,
emailVerified: false,
password: passwd,
disabled: false
})
.then((result) => {
console.log('result',result);
return res.status(200).send(result);
}).catch((error) => {
console.log(error.message);
return res.status(400).send(error.message);
})
}
});
});
CreateUser(){
//console.log('Create User')
this.submitted = true;
if (this.myGroup.invalid) {
return;
}
let Email = this.myGroup.value.Email;
let Passwd = this.myGroup.value.Passwd;
let Role = 'myrole';
let TechNum = this.myGroup.value.TechNum;
let user = JSON.parse(localStorage.getItem('user'));
let role = user.role;
let AdminUid = user.uid;
let authToken = user.stsTokenManager.accessToken;
let httpHeaders = new HttpHeaders().set('Authorization', 'Bearer ' + authToken);
let options = { headers: httpHeaders };
let params = { email:Email,passwd:Passwd,role:Role };
this.httpClient.post('https://us-central1-myproject.cloudfunctions.net/AddUser', params, options)
.subscribe( val => {
//console.log('Response from cloud function', val );
let createdUser:any = val;
//console.log(createdUser.uid);
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${createdUser.uid}`);
const userUpdate = {
uid: createdUser.uid,
email: createdUser.email,
displayName: null,
photoURL: null,
emailVerified: createdUser.emailVerified,
role: Role,
TechNum:TechNum,
AccountAccess:this.AccountAccess,
UserStatus:'open',
OwnerUid:AdminUid,
OwnerUidRole:role,
RootAccountAccess:this.RootAccountAccess
}
userRef.set(userUpdate, {
merge: false
});
this.toastr.success('Success, user add','Success');
this.myGroup.reset();
this.submitted = false;
},
err => {
console.log('HTTP Error', err.error)
this.toastr.error(err.error,'Error')
},
() => console.log('HTTP request completed.')
);
}
On the web, this is due to unexpected behavior when you call createUserWithEmailAndPassword out of the registration context; e.g. inviting a new user to your app by creating a new user account.
Seems like, createUserWithEmailAndPassword method triggers a new refresh token and user cookies are updated too. (This side-effect is not documented)
Here is a workaround for Web SDK:
After creating the new user;
firebase.auth().updateCurrentUser (loggedInUser.current)
provided that you initiate loggedInUser with the original user beforehand.
Hey i had similar problem ,trying to create users through admin , as it is not possible to signUp user without signIn ,I created a work around ,adding it below with steps
Instead of signup create a node in firebase realtime db with email as key (firebase do not allow email as key so I have created a function to generate key from email and vice versa, I will attach the functions below)
Save a initial password field while saving user (can even hash it with bcrypt or something, if you prefer though it will be used one time only)
Now Once user try to login check if any node with that email (generate key from email) exist in the db and if so then match the password provided.
If the password matched delete the node and do authSignUpWithEmailandPassword with provided credentials.
User is registered successfully
//Sign In
firebaseDB.child("users").once("value", (snapshot) => {
const users = snapshot.val();
const userKey = emailToKey(data.email);
if (Object.keys(users).find((key) => key === userKey)) {
setError("user already exist");
setTimeout(() => {
setError(false);
}, 2000);
setLoading(false);
} else {
firebaseDB
.child(`users`)
.child(userKey)
.set({ email: data.email, initPassword: data.password })
.then(() => setLoading(false))
.catch(() => {
setLoading(false);
setError("Error in creating user please try again");
setTimeout(() => {
setError(false);
}, 2000);
});
}
});
//Sign Up
signUp = (data, setLoading, setError) => {
auth
.createUserWithEmailAndPassword(data.email, data.password)
.then((res) => {
const userDetails = {
email: res.user.email,
id: res.user.uid,
};
const key = emailToKey(data.email);
app
.database()
.ref(`users/${key}`)
.remove()
.then(() => {
firebaseDB.child("users").child(res.user.uid).set(userDetails);
setLoading(false);
})
.catch(() => {
setLoading(false);
setError("error while registering try again");
setTimeout(() => setError(false), 4000);
});
})
.catch((err) => {
setLoading(false);
setError(err.message);
setTimeout(() => setError(false), 4000);
});
};
//Function to create a valid firebase key from email and vice versa
const emailToKey = (email) => {
//firebase do not allow ".", "#", "$", "[", or "]"
let key = email;
key = key.replace(".", ",0,");
key = key.replace("#", ",1,");
key = key.replace("$", ",2,");
key = key.replace("[", ",3,");
key = key.replace("]", ",4,");
return key;
};
const keyToEmail = (key) => {
let email = key;
email = email.replace(",0,", ".");
email = email.replace(",1,", "#");
email = email.replace(",2,", "$");
email = email.replace(",3,", "[");
email = email.replace(",4,", "]");
return email;
};
If you want to do it in your front end create a second auth reference use it to create other users and sign out and delete that reference. If you do it this way you won't be signed out when creating a new user and you won't get the error that the default firebase app already exists.
const createOtherUser =()=>{
var config = {
//your firebase config
};
let secondaryApp = firebase.initializeApp(config, "secondary");
secondaryApp.auth().createUserWithEmailAndPassword(email, password).then((userCredential) => {
console.log(userCredential.user.uid);
}).then(secondaryApp.auth().signOut()
)
.then(secondaryApp.delete()
)
}
Update 19.05.2022 - using #angular/fire (latest available = v.7.3.0)
If you are not using firebase directly in your app, but use e.g. #angular/fire for auth purposes only, you can use the same approach as suggested earlier as follows with the #angular/fire library:
import { Auth, getAuth, createUserWithEmailAndPassword } from '#angular/fire/auth';
import { deleteApp, initializeApp } from '#angular/fire/app';
import { firebaseConfiguration } from '../config/app.config'; // <-- Your project's configuration here.
const tempApp = initializeApp(firebaseConfiguration, "tempApp");
const tempAppAuth = getAuth(tempApp);
await createUserWithEmailAndPassword(tempAppAuth, email, password)
.then(async (newUser) => {
resolve( () ==> {
// Do something, e.g. add user info to database
});
})
.catch(error => reject(error))
.finally( () => {
tempAppAuth.signOut()
.then( () => deleteApp(tempApp));
});
The Swift version:
FIRApp.configure()
// Creating a second app to create user without logging in
FIRApp.configure(withName: "CreatingUsersApp", options: FIRApp.defaultApp()!.options)
if let secondaryApp = FIRApp(named: "CreatingUsersApp") {
let secondaryAppAuth = FIRAuth(app: secondaryApp)
secondaryAppAuth?.createUser(...)
}
Here is a Swift 3 adaptaion of Jcabrera's answer :
let bundle = Bundle.main
let path = bundle.path(forResource: "GoogleService-Info", ofType: "plist")!
let options = FIROptions.init(contentsOfFile: path)
FIRApp.configure(withName: "Secondary", options: options!)
let secondary_app = FIRApp.init(named: "Secondary")
let second_auth = FIRAuth(app : secondary_app!)
second_auth?.createUser(withEmail: self.username.text!, password: self.password.text!)
{
(user,error) in
print(user!.email!)
print(FIRAuth.auth()?.currentUser?.email ?? "default")
}
If you are using Polymer and Firebase (polymerfire) see this answer: https://stackoverflow.com/a/46698801/1821603
Essentially you create a secondary <firebase-app> to handle the new user registration without affecting the current user.
Android solution (Kotlin):
1.You need FirebaseOptions BUILDER(!) for setting api key, db url, etc., and don't forget to call build() at the end
2.Make a secondary auth variable by calling FirebaseApp.initializeApp()
3.Get instance of FirebaseAuth by passing your newly created secondary auth, and do whatever you want (e.g. createUser)
// 1. you can find these in your project settings under general tab
val firebaseOptionsBuilder = FirebaseOptions.Builder()
firebaseOptionsBuilder.setApiKey("YOUR_API_KEY")
firebaseOptionsBuilder.setDatabaseUrl("YOUR_DATABASE_URL")
firebaseOptionsBuilder.setProjectId("YOUR_PROJECT_ID")
firebaseOptionsBuilder.setApplicationId("YOUR_APPLICATION_ID") //not sure if this one is needed
val firebaseOptions = firebaseOptionsBuilder.build()
// indeterminate progress dialog *ANKO*
val progressDialog = indeterminateProgressDialog(resources.getString(R.string.progressDialog_message_registering))
progressDialog.show()
// 2. second auth created by passing the context, firebase options and a string for secondary db name
val newAuth = FirebaseApp.initializeApp(this#ListActivity, firebaseOptions, Constants.secondary_db_auth)
// 3. calling the create method on our newly created auth, passed in getInstance
FirebaseAuth.getInstance(newAuth).createUserWithEmailAndPassword(email!!, password!!)
.addOnCompleteListener { it ->
if (it.isSuccessful) {
// 'it' is a Task<AuthResult>, so we can get our newly created user from result
val newUser = it.result.user
// store wanted values on your user model, e.g. email, name, phonenumber, etc.
val user = User()
user.email = email
user.name = name
user.created = Date().time
user.active = true
user.phone = phone
// set user model on /db_root/users/uid_of_created_user/, or wherever you want depending on your structure
FirebaseDatabase.getInstance().reference.child(Constants.db_users).child(newUser.uid).setValue(user)
// send newly created user email verification link
newUser.sendEmailVerification()
progressDialog.dismiss()
// sign him out
FirebaseAuth.getInstance(newAuth).signOut()
// DELETE SECONDARY AUTH! thanks, Jimmy :D
newAuth.delete()
} else {
progressDialog.dismiss()
try {
throw it.exception!!
// catch exception for already existing user (e-mail)
} catch (e: FirebaseAuthUserCollisionException) {
alert(resources.getString(R.string.exception_FirebaseAuthUserCollision), resources.getString(R.string.alertDialog_title_error)) {
okButton {
isCancelable = false
}
}.show()
}
}
}
For Android, i suggest a simpler way to do it, without having to provide api key, application id...etc by hand by just using the FirebaseOptions of the default instance.
val firebaseDefaultApp = Firebase.auth.app
val signUpAppName = firebaseDefaultApp.name + "_signUp"
val signUpApp = try {
FirebaseApp.initializeApp(
context,
firebaseDefaultApp.options,
signUpAppName
)
} catch (e: IllegalStateException) {
// IllegalStateException is throw if an app with the same name has already been initialized.
FirebaseApp.getInstance(signUpAppName)
}
// Here is the instance you can use to sign up without triggering auth state on the default Firebase.auth
val signUpFirebaseAuth = Firebase.auth(signUpApp)
How to use ?
signUpFirebaseAuth
.createUserWithEmailAndPassword(email, password)
.addOnSuccessListener {
// Optional, you can send verification email here if you need
// As soon as the sign up with sign in is over, we can sign out the current user
firebaseAuthSignUp.signOut()
}
.addOnFailureListener {
// Log
}
My solution to this question is to store the User Name/Email and password in a static class and then add a new user log out the new user and immediately log in as the admin user(id pass you saved). Works like a charm for me :D
This is a version for Kotlin:
fun createUser(mail: String, password: String) {
val opts = FirebaseOptions.fromResource(requireContext())
if (opts == null) return
val app = Firebase.initialize(requireContext(), opts, "Secondary")
FirebaseAuth.getInstance(app)
.createUserWithEmailAndPassword(mail, password)
.addOnSuccessListener {
app.delete()
doWhateverWithAccount(it)
}.addOnFailureListener {
app.delete()
showException(it)
}
}
It uses the configuration from your default Firebase application instance, just under a different name.
It also deletes the newly created instance afterwards, so you can call this multiple times without any exception about already existing Secondary application.

Adonis JS Primary key value is missing for user

I'm trying to make a migratiion of users model from adonis to postgres but I keep getting this error Primary key value is missing for user
My model look like this:
class User extends Model {
static boot () {
super.boot()
this.addHook('beforeSave', async (userInstance) => {
if (userInstance.dirty.password) {
userInstance.password = await Hash.make(userInstance.password)
}
})
}
tokens () {
return this.hasMany('App/Models/Token')
}
}
module.exports = User
And the migration I'm trying to run is:
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class UserSchema extends Schema {
up () {
this.create('users', (table) => {
table.increments()
table.string('username', 80).notNullable().unique()
table.string('email', 254).notNullable().unique()
table.string('password', 60).notNullable()
table.timestamps()
})
}
down () {
this.drop('users')
}
}
module.exports = UserSchema
I tried adding primary() to table.increments() asuming it is generating the auto increments id in postgre database. When I check the database the user is sometimes added, but with id's that goes from 2 to 4 and so on
The controller looks like this
const User = use('App/Models/User')
class UserController {
async register({request, auth, response}) {
const username = request.input("username")
const email = request.input("email")
const password = request.input("password")
let user = new User()
user.username = username
user.email = email
user.password = password
user = await user.save()
let accessToken = await auth.generate(user)
return response.json({"user": user, "access_token": accessToken})
}
async login({request, auth, response}) {
const email = request.input("email")
const password = request.input("password");
try {
if (await auth.attempt(email, password)) {
let user = await User.findBy('email', email)
let accessToken = await auth.generate(user)
return response.json({"user":user, "access_token": accessToken})
}
}
catch (e) {
return response.json({message: 'You first need to register!'})
}
}
show ({ auth, params }) {
if (auth.user.id !== Number(params.id)) {
return "You cannot see someone else's profile"
}
return auth.user
}
}
module.exports = UserController
The problem is that you overwrite user model instance in user = await user.save().
as #Costin say's simply storing result of await user.save() in new variable will solve the issue.
for example:
...
let saved = await user.save()
let accessToken = await auth.generate(user)
...
table.increments() need column name as parameter -> table.increments(name)
Please read official knex documentation : https://knexjs.org/#Schema-increments
Example :
table.increments('id').primary()
The issue is with
user = await user.save()
that will return true when user is saved to db, passing that to auth.generate is what cause that error.
Just use something like
let success = await user.save()
and check success value after. Might want to return the token and 200 status only if user is saved (success is true).

TypeError: Cannot set property 'user' of undefined<br> at [file path] sqlite3

I am receiving an undefined error when attempting to set a session for the user upon validation of credentials on login. I am trying to use express-session to create the session for the user but do not have it directly imported into the file (I was guided to not do so) but am unsure how to resolve this error given. Any help and insight would be much appreciated!
End point:
router.post("/login", async (req, res, next) => {
try {
const { username, password } = req.body
// * checks for record existence in db, assigns record to var for access
const user = await users_access.findByFilter({ username })
if (!user) {
return res.status(401).json({ message: 'invalid crededentials' });
}
// * compares the entered password to the hash on record
const passwordValid = await secure.compare(password, user.password)
// * handling invalid responses + creating session
if (!passwordValid) {
return res.status(401).json({ message: 'invalid credentials' });
}
req.session.user = user
res.json({ message: `welcome, ${user.username}!`})
} catch(error) {
next(error)
}
});
application model:
// * add users to the datbase
// * inserts argument into user table in db access
// * returns a user found by id
const add = async (user) => {
const [id] = await database_access("users")
.insert(user)
return findById(id)
}
// * find user record with username and password
const find = () => {
return database_access("users").select("id", "username")
}
// * find user by filter
const findByFilter = (filter) => {
return database_access("users")
.select("id", "username", "password")
.where(filter)
.first()
}
// * find user with id
const findById = (id) => {
return database_access("users")
.select("id", "username")
.where({ id }) // desctructuring id from the request
.first()
}
module.exports = {
add,
find,
findByFilter,
findById
}
if you need to see any additional code to assess I am happy to provide but believe this is the source of issue per the error response. Thank you in advanced!
so I guess you are using the express-session module in your entry file for the server, app.js, server.js, index.js however you call it.
this login handler require to be used in a context where the session is available.
I think what you want is not a unit test for this particular router, but an integration test, to test this router in the context of your app.
This is all I can see from the information you provided. If this was not helpful enough, maybe you can show us your servers main file. and how this router is used.

Categories

Resources