When signing up a user with email and password, or any other sign in method, with an email example#gmail.com. And then when signing in with google , with example#gmail.com, the google sign in overrides that previous email that was already used.
Here are the functions I'm using:
For email and password:
const handleSignup = () => {
console.log('signup')
clearErrors()
firebase.auth().createUserWithEmailAndPassword(email, password).catch(err => {
switch(err.code) {
case "auth/email-already-in-use":
setEmailError(err.message)
break
case "auth/invalid-email":
setEmailError(err.message)
break
case "auth/weak-password":
setPasswordError(err.message)
break
default:
setEmailError('Invalid')
}
})
firebase.auth().onAuthStateChanged(user => {
if(user) {
user.updateProfile({
displayName: name,
})
db.collection('users').doc(user.uid).set({
created: new Date(),
msgids,
uid: firebase.auth().currentUser.uid,
online: true,
userinfo: {
name,
cover,
age: '',
phone: '',
city: '',
country: '',
website: 'https://',
job: '',
email,
},
customization: {
color: '#10325c',
themecolor: '#0f6ce6',
darkmode: false,
widemode: false,
}
})
db.collection('notifications').doc(user.uid).set({
notifications: []
})
}//if (user)
else {
setUser('')
}
})
}
For Google:
function loginwithGoogle(){
var provider = new firebase.auth.GoogleAuthProvider();
provider.addScope('email');
firebase.auth()
.signInWithPopup(provider)
.then((result) => {
if(result.additionalUserInfo.isNewUser){
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
db.collection('users').doc(user.uid).set({
created: new Date(),
msgids,
uid: user.uid,
online: true,
userinfo: {
name: user.displayName,
cover: user.photoURL,
age: '',
phone: user.phoneNumber,
city:'',
country: '',
website: 'https://',
job: '',
email:user.email
},
customization: {
color: '#10325c',
themecolor: '#0f6ce6',
darkmode: false,
widemode: false,
}
})
db.collection('notifications').doc(user.uid).set({
notifications: []
})
}
}).catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
setEmailError(error.message)
setTimeout(()=>{
setEmailError('')
},3000)
});
}
Any idea why google sign up is overriding other email?
Related
I'm using bell (https://www.npmjs.com/package/bell) for authentication plugin. I used version 10.0.0.
I've setup the configuration in Apple developer site. I got this issue:
ERROR secretOrPrivateKey must be an asymmetric key when using ES256
Here is my apple authentication plugin for hapi
const jwt = require('jsonwebtoken')
const jwksClient = require('jwks-rsa')
const consola = require('consola')
const config = require('../config/server')
const client = jwksClient({
jwksUri: 'https://appleid.apple.com/auth/keys',
timeout: 30000
})
function getSecretKey () {
const claims = {
iss: config.authAppleSecretTeamId,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 86400 * 180,
aud: 'https://appleid.apple.com',
sub: config.authAppleId
}
const token = jwt.sign(claims, config.authAppleSecretPrivateKey, {
algorithm: 'ES256',
keyid: config.authAppleSecretKeyId
})
return token
}
function getApplePublicKey (kid) {
return new Promise((resolve) => {
client.getSigningKey(kid, (_, key) => {
const publicKey = key.getPublicKey()
resolve(publicKey)
})
})
}
const appleProvider = {
auth: 'https://appleid.apple.com/auth/authorize',
token: 'https://appleid.apple.com/auth/token',
name: 'apple',
protocol: 'oauth2',
useParamsAuth: true,
profile: async (credentials, params) => {
const { header } = jwt.decode(params.id_token, { complete: true })
const publicKey = await getApplePublicKey(header.kid)
const resp = jwt.verify(params.id_token, publicKey)
if (config.environment !== 'production') {
console.log('appleProvider -> params, header, resp : ', params, header, resp)
}
const { sub, email, name } = resp
credentials.profile = {
id: sub,
email,
name: {
first: name.firstName || '',
last: name.lastName || ''
}
}
}
}
module.exports.plugin = {
name: 'auth-apple',
register: (server, options) => {
const strategy = {
provider: appleProvider,
providerParams: { response_mode: 'form_post' },
scope: ['name', 'email'],
password: config.authApplePassword,
clientId: config.authAppleId,
clientSecret: getSecretKey(),
location: config.baseUrl,
forceHttps: true,
isSecure: true
}
if (['development', 'localtest'].includes(config.environment)) {
consola.log(`Un-setting https for Apple auth strategy in ${config.environment}`)
delete strategy.location
delete strategy.forceHttps
strategy.isSecure = false
}
server.auth.strategy('apple', 'bell', strategy)
}
}
Also bell doesn't have default provider for Apple sign-in, we have to write it.
Any suggestion or guidance will be appreciated
I create a custom user registration because nextauth does not support registration. Everything works correctly but I do not know how after a successful registration of the user immediately log him in the credentials that he gave during registration.
As far as I can see, the signIn method from nextauth does not allow any credentials to be passed on to it. Redirects only to the login subpage.
I also did not find any endpoint that provides nextauth to which I can pass parameters so as to log in the user.
In fact, it is enough to call the authorize method that is in nextauth, unfortunately, there is no possibility to export it or there is no way to call it from the call to api level and it is a pity because with it I could log in the user.
User flow
User registers if the registration is successful, he is immediately logged in credentials that he provided during registration
My registration
async function handleRegister(
username: string,
email: string,
password: string
) {
const registerUser = await fetch(
`${process.env.API_URL}`,
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
username,
email,
password,
}),
}
);
const registerResponse = await registerUser.json();
if (registerResponse.user) {
// TODO: Login with NextAuth
}
}
[...nextauth].ts
export default NextAuth({
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
identifier: {
label: "Email or Username",
type: "text",
placeholder: "jsmith",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/auth/local`,
{
method: "POST",
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" },
}
);
const user = await res.json();
if (res.ok && user) {
return user;
}
return null;
},
}),
],
session: {
strategy: "jwt",
},
jwt: {
maxAge: 60,
encode: async ({ secret, token }) => {
const encodedToken = jsonwebtoken.sign(token!, secret, {
algorithm: "HS256",
});
return encodedToken;
},
decode: async ({ secret, token }) => {
const decodedToken = jsonwebtoken.verify(token!, secret, {
algorithms: ["HS256"],
});
return decodedToken as JWT;
},
},
callbacks: {
jwt: async (token, user, account) => {
const isSignIn = user ? true : false;
if (isSignIn) {
token.jwt = user.jwt;
token.id = user.user.id;
token.name = user.user.username;
token.role = user.user.user_role;
token.email = user.user.email;
}
return Promise.resolve(token);
},
session: async ({ session, token }) => {
if (session.user) {
session.user.id = token.sub!;
}
return session;
},
},
secret: process.env.JWT_SECRET,
});
You need to login with credentials e.g
const response: SignInResponse | undefined = await signIn(
"credentials",
{
redirect: false,
email: "example#user.com",
password: "12345678",
}
);
So I'm having trouble authenticating a user login using express for the backend. If I do a simple res.send I could get a response in postman. but if I do a check if the user and password check and generate a token if says error 401 invalid usernames and password. mind the tokens work for register and update profiles. I also attached the user schema.
Simple Approach
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
res.send({ email, password })
})
When I try to check and generate a token
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid Email or password')
}
})
Also here's my user schema using mongoose and added a function to check password since in the database it's encrypted using bcrypt.js
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
You see, your code is absolutely correct, but only if you have saved the user correctly in your model at the first. In addition, the password used during storage is hashed in the same way and from the same module.
Now you have to change your checking terms because there may not really be a user with this email in the database to match your password and return true value.
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.pre('save', function (next) {
if (this.password) {
bcrypt.hash(this.password, bcrypt.genSaltSync(15), (err, hash) => {
if (err) console.log(err);
this.password = hash;
next();
});
}
}
userSchema.methods.Save = async (data) => {
let model=new User(data)
await model.save();
}
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
and update this code:
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user) {
if(await user.matchPassword(password)) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid password')
}
} else {
res.status(401)
throw new Error('Invalid Email')
}
})
Now you know which part of the job is the problem. No user or no wrist password
Am working with mongoose and have two models. The User model and the Service model, when a user logs in the method will findOne() user if one exists or create() a new user based on the what's passed in from req.body.
My Service Schema is like this:
const serviceSchema = new mongoose.Schema({
name: {
type: String,
default: 'contentEditor'
},
display: {
type: String,
default: 'Content Editor'
},
accessLevel: {
type: Number,
min: 0,
max: 4,
default: 4
}
});
My User Schema is a bit bigger, I've removed some of the field/value pairs but the part where I embed the Service Schema looks like this:
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: [true, 'Must have a email address'],
trim: true,
unique: true,
},
firstName: {
type: String,
},
lastName: {
type: String,
},
services: {
type: [serviceSchema],
ref: 'Services',
default: [serviceSchema],
},
},
);
When I hit the /api/v1/login endpoint a new user will be created with the Service document correctly but within the Mongoose database only a User collection exists. How do I make it so that both a Users collection and Services collection are created?
Edit: Below is the function that I create/find the user with when they login. When an existing User is found, by their email it will return that user if the user is not found then it will create a new one...
Both behaviours are as expected including adding the Services to the newly created User. What isn't expected is that only ONE collection is added to the DB.
const login = catchAsync(async ({ body: { email, password } }, res, next) => {
if (!email || !password) {
return next(new AppError('Please provide email and password', 400));
}
const { Success } = await webApi(email, password);
const mongoUser = await User.findOne({ email });
if (Success && mongoUser) {
return createSendtoken(mongoUser, 200, res);
}
if (Success && !mongoUser) {
const newUser = await User.create({ email });
return createSendtoken(newUser, 201, res);
}
return next(new AppError('User not found', 404));
});
Make sure you are making the serviceSchema a mongoose model.
const Services = mongoose.model('Service', serviceSchema)
You also have to save it using mongooses model.save() function
Here Hapi Lab why Test failed when all the tests are passed why the test is failed.
In this new question why I get The following leaks were detected:lr even if there is no global var in the code.
Running this simple test
var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var server = require('../../');
lab.experiment('Users', function () {
lab.test('create joi required', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
lab.test('create', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload:{
firstname: 'Fabio',
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var token = response.result.token;
var payload = options.payload;
Code.expect(response.statusCode).to.equal(201);
done();
});
});
});
2 tests complete
Test duration: 363 ms
The following leaks were detected:lr
but I don't see any lr var !
and the strange is if I run this
payload.passdword
instead of
payload.password
var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var server = require('../../');
lab.experiment('Users', function () {
lab.test('create joi required', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
lab.test('create', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload:{
firstname: 'Fabio',
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
passdword: 'mysecret'
}
};
server.inject(options, function(response) {
var token = response.result.token;
var payload = options.payload;
Code.expect(response.statusCode).to.equal(201);
done();
});
});
});
I've got
1 of 2 tests failed
Test duration: 73 ms
No global variable leaks detected
with no warning about lr var.
So I don't know which way to turn :(
Can help me, please ?
UPDATE
controller
'use strict';
/**
* Module dependencies.
*/
var BcryptUtil = require('../utils/bcrypt');
var JwtUtil = require('../utils/jwt');
var Models = require('../models');
var ReplyUtil = require('../utils/reply');
var User = Models.users;
exports.create = function create(request, reply) {
var params = request.payload;
params.password = BcryptUtil.generateHash(params.password);
params.roles =JSON.stringify(['user']);
User
.create(params)
.then(function(user) {
var token = JwtUtil.getUserToken(user);
var redisClient = request.server.plugins['hapi-redis'].client;
redisClient.set('user_'+user.userId, token);
return reply(ReplyUtil.ok(token)).created('/api/users/' + user.userId);
})
.catch(function(err){
if(err instanceof Models.Sequelize.ValidationError){
return reply(ReplyUtil.badData(err,params));
}
return reply(ReplyUtil.badImplementation(err));
});
};
exports.findAll = function (request, reply) {
User
.findAll({
order: [['createdAt','DESC']],
attributes: ['userId', 'firstname', 'lastname', 'username', 'email']
})
.then(function(users) {
return reply(ReplyUtil.ok(users));
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.findById = function (request, reply) {
var userId = request.params.userId;
User
.findById(
userId,
{
attributes: ['userId', 'firstname', 'lastname', 'username', 'email']
})
.then(function(user) {
if(!user){
return reply(ReplyUtil.notFound({userId:userId}));
}
return reply(ReplyUtil.ok(user));
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.update = function (request, reply) {
var userId = request.params.userId;
var params =request.payload;
User
.update(params,{
where: {
userId: userId
}
})
.then(function(rows) {
var affectedRows = rows.pop();
if(!affectedRows){
return reply(ReplyUtil.notFound({userId:userId}));
}
return reply(ReplyUtil.ok(affectedRows));
})
.catch(function(err){
if(err instanceof Models.Sequelize.ValidationError){
return reply(ReplyUtil.badData(err,params));
}
return reply(ReplyUtil.badImplementation(err));
});
};
exports.destroy = function (request, reply) {
var userId = request.params.userId;
User
.destroy({
where: {
userId: userId
}
})
.then(function(rows) {
if(!rows){
return reply(ReplyUtil.notFound({userId:userId}));
}
return reply(ReplyUtil.ok(rows));
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.signIn = function (request, reply) {
var params = request.payload;
User
.findOne({
where: {
email: params.email
}
})
.then(function(user) {
if(!user){
return reply(ReplyUtil.invalidPassword());
}
if(BcryptUtil.authenticate(params.password, user.password)){
var token = JwtUtil.getUserToken(user);
var redisClient = request.server.plugins['hapi-redis'].client;
redisClient.set('user_'+user.userId, token);
return reply(ReplyUtil.ok(token));
}
return reply(ReplyUtil.invalidPassword());
})
.catch(function(err){
return reply(ReplyUtil.badImplementation(err));
});
};
exports.logOut = function (request, reply) {
var userId = request.auth.credentials.jti;
var redisClient = request.server.plugins['hapi-redis'].client;
redisClient.del('user_'+userId);
return reply();
};
exports.methodNotAllowed = function (request, reply) {
return reply( ReplyUtil.methodNotAllowed() );
};
route
'use strict';
/**
* Module dependencies.
*/
var User = require('../controllers/users');
var Validator = require('../validations/users');
/**
* Resource configuration.
*/
var internals = {};
internals.resourcePath = '/users';
module.exports = function() {
return [
{
method: 'POST',
path: internals.resourcePath,
config : {
handler: User.create,
validate: Validator.create
}
},
{
method: 'GET',
path: internals.resourcePath,
config : {
handler : User.findAll,
auth: {
strategy: 'token',
scope: ['admin']
}
}
},
{
method: 'GET',
path: internals.resourcePath + '/{userId}',
config : {
handler : User.findById,
validate: Validator.findById,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: 'PUT',
path: internals.resourcePath + '/{userId}',
config : {
handler: User.update,
validate: Validator.update,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: 'DELETE',
path: internals.resourcePath + '/{userId}',
config : {
handler: User.destroy,
validate: Validator.destroy,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: 'POST',
path: internals.resourcePath + '/signin',
config : {
handler: User.signIn,
validate: Validator.signIn
}
},
{
method: 'GET',
path: internals.resourcePath + '/logout',
config : {
handler : User.logOut,
auth: {
strategy: 'token',
scope: ['user']
}
}
},
{
method: '*',
path: internals.resourcePath + '/{somethingss*}',
config : {
handler: User.methodNotAllowed
}
}
];
}();
I no I am a little late, but just in case anyone else has this problem. It's a problem with bcrypt. I had a similar problem where whenever I used bcrypt-nodejs it would give me The following leaks were detected:lr, password, but when I changed to reqular bycrypt it worked with no leaks. Try updating your bycrypt version.
Just add all leakimg elements to ignore list
"test": "lab -c -L -I 'Reflect,core,_babelPolyfill,regeneratorRuntime,__core-js_shared__ css'",