save Authentication data to firebase React native - javascript

i have a react natve app it has a signup whith google button when i click on signin i am getting data in console.log i want to save the data in firebase i sont know how to do it
const googleLogin = async () => {
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
console.log(userInfo);// i am getting user data here
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
// some other error happened
}
}
};

You can refer to this LINK for google social auth, this is what you're looking for to save the auth data to firebase:
import auth from '#react-native-firebase/auth';
import { GoogleSignin } from '#react-native-google-signin/google-signin';
async function onGoogleButtonPress() {
// Check if your device supports Google Play
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
// Get the users ID token
const { idToken } = await GoogleSignin.signIn();
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
return auth().signInWithCredential(googleCredential);
}

This can helpful. Just make sure you have integrated all libraries for firebase authentication.
import {
GoogleSignin,
statusCodes,
} from '#react-native-google-signin/google-signin';
import auth, {FirebaseAuthTypes} from '#react-native-firebase/auth';
// This function will be call on tapping sign in with google button
const signInWithGoogle = async () => {
try {
// This will check whether there is Google play service or not
await GoogleSignin.hasPlayServices();
//This will give you userInformation
const userInfo = await GoogleSignin.signIn();
// This will create new credential which can help to signIn in firebase
const credential = auth.GoogleAuthProvider.credential(userInfo.idToken);
//Here we are trying to return promise so when we call function we can have promise object
return new Promise((resolve, reject) => {
auth()
.signInWithCredential(credential)
.then(response => {
console.log('response in', response);
resolve(response);
})
.catch(error => {
console.log('error in', error);
reject(error);
});
});
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
alert(JSON.stringify(error));
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
alert(JSON.stringify(error));
} else {
alert(error);
}
}
};
Now when you call this function on google button on press it will give you promise object and you can do it like below.
onPress={() => {
SignInMethods.signInWithGoogle()
.then(response => {
console.log('user information from firebase authentication', response.user);
})
.catch(error => {
console.log('error in google sign in :', error);
});
}}

Related

Check if user isLoggedIn MongoDB Realm Web SDK

I wish to check if a user is already logged in through email/password auth via mongodb realm web-sdk. Knowing if the user is logged in will allow me to hide the loggin page from site and instead show a log out button.
So far I've successfully created a user and logged in. Using the code/methods below.
async function registerEmailPassword(email, password) {
try {
const user = await app.emailPasswordAuth.registerUser({ email, password });
return user;
} catch (error) {
console.error("Failed to register", error)
}
}
async function loginEmailPassword(email, password) {
// Create an email/password credential
const credentials = Realm.Credentials.emailPassword(email, password);
try {
// Authenticate the user
const user = await app.logIn(credentials);
// `App.currentUser` updates to match the logged in user
console.assert(user.id === app.currentUser.id);
return user;
} catch (error) {
console.error("Failed to log in", error);
}
}
While going through the mongodb class documentation, I wrote the following function which appears to work.
The code is checking for if their is any user in currentUser, if their is no currentUser, their no account logged in. In the event their is a currentUser, the code then checks using currentUser.isLoggedIn if that user is logged and at the end returns a boolean value.
async function isUserLoggedIn() {
try {
const userStatus = await app.currentUser;
if (userStatus == null) {
return false
} else {
const userStatus = await app.currentUser.isLoggedIn;
return userStatus
}
} catch (error) {
console.log("Failed to fetch user", error);
}
}
// Check if user is logged in
isUserLoggedIn().then((value) => {
console.log(value);
}).catch((error) => {
console.log(error);
});

userSession is null after Auth.signUp with "autoSignIn" enabled (AWS Cognito)

I need to get the jwtToken from the Auth.signUp. Is this possible if i enable autoSignIn:{enabled:true}?
const signUp = async () => {
await Auth.signUp({
username: email,
password,
attributes: {
email, // optional
name,
},
autoSignIn:{
enabled: true
}
})
.then((data) => {
console.log(data.user); //user.signInUserSession is null
})
.catch((err) => {
if (err.message) {
setInvalidMessage(err.message);
}
console.log(err);
});
await Auth.currentAuthenticatedUser()
.then(user =>{
console.log(user)
})
.catch(error => {
console.log(error) //"User is not authenticated"
})
};
I call I want the jwttoken from the userSession data for conditional rendering and I store the token in my router.js. The response object from Auth.signUp contains a CognitoUser which has a signInUserSession value but its's null.
EDIT: Tried to call Auth.currentAuthenticatedUser() after but yields an error that user is not authenticated. But when i restart my app, the user will be authenticated. I still cant authenticate user on the same app "instance"
import { Auth, Hub } from 'aws-amplify';
const listener = (data) => {
switch (data.payload.event) {
case 'autoSignIn':
console.log('auto sign in successful');
console.log(data.payload) //returns user data including session and tokens.
//other logic with user data
break;
}
};
Above is the code to initalize the Hub listener provided by amplify api. Ater user presses sign up, I called to get user session data when user is automatically signed in.
Hub.listen('auth', listener)

React Native - Direct user back to login if current user token is invalid?

I have this code that authenticates the user session by taking their token and sending it to AWS-amplify to ensure their token hasn't timed out and it's a valid user
import {Auth} from 'aws-amplify';
export async function getAccessToken() {
try {
// const currentUser = await Auth.currentAuthenticatedUser();
// console.log(currentUser);
return await Auth
.currentSession()
.then(res => {
let accessToken = res.getAccessToken();
let jwt = accessToken.getJwtToken();
// You can print them to see the full objects
// console.log(`myAccessToken: ${JSON.stringify(accessToken)}`);
// console.log(`myJwt: ${JSON.stringify(jwt)}`);
return jwt
});
} catch (error) {
console.log(error);
}
}
This gets called when make ANY endpoint call, because I want to ensure the user has been validated by aws-amplify. So for example I might have some code that does something like
function getSomething() {
getAccessToken().then(token => {
const config = {
headers: {
Authorization: `Bearer ${token}`
}
};
axios
.get(<url>, config)
.then(response => {
console.log(response.data)
setData(response.data);
})
.catch(error => {
console.log(JSON.stringify(error));
});
}).catch(error => {
console.log(JSON.stringify(error));
});;
};
So every endpoint call of my app requires the access token and if it's not authenticated properly the backend will shoot back a 401. If 401 is received I want to automatically bounce users back to login for any and all screens (except login of course)
for navigation I'm using
const NavigatorTab = ({navigation}) => {
and just setting the navigation parameter.

Angularfire check password

I'm coding a "delete account" functionality on my app and I want the user to enter their password again before triggering the deletion.
What would be the best way to implement this? I was thinking on using the "signInWithEmailAndPassword" method and capturing the result to check if the credentials are correct, but I'm afraid that would overwrite the current session.
Any tips?
If the session is too old or whatever, an error is thrown by the 'delete account' function anyways. Then you need to re-auth the user. There is a special function for that too: reauthenticateWithCredential().
Here I have an example to show the difference of the login and re-authenticate functions (copied from a project of mine and cut down a bit because there was some analytics and stuff):
public async reAuthenticate(user: User, { email, password }: IEmailLoginData): Promise<UserCredential> {
const credentials = firebase.auth.EmailAuthProvider.credential(email, password);
return user.reauthenticateWithCredential(credentials)
.catch(e => {
console.error(e);
throw e;
});
}
public async login({ email, password }: IEmailLoginData): Promise<UserCredential> {
return firebase.auth().signInWithEmailAndPassword(email, password)
.catch(e => {
console.error(e);
throw e;
});
}
// PS: IEmailLoginData is a custom interface from me, but it just contains email and password
Also, here is the code for the 'delete account'. It should be pretty self-explanatory - hope it helps a bit:
async delete(): Promise<void> {
const dialogRef = this.dialog.open(YesNoDialogComponent, {
data: {
yes: 'Yes',
no: 'No',
title: 'Are you sure that you want to delete your account?'
}
});
const result = await dialogRef.afterClosed().pipe(take(1)).toPromise();
if (result === IYesNoDialogResult.YES) {
try {
const authUser = await this.auth.nextAuthUser(); // Getting the current firebase user from my custom service
await authUser.delete();
await this.router.navigateByUrl('login');
} catch(e) {
const toast = await this.toast.create({
duration: 3000,
message: 'This is a sensitive operation. Please login again to do this'
});
await toast.present();
await this.router.navigateByUrl('reauth');
});
}
}
For different auth provider it might be slightly different, but in the essence it is still the same. Just for example with google (if you want to use the Ionic Native Google Plus Login Plugin), you need to create the re-authenticate credentials from the plugin result:
public async reAuthenticate(user: User): Promise<UserCredential> {
try {
if (this.platform.is('cordova')) {
try {
const gUser = await this.gPlus.login({
webClientId: environment.googleWebClientId,
offline: true,
scopes: 'profile email'
});
const credential = firebase.auth.GoogleAuthProvider.credential(gUser.idToken);
return await user.reauthenticateWithCredential(credential);
} catch (nativeE) { // If login failed via native method, fallback to redirects
if (nativeE == 12501 || nativeE == 13) { // User cancelled login
return null;
}
console.error(nativeE);
// In constructor:
// this._provider = new firebase.auth.GoogleAuthProvider();
await user.reauthenticateWithRedirect(this._provider);
return await firebase.auth().getRedirectResult();
}
}
else {
return await user.reauthenticateWithPopup(this._provider);
}
} catch (e) {
console.error(e);
throw e;
}
}

Firebase: Getting new token after current FCM token expires

I am working on a reactJS website with firebase as a backend. One of the main service that I give to my user is notification everyday. I had implemented a code to ask for permission, if they grant access, store them to my database. But after sometime I noticed that tokens are expiring. I did a research about it and found out about onTokenRefresh() method. I implemented that also. But I believe for some reason, it is not working correctly. I am not getting the new tokens if tokens are expired. I'll paste the snippet below of what I am trying to do.
On the main page,
if (!("Notification" in window)) {
//alert("This browser does not support desktop notification");
}
// Check whether notification permissions have already been granted
else if (Notification.permission === "granted") {
// Refresh logic here
this.checkForRefreshToken();
}
// Otherwise, ask the user for permission
else if (Notification.permission !== "denied") {
//Ask user here, then call another function for storing token
this.addNewToken()
}
If user is accepting notification for the first time, I call addNewToken() method
addNewToken = () => {
this.auth.onAuthStateChanged(authUser => {
const messaging = firebase.messaging();
messaging
.requestPermission()
.then(() => {
return messaging.getToken();
})
.then(token => {
firebase.database().ref('tokens').once('value')
.then(snapshots => {
let tokenExist = false
snapshots.forEach(childSnapshot => {
// console.log("Child snapshot: ", childSnapshot.key);
if (childSnapshot.val().token === token) {
tokenExist = true
return console.log('Device already registered.');
}
})
if (!tokenExist) {
// console.log('Device subscribed successfully');
return firebase.database().ref('tokens').push(token);
}
})
})
.catch(error => {
if (error.code === "messaging/permission-blocked") {
// console.log("Please Unblock Notification Request Manually");
} else {
// console.log("Error Occurred", error);
}
});
})
}
Now if user has already subscribed, I am checking if onTokenRefresh() is called, basically if token needs a refresh.
checkForRefreshToken = () => {
this.auth.onAuthStateChanged(authUser => {
const messaging = firebase.messaging();
messaging.onTokenRefresh(() => {
messaging.getToken().then((refreshedToken) => {
const token = refreshedToken;
firebase.database().ref('tokens').once('value')
.then(snapshots => {
let tokenExist = false
snapshots.forEach(childSnapshot => {
if (childSnapshot.val().token === token) {
tokenExist = true
return console.log('Device already registered.');
}
})
if (!tokenExist) {
return firebase.database().ref('device_ids').push(token);
}
})
})
})
})
}
I don't know what is going wrong here, but I am not able to get the new tokens.
For testing purpose, I have my own device with expired token, I deployed this code and opened my website on the device, refreshed the page etc but I didn't get the new token.
Also, it would be great if anyone can help me how I can test expired tokens locally.
I found different methods for an app, but not for website (javascript).
Thanks
auth.onAuthStateChanged(...) will add an event listener to events signalling changes in the authentication state. In your code above, you expect that the code for addNewToken and checkForRefreshToken are evaluated immediately, but instead they are simply adding new event listeners. The same applies for onTokenRefresh().
So reworking addNewToken() into an on-demand function yields:
requestMessagingToken = () => {
let authUser = this.auth.currentUser;
if (!authUser) {
console.log("Not logged in!");
return Promise.resolve(false); // silently ignore & fail
}
const messaging = firebase.messaging();
return messaging
.requestPermission()
.then((permission) => {
if (permission !== 'granted') {
throw 'insufficient permissions'; // notification permission was denied/ignored
}
return messaging.getToken();
})
.then(token => saveMessagingTokenForUser(authUser.uid, token))
.catch(error => {
if (error && error.code === "messaging/permission-blocked") {
// console.log("Please Unblock Notification Request Manually");
} else {
// console.log("Error Occurred", error);
}
return false; // silently fail
});
};
This has the added benefit of being able to handle auth state changes easily using:
this.auth.onAuthStateChanged(requestMessagingToken);
Once you've been granted permission to send notifications, you can activate the refreshed token listener using:
const messaging = firebase.messaging();
messaging.onTokenRefresh(() => {
let authUser = this.auth.currentUser;
if (!authUser) {
console.log("Not logged in!");
return; // ignore
}
messaging.getToken().then((refreshedToken) => {
return saveMessagingTokenForUser(authUser.uid, refreshedToken);
}).catch((err) => {
console.log('Unable to retrieve/store messaging token ', err);
});
});
Lastly, to save the token to the database, instead of searching through your database for a matching device token as the value of a push ID, you can use the token itself as the key. Furthermore, to make outdated token management easier, it is best to split your tokens by user.
"userData": {
"userId1": {
"tokens": {
"deviceToken1": true,
"deviceToken2": true,
"deviceToken3": true,
},
...
},
"userId2": {
"tokens": {
"deviceToken4": true,
"deviceToken5": true
},
...
},
...
}
Using this structure, you can use transactions to check if the data has been stored in the database and use cloud functions to check for invalid tokens.
saveMessagingTokenForUser = (uid, token) => {
return firebase.database().ref('userData/' + uid + '/tokens/' + token)
.transaction((currentData) => {
if (currentData != null) {
console.log('Device already registered.');
return; // abort transaction, no changes needed
} else {
console.log('Saving token to database.');
return true;
}
})
.then(() => true); // no errors = success
};
On the server you could run a Cloud Function listening to new device tokens added to the database and check that user's other device tokens for expiry.
exports.checkUserForOutdatedTokens = functions.database.ref('/userData/{userId}/tokens/{tokenId}')
.onCreate((newTokenSnapshot, context) => {
return newTokenSnapshot.ref.once('value')
.then((tokensSnapshot) => {
let tokens = Object.keys(tokensSnapshot.val());
const message = {
data: {heartbeat: true},
tokens: tokens,
}
return admin.messaging().sendMulticast(message)
.then((response) => {
if (response.failureCount > 0) {
const dataToDelete = {};
response.responses.forEach((resp, idx) => {
if (!resp.success) {
dataToDelete[tokens[idx]] = null;
}
});
console.log('List of tokens that caused failures: ' + Object.keys(dataToDelete));
return tokensSnapshot.ref.update(dataToDelete); // deletes failed tokens
}
});
});
});

Categories

Resources