Firebase .getIdToken() returns invalid token - javascript

I'm trying to make just a simple authentication app with electron and firebase redirect, but if the user is already logged in and I use the firebase.auth().currentUser.getIdToken() to get the IdToken of that user, but when i try that token in
firebase.auth().signInWithCredential(credential) I get the error that says ERROR: auth/invalid-credential
Here is my code front-end
firebase.auth().onAuthStateChanged( async function (user) {
if (user) {
// User is signed in.
var user = await firebase.auth().currentUser;
if (user != null) {
await firebase.auth().currentUser.getIdToken().then(function(idToken) {
window.location.href = "electron://"+idToken;
}).catch(function(error) {
console.log(error)
});
}
} else {
// No user is signed in.
document.getElementById("user_div").style.display = "none";
document.getElementById("login_div").style.display = "block";
}
});
Here is my code back-end
app.on('second-instance', (event, commandLine, workingDirectory) => {
if (commandLine[2]) {
var url = commandLine[2].split('/')
var id_token = url[2]
console.log('id: ', id_token)
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(id_token);
// Sign in with credential from the Google user.
firebase.auth().signInWithCredential(credential)
.then((success)=>{
myWindow.loadFile('./scr/welcome.html')
console.log('RESULT: ',success)
})
.catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log('ERROR:', errorMessage)
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
console.log('ERROR:', credential)
// ...
})
}
I'm missing something or doing something wrong?

That's not how ID tokens work. The purpose of an ID token is to pass to your backend, so that it can validate the identity of the signed in user, and perform some action on their behalf. It's not valid for signing in on the client again. You might want to review the documentation on use of ID tokens to learn how this works.
signInWithCredential only works with Google Auth when you correctly construct a GoogleAuthProvider credential. There is plenty of sample code in the API documentation for that.

Related

Google signin with signInWithRedirect always takes me back to the login page

I'm having a hard time getting the Firebase signInWithRedirect with google to work. Below is the doc I've been looking at: https://firebase.google.com/docs/auth/web/google-signin.
I have a login page which contains a button. When the button is clicked it take you to google sign in page. However every time I sign in with google it redirects me back to the login page of my app. Part of the code is shown below:
login() {
const auth = getAuth();
signInWithRedirect(auth, provider);
console.log("AAA")
auth.onAuthStateChanged((user) => {
console.log("BBB")
if (user) {
this.$router.push('home')
} else {
getRedirectResult(auth)
.then((result) => {
console.log("WIN0")
// This gives you a Google Access Token. You can use it to access Google APIs.
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
console.log(token)
console.log(user)
console.log("WIN")
}).catch((error) => {
console.log("ERROR0")
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.customData.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
console.log(errorCode)
console.log(errorMessage)
console.log(email)
console.log(credential)
console.log("ERROR")
// ...
});
}
})
From the console, I only ever see the log AAA, but I don't see any other logs at all. What am I missing? Thank you.
I should also mention that I'm doing this in vue js but I don't think it matters.
So the problem was that I have onAuthStateChanged within the login function. Since the SignInWithRedirect takes to a different page and then redirects back to the page where it was called, in vue I basically moved the onAuthStateChanged to the mounted lifecycle hook.
mounted() {
auth.onAuthStateChanged((user) => {
console.log("BBB")
if (user) {
this.$router.push('home')
} else {
getRedirectResult(auth)
.then((result) => {
console.log("WIN0")
// This gives you a Google Access Token. You can use it to access Google APIs.
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
console.log(token)
console.log(user)
console.log("WIN")
}).catch((error) => {
console.log("ERROR0")
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.customData.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
console.log(errorCode)
console.log(errorMessage)
console.log(email)
console.log(credential)
console.log("ERROR")
// ...
});
}
})
}

How to pass user uid from login page to a different page in a web app

I'm logging in users via firebase-auth and need to pass their user uid to the redirected page. How can the user-uid be passed?
I've tried using the docs on firebase auth but cant quite solve it
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
const email = loginForm['email'].value;
const password = loginForm['password'].value;
console.log(email, password);
firebase.initializeApp(config);
firebase.auth().signInWithEmailAndPassword(email, password)
.then(cred => {
if(email ==='admin#mombasa.com' && password ==='adminMomb') {
window.location = './admin-map.php';
}
else if(email ==='admin#nairobi.com' && password ==='adminNai') {
window.location = './admin-map.php';
}
else if(email ==='admin#eldoret.com' && password ==='adminEld') {
window.location = './admin-map.php';
}
else firebase.auth().onAuthStateChanged(function(user) {
window.user = user;
console.log('logged in ', user);
window.location = './dailyreports.php';
console.log('logged in ', user);
});
}).catch(
function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
console.log(errorMessage, errorCode);
}
)
;
});
On navigation to the next page, I expected to see the user-uid output in the console but it returns blank
Instead of passing the UID, it's better to pick up the state in the next page with a so-called authentication state listener.
As shown in the documentation on getting the currently signed in user:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
There will be no delay in picking up the user account this way on the new page, as the Firebase SDK will be able to restore the state from the information it stored in local storage.

firebase reauthenticateAndRetrieveDataWithCredential facebook user credential

I'm trying to write a function to reauthenticate a firebase user that is logged in with a facebook account. For the email I create the credentials from the email and password but with Facebook no password is stored for that user. I tried to find this in the docs but couldn't find
let user = firebase.auth().currentUser;
let credential = '';
//User created with Email
if(user.providerData[0].providerId === "password") {
credential = firebase.auth.EmailAuthProvider.credential(
user.email,
this.state.password
);
}
//User created with Facebook
if(user.providerData[0].providerId === "facebook.com") {
credential = firebase.auth.FacebookAuthProvider.credential(
? // <= User Email, Token
)
}
user.reauthenticateAndRetrieveDataWithCredential(credential).then(function(){
user.delete().then(function() {
}).catch(function(error) {
console.log(error)
}
}).catch((error) => {
console.log(error)
}
Based on the FacebookAuthProvider.credential() signature, you will need the OAuth token / accessToken to create the credential.
You can extract the accessToken when the user logs in:
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
The example was extracted from https://firebase.google.com/docs/auth/web/facebook-login where you can find more info about this.
If you want to reauthenticate a Facebook user, simply use reauthenticateWithPopup or reauthenticateWithRedirect, assume you used signInWithPopup or signInWithRedirect to sign in the user with Facebook.
const provider = new firebase.auth.FacebookAuthProvider();
firebase.auth().currentUser.reauthenticateWithPopup(provider)
.then((userCredential) => {
// User successfully reauthenticated.
})
.catch((error) => {
// Error.
});

Only authenticate with Google Auth Provider if user exists

I am using Firebase to authenticate users in our app using GoogleAuthProvider. But I don't want a new user to sign in if they are not already an authenticated user.
If the user exists then sign them in and console.log('user ' + user.email + ' does exist!');.
However, if the user does not exist. Then do not allow authentication and console.log('user ' + user.email + ' does not exist!')
var googleProvider = new firebase.auth.GoogleAuthProvider();
export const doSignInWithGoogle = () => auth.signInWithPopup(googleProvider);
googleLogin = () => {
auth
.doSignInWithGoogle()
.then(result => {
var user = result.user;
const userRef = db.collection('users').doc(user.uid);
userRef.get().then(docSnapshot => {
if (docSnapshot.exists) {
userRef.onSnapshot(() => {
console.log('user ' + user.email + ' does exist!');
});
} else {
console.log('user ' + user.email + ' does not exist!');
}
});
})
.catch(error => {
this.setState(updateByPropertyName('error', error));
});
};
I thought referencing the user records in Firestore would be a simple approach to this. However, perhaps Firebase Auth already have a way to do this. I cannot find documentation or any example.
In the above code, nothing gets logged and the user is either created or logged in.
How can I stop new users from signing up, whilst still allowing current users to sign in?
If you really want to use signInWithPopup method, you have this option,
but it's not the best way. when you are signing in with google, signInWithPopup method returns a promise. you can access the isNewUser property in additionalUserInfo from resulting object. then delete the user you just created.
firebase.auth().signInWithPopup(provider).then(
function (result) {
var token = result.credential.accessToken;
var user = result.user;
//this is what you need
var isNewUser = result.additionalUserInfo.isNewUser;
if (isNewUser) {
//delete the created user
result.user.delete();
} else {
// your sign in flow
console.log('user ' + user.email + ' does exist!');
}
}).catch(function (error) {
// Handle Errors here.
});
This is the easy way but deleting after creating is not the best practice. There is another option,
you can use, signInAndRetrieveDataWithCredential method for this. according to the docs,
auth/user-not-found will be
Thrown if signing in with a credential from
firebase.auth.EmailAuthProvider.credential and there is no user
corresponding to the given email.
function googleSignInWithCredentials(id_token) {
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(id_token);
// Sign in with credential from the Google user.
firebase.auth().signInAndRetrieveDataWithCredential(credential)
.then(function (userCredential) {
//sign in
console.log(userCredential.additionalUserInfo.username);
}).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
if (errorCode === 'auth/user-not-found') {
//handle this
} else {
console.error(error);
}
});
}
here is an example from firebase github repo.
with Firebase security rules, can only check if keys exist - therefore searching in the users table is not an option:
"emails": {
"example1#gmail.com": true,
"example2#gmail.com": true
}
and then one can check with security rules, if the auth.token.email exists as a key:
{
"rules": {
".read": "root.child('emails').child(auth.token.email).exists(),
".write": false,
}
}
in the client, this should throw an "The read failed: Permission denied error" error then, to be handled accordingly. hooking into the Firebase sign-up isn't possible - but while they cannot log-in, this has the same effort (except that on has to clean up the user-database from time to time); eg. with a Cloud Function, which deletes users, which do not have their email as key in the emails "table".
in Firestore security rules, one can check with:
request.auth.token.email & request.auth.token.email_verified
for example, with a collection called emails and a collection called content:
match /databases/{database}/documents {
function userMatchesId(userId) {
return request.auth != null && request.auth.uid == userId
}
function readAllowed(email) {
return if get(/databases/$(database)/documents/emails/$(request.auth.token.email)).data != null
}
match /users/{userId} {
allow get: if userMatchesId(userId)
}
match /content {
allow get: if readAllowed(request.auth.token.email)
}
}
The object you receive from firebase after login has additionalUserInfo where you have the property isNewUser.
You can find the reference here: https://firebase.google.com/docs/reference/js/firebase.auth.html#.AdditionalUserInfo

firebaseAuth GoogleAuthProvider() signInWithRedirect

I've a authentication Google with redirect in my app, and I would like just redirect when authentication is completely finished.
But the promise is not working
function loginGoogle() {
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithRedirect(provider);
firebase.auth().getRedirectResult().then(function (result) {
// This gives you a Google Access Token. You can use it to access the Google API.
if (result.credential) {
var token = result.credential.accessToken;
console.log('token ' + token);
}
// The signed-in user info.
var user = result.user;
console.log('user ' + user);
// if success redirect to
$state.go('maps-fullwidth');
// ...
}).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
console.log(errorCode);
var errorMessage = error.message;
// The email of the user's account used.
console.log(errorMessage);
var email = error.email;
console.log(email);
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
console.log(credential);
// ...
});
}
Thanks.
Move the getRedirectResult() call out of your loginGoogle() function. getRedirectResult() should be called on page load. An example of this in action can be found here:
https://github.com/firebase/quickstart-js/blob/master/auth/google-redirect.html

Categories