Firebase sendPasswordResetEmail in JS SDK, doesn't send confirmation code - reactjs

I am trying to do the following:
User asks to reset his password
Email is sent with a CODE and an URL to visit to reset his password
User visits the URL inputs the code and the new password and I call the confirmPasswordReset
But this doesn't work, the docs, their examples and also their functionality differs. Here's what happens
Greeting,
Follow this link to reset the example#gmail.com account password for the Example Dev app.
the URL
No Code, nothing, if I follow the URL it will take me to Firebase hosted app that will handle the reset with an ugly looking UI and THEN redirect me to the specified URL in the settings for sendResetEmail...
While the docs specify expected behavior, it differs from the actual one.
Sends a password reset email to the given email address.
#remarks
To complete the password reset, call confirmPasswordReset with the code supplied in the email sent to the user, along with the new password specified by the user.
#example
const actionCodeSettings = {
url: 'https://www.example.com/?email=user#example.com',
iOS: {
bundleId: 'com.example.ios'
},
android: {
packageName: 'com.example.android',
installApp: true,
minimumVersion: '12'
},
handleCodeInApp: true
};
await sendPasswordResetEmail(auth, 'user#example.com', actionCodeSettings);
// Obtain code from user.
await confirmPasswordReset('user#example.com', code);
The docs specify my intended behavior, but when used, it's totally different. What is happening here?
const onSubmit = (data: PasswordResetData) => {
setIsLoading(true);
sendPasswordResetEmail(getAuth(), data.email, {
url: "http://localhost:3002/en/auth/reset/confirm?email=" + data.email,
handleCodeInApp: true,
})
.then(() => {
})
.catch((err) => {
console.error(err);
})
.finally(() => {
setIsLoading(false);
});
};

Related

change password not working amplify amazon

i am trying to implement a simple password change after user gets invitation by email.
my code for changing password is from documentation:
https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/
import { Auth } from 'aws-amplify';
Auth.currentAuthenticatedUser()
.then(user => {
return Auth.changePassword(user, 'oldPassword', 'newPassword');
})
.then(data => console.log(data))
.catch(err => console.log(err));
it is showing an error
NotAuthorizedException: Incorrect username or password.
which is not true, anyone had similar issue?,
I tried different solutions for this pasword change but seems I can get my head around it

Amplify and React : Code mismatch and fail enable Software Token MFA

I have a Cognito user pool that has MFA set to Optional with TOTP only.
I'm trying to set up a page that enables MultiFactorAuthentication for the user for the first time following this AWS Documentation.
As my component mounts, I generate a QR code and show it on screen using qrcode.react
useEffect(() => {
Auth.currentAuthenticatedUser({ bypassCache: true }).then(user => {
setUser(user);
Auth.setupTOTP(user).then(code => {
const authCode = "otpauth://totp/AWSCognito:" + user.username + "?secret=" + code + "&issuer=ivt";
setQrCode(authCode);
});
});
}, []);
Then, when the user puts the input, I verify it and call the setPrefferredMFA. Now here, I checked if "input" is correctly passed and no issues there.
const setupMFA = input => {
Auth.setupTOTP(user).then(() => {
Auth.verifyTotpToken(user, input)
.then(() => {
Auth.setPreferredMFA(user, "TOTP").then(() => {
props.setShowModal(false);
});
})
.catch(e => {
// Token is not verified
});
});
};
I'm still getting Code mismatch and fail enable Software Token MFA error and failing to set a MFA to the user.
I solved it.
Auth.verifyTotpToken() is not supposed to be in the .then() block of setupTOTP.
Pfft. Removing the Auth.setupTOTP in the setupMFA function made it work.

What's the proper way to use Firebase Authentication along with Firestore?

Hi all—I'm building an app using Next.js and Firebase, both brand new technologies for me. It's a simple app where a user creates an account and must log in. If the user doesn't create an account, the app is useless—they can't move on to the next screen, which is a dashboard. Anyway, when they log in, they can then create a trip/vacation itinerary. I'm using Firebase Auth for auth and Firestore (not real-time db) as my db. My goal is that when a user logs in, the user can see every itinerary that they created and no one else's. It should be full CRUD. This is the first time I've done this sort of authentication as well, so that's likely adding to my confusion.
I know that my code isn't right, but it sort of worked. What keeps happening is that it seems like there's a lag when a user logs in and out. I've tested this on my local copy. When I log out and then log back in as a different user, it tells me that the uid is null. Anywhere from 1 - 30 minutes later (seriously), all of a sudden the page loads for the uid that I logged in with! Everything that I've read says that there's a lag with the authentication, but I couldn't really find a solution other than just pointing out the problem—so basically writing a console log that says who's logged in at the time and then the same when they've logged out. Also, I watched / read tons of tutorials, so maybe it's my code? I'm so sorry in advance for this novel of code—I'll organize as best as I can!
Here's my config info, so I'm referring to Firebase as fire. The sign-in method is email and password, and everything looks as it should in Firebase as far as capturing that information on the Authentication screen.
import firebase from 'firebase';
const firebaseConfig = {
apiKey: 'AIzaSyB-xEPETXSfjboKe5H0kPUu-ZdRDGfszmA',
authDomain: "where-to-jess.firebaseapp.com",
databaseURL: 'https://where-to-jess-default-rtdb.firebaseio.com/',
projectId: "where-to-jess",
storageBucket: "where-to-jess.appspot.com",
messagingSenderId: "914509599583",
appId: "1:914509599583:web:80cdf3e4090417b0f35cea"
};
try {
firebase.initializeApp(firebaseConfig);
} catch(err){
if (!/already exists/.test(err.message)) {
console.error('Firebase initialization error', err.stack)}
}
const fire = firebase;
export default fire;
When a user creates an account, they're also added to a collection 'users' in my db. I am using React Hooks (for the first time) as well. Their email is their username to login, but I'm capturing their email in the db. They are also immediately logged in upon account creation. This part also works.
const handleSubmit = (e) => {
e.preventDefault();
setPassword('');
fire.auth().createUserWithEmailAndPassword(userName, password)
.then(() => {
fire.firestore().collection('users').doc(fire.auth().currentUser.uid)
.set({
firstName: firstName,
lastName: lastName,
email: userName
})
.catch(error => {
console.log('user wasn\'t added to db: ', error);
})
})
.catch(error => {
console.log('user wasn\'t able to create an account: ', error);
})
router.push('/users/dashboard')
};
This is my login code:
const handleLogin = (e) => {
e.preventDefault();
fire.auth()
.signInWithEmailAndPassword(username, password)
.catch((error) => {
console.log('user wasn\'t able to login: ', error);
})
setUsername('')
setPassword('')
router.push('/users/dashboard')
};
Now for the fun part! This is my code for form submission for the itinerary. What I'm trying to achieve here is to have this newly created itinerary attached to their uid in the 'users' db. I'm leaving out all the form stuff because it's super long. This also seems to work—I can see it coming in in the db for whichever account I'm using.
const handleSubmit = (e) => {
e.preventDefault();
fire.firestore()
.collection('users').doc(fire.auth().currentUser.uid).collection('itineraries')
.add({
//
})
.catch(error => {
console.log('itinerary not added to db ', error)
})
router.push('/users/dashboard')
}
Here's where it all went to heck! I suspect it's because I'm cutting corners, which I'll explain next. This dashboard should show ONLY itineraries that the current logged-in user created. If the current logged-in user didn't create any itineraries, I'd get an error saying that the uid was null. SO, my workaround was to just create a fake itinerary manually in the db on their account (since I was testing) and give the tripName value as null. This seems to work, but this is where the weird login / logout stuff happens.
export default function Dashboard() {
const router = useRouter();
const [itineraries, setItineraries] = useState([]);
const [loggedIn, setLoggedIn] = useState(false);
fire.auth()
.onAuthStateChanged((user) => {
if (user) {
console.log(user.email + " is logged in!");
setLoggedIn(true)
} else {
setLoggedIn(false)
console.log('User is logged out!');
}
})
useEffect(() => {
const unsubscribe =
fire.firestore()
.collection('users').doc(fire.auth().currentUser.uid).collection('itineraries').where('tripName', '!=', 'null')
.onSnapshot(snap => {
const itineraries = snap.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
setItineraries(itineraries);
return () => {
unsubscribe();
};
});
}, []);
const handleLogout = () => {
fire.auth()
.signOut()
router.push('/')
};
Lastly, here is the one rule that I have on the db. I got confused reading the rule docs, and I feel like I cut a corner here.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read
allow write
}
}
}
Again, I'm really sorry for ALL of that code. This is my first time using React Hooks, Next, and Firebase—so it's a mashup of Firebases's docs, tutorials, and my own code. I'd appreciate ANY help or advice here.
That rule will allow all access to all documents in your db at present. You want something like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user_id}{
allow read, write: if request.auth != null && request.auth.uid == user_id;
}
}
}
That will allow access only to users that are authenticated
This was 100% user error on my part, but I wanted to share since some of my issues seem pretty common. In addition to AspiringApollo's advice above, I had my function completely out of order (as I mentioned, hook newbie). The above plus structuring my function like this fixed it:
useEffect(() => {
const unsubscribe =
fire.auth()
.onAuthStateChanged((user) => {
if (user) {
let uid = user.uid
console.log(user.email + ' is logged in!');
setLoggedIn(true)
// all the things you want to do while the user is logged in goes here
} else {
setLoggedIn(false)
console.log('user is logged out!');
}
return () => {
unsubscribe();
};
});
}, []);
Still open to suggestions and more sets of eyes because I know this is a little messy!

How to create logout when expires token

could someone help me to resolve this problem?
I don't know how to implement expired session to my react app.
I have json data with expires_in: 86400 what I need to do to implement this to my app, when expired to logout user.
I using react.JS and redux.
Action:
export const signin = obj => {
return dispatch => {
let data = {
method: "post",
url: "/oauth/token",
data: {
grant_type: "password",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
username: obj.email,
password: obj.password
}
};
return API(data)
.then(res => {
window.localStorage.setItem("access_token", res.data.access_token);
console.log('uuuuuuuuuu', res)
dispatch({
type: AUTH.LOGGED_IN,
payload: {
access_token: res.data.access_token,
expired_in: res.data.expires_in
}
});
})
.catch(err => {
throw err;
});
};
};
You can simply achieve this using setTimeout function to trigger your logout functionality. The below code is on the assumption that expires_in is the relative time not the absolute time.
window.localStorage.setItem("access_token", res.data.access_token);
setTimeout(logoutFunction, response.data.expires_in)
console.log('uuuuuuuuuu', res)
dispatch({
type: AUTH.LOGGED_IN,
payload: {
access_token: res.data.access_token,
expired_in: res.data.expires_in
}
});
and your logoutFunction will look something like this:
function logOutFunction() {
window.localStorage.removeItem("access_token");
// your redux dispatch and probably a redirection logic to go to a proper UX page
}
I mistakenly said in comments to use setInterval. I assume this will be a one time execution, setTimeout is better to use.
From your code I found. you have used window.localStorage.setItem("access_token", res.data.access_token);
But you actually need a cookie there and set the expire time from the response. Then you have to check is that the cookie existence in the parent component. if it, not exist (expired) you can redirect to the login page.
For force logout, you can simply make the cookie expire. ;)
You shouldn’t store the time left to expire, but the current time + the time left. Otherwise you can’t know when this happened.

Google-OAuth REST Api to react

I have successfully created an API that uses passport-google-oauth to return a JWT. Currently when I go through the process using my API routes it returns a json object with a JWT Bearer token.
I am attempting to use Reactjs on the front end however am running into a couple issues.
In my signin button component I am just trying to retrieve the result with the bearer token to pass it into a reducer
When using Axios -> I am running into a CORS issue when using exios and cant return a result, when adding CORS into my build and a proxy to my react project I recieve the following error No 'Access-Control-Allow-Origin' header is present on the requested resource.
When I use a anchor tag with href link the authentication successfully works however it redirects to the /api/auth/google/callback link itself instead of allowing me to catch the bearer token and then run it through my reducers to save it into local storage and update my state.
Am I missing a step? Ive looked for a few hours at various resources online and cant seem to find the solution im looking for
React
(for simplicity at the moment I am just trying to catch the response, which should be returned bearer token, however I am unable to do this)
googleAuth = (e) => {
e.preventDefault()
axios.get('/api/auth/google')
.then(res => console.log(res))
.catch(err => console.log(err))
}
render() {
return (
<button onClick={this.googleAuth}>Signin With Google</button>
)
}
API
Routes
router.get('/google', passport.authenticate('google', {
session: false,
scope: ['profile', 'email']
}))
router.get('/google/callback', passport.authenticate('google', { session: false }), generateUserToken)
Strategy
passport.use(new passportGoogle.OAuth2Strategy(googleConfig, async (request, accessToken, refreshToken, profile, done) => {
// Check for existing user
const existingUser = await User.findOne({
providers: {
$elemMatch: {
provider: 'Google',
providerId: profile.id
}
}
})
// If user exists return done
if (existingUser) return done(null, existingUser)
// If user does not exist create a new user
const newUser = await new User({
name: profile.displayName,
providers: [
{
provider: 'Google',
providerId: profile.id
}
]
}).save()
// Create profile with new user information
const newProfile = await new Profile({
userId: newUser.id
}).save()
return done(null, newUser)
}))
I've looked a bit at your code and haven't seen any serializing/deserializing going on. Usually you'd have to go through this process to be able to connect with whatever authentication strategy you are using.
Here is a snippet of code that you could use in the file you keep your strategies:
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
Maybe you could look it up in more detail in the documentation. Here is a link http://www.passportjs.org/docs/ go to the sessions part. Plus, make sure to look at how the app.use is put together with the .session() func.

Resources