Firebase multi-tenancy has this error There is no user record corresponding to the provided identifier - firebase

I have setup my firebase with multiple tenants using Google Identity Platform.
And through Identity Platform, I manually added a user acct to each tenant.
For example, test#abcdemo.com for abcdemo tenant
test#defdemo.com for defdemo tenant
In my Flutter Web client app, I was able to sign in with FirebaseAuth's signInWithEmailAndPassword successfully with user acct and tenantId.
After successful sign in, I want to set a custom claim by passing the idToken that I retrieved from successful sign-in to setCustomClaims cloud function below:
const express = require("express");
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const app = express();
app.post("/setCustomClaims", async (req, res)=>{
// Get the ID token passed.
const idToken = req.body;
functions.logger.log("Here's idToken: ", idToken);
// Verify the ID token and decode its payload.
const claims = await admin.auth().verifyIdToken(idToken);
functions.logger.log("After verify ID Token");
// Verify user is eligible for additional privileges.
if (
typeof claims.email !== "undefined" &&
typeof claims.email_verified !== "undefined"
) {
functions.logger.log("Inside if condition");
//Result of code execution below:
//This shows thee correct project id, etc
functions.logger.log("Project ID is ", process.env.FIREBASE_CONFIG);
//Result of code execution below:
//Rejected: FirebaseAuthError: There is no user record corresponding to the provided identifier.
await admin.auth().getUserByEmail(claims.email).then(
(record) =>
functions.logger.log("Success: ", record)).catch(
(reasonStr) =>
functions.logger.log("Rejected: ",
reasonStr));
//Result of code execution below:
//Users: {"users":[]}
await admin.auth().listUsers().then((users) =>
functions.logger.log("Users: ", users));
//Result of code execution below:
//Error: There is no user record corresponding to the provided identifier.
await admin.auth().setCustomUserClaims(claims.sub, {
youcanaccess: true,
});
//Didn't even go to this line because the above code was erroring out.
functions.logger.log("after setCustomClaims");
// Tell client to refresh token on user.
res.end(JSON.stringify({
status: "success",
}));
functions.logger.log("after success");
} else {
// Return nothing.
res.end(JSON.stringify({status: "ineligible"}));
functions.logger.log("after ineligible");
}
});
exports.api = functions.https.onRequest(app);
The code above has some extra code for debugging purpose.
As you can see the code above, I put some comments till the last line
of executed statement.
It's erroring out in this line:
await admin.auth().setCustomUserClaims.
And the error message again is this:
Error: There is no user record corresponding to the provided identifier.
I don't know exactly why it stated that there's no user record even though I was able to sign in successfully.
My guess is the users in the tenant scope didn't get recognized by the admin.auth()?
By the way, this wasn't done in local emulator.
Looking forward for any advice. Thank you very much for the help

If you are using multi-tenant you must set the tenant id before you access it. if not it will check only outside of the tenant. So you should modify the code following:
first, you've to assign your user's tenant id like the following:
const tenantAuth = admin.auth().tenantManager().authForTenant("TENANT_ID");
Now you can access a particular tenant:
const claims = await tenantAuth.verifyIdToken(idToken);
then,
if (
typeof claims.email !== "undefined" &&
typeof claims.email_verified !== "undefined"
) {
functions.logger.log("Inside if condition");
functions.logger.log("Project ID is ", process.env.FIREBASE_CONFIG);
await tenantAuth.getUserByEmail(claims.email).then(
(record) =>
functions.logger.log("Success: ", record)).catch(
(reasonStr) =>
functions.logger.log("Rejected: ",
reasonStr));
await tenantAuth.listUsers().then((users) =>
functions.logger.log("Users: ", users));
await tenantAuth.setCustomUserClaims(claims.sub, {
youcanaccess: true,
});
functions.logger.log("after setCustomClaims");
// Tell client to refresh token on user.
res.end(JSON.stringify({
status: "success",
}));
functions.logger.log("after success");
} else {
// Return nothing.
res.end(JSON.stringify({status: "ineligible"}));
functions.logger.log("after ineligible");
}
Hope this solution will help you to solve the problem.

Related

Unable to fetch an already created account

I have the following api method, which I am calling when a button is clicked in the front end:-
export const sendMessage = async (content, roomPublicKey) => {
const { wallet, program, provider } = useWorkspace()
const message = web3.Keypair.generate()
const tx = await program.value.rpc.sendMessage(content, roomPublicKey, {
accounts: {
message: message.publicKey,
author: wallet.value.publicKey,
systemProgram: web3.SystemProgram.programId,
},
signers: [message]
})
console.log(tx);
const messageAccount = await program.value.account.message.fetch(message.publicKey)
}
sendMessage rpc call is creating a new account, and I am then trying to fetch the just created account. But I am getting an error that no such account exists.
I logged the transaction hash and checked on solana explorer and it seems that the account is definitely there, but I am not sure why I am not able to fetch that account
I would recommend always confirming the transactions you run on your code, because the problem may be that you are creating the account, but you check it too fast and the RCP has not been updated yet or something.
That is considering you did everything correctly in your contract code, but i can't know that since you didn't provide it.
Add this line of code after your transaction request:
await program.provider.connection.confirmTransaction(tx);
so it will look like this:
export const sendMessage = async (content, roomPublicKey) => {
const { wallet, program, provider } = useWorkspace()
const message = web3.Keypair.generate()
const tx = await program.value.rpc.sendMessage(content, roomPublicKey, {
accounts: {
message: message.publicKey,
author: wallet.value.publicKey,
systemProgram: web3.SystemProgram.programId,
},
signers: [message]
})
console.log(tx);
await program.provider.connection.confirmTransaction(tx);
const messageAccount = await program.value.account.message.fetch(message.publicKey)
}
Also another check you can do is getting the account info to see if it was created correctly, since fetch uses the discriminator from anchor to determine if the account is the right type.
like this :
const collectionPDAAccount = await program.provider.connection.getAccountInfo(message.publicKey);
Hope this helps!

signInWithEmailAndPassword: getting auth/user-token-expired [duplicate]

I am using Firebase authentication in my iOS app. Is there any way in Firebase when user login my app with Firebase then logout that user all other devices(sessions)? Can I do that with Firebase admin SDK?
When i had this issue i resolved it with cloud functions
Please visit this link for more details https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens
Do the following;
Set up web server with firebase cloud functions (if none exists)
use the admin sdk(thats the only way this method would work) - [Visit this link] (
(https://firebase.google.com/docs/admin/setup#initialize_the_sdk).
Create an api that receives the uid and revokes current sessions as specified in the first link above
admin.auth().revokeRefreshTokens(uid)
.then(() => {
return admin.auth().getUser(uid);
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
})
.then((timestamp) => {
//return valid response to ios app to continue the user's login process
});
Voila users logged out. I hope this gives insight into resolving the issue
Firebase doesn't provide such feature. You need to manage it yourself.
Here is the Firebase Doc and they haven't mentioned anything related to single user sign in.
Here is what you can do for this-
Take one token in User node (Where you save user's other data) in Firebase database and regenerate it every time you logged in into application, Match this token with already logged in user's token (Which is saved locally) in appDidBecomeActive and appDidFinishLaunching or possibly each time you perform any operation with Firebase or may be in some fixed time interval. If tokens are different logged out the user manually and take user to authenticate screen.
What i have done is:
Created collection in firestore called "activeSessions".User email as an id for object and "activeID" field for holding most recent session id.
in sign in page code:
Generating id for a user session every time user is logging in.
Add this id to localstorage(should be cleaned everytime before adding).
Replace "activeID" by generated id in collection "activeSessions" with current user email.
function addToActiveSession() {
var sesID = gen();
var db = firebase.firestore();
localStorage.setItem('userID', sesID);
db.collection("activeSessions").doc(firebase.auth().currentUser.email).set({
activeID: sesID
}).catch(function (error) {
console.error("Error writing document: ", error);
});
}
function gen() {
var buf = new Uint8Array(1);
window.crypto.getRandomValues(buf);
return buf[0];
}
function signin(){
firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
localStorage.clear();
addToActiveSession();
}
}), function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode === 'auth/wrong-password') {
alert('wrong pass');
} else {
alert(errorMessage);
}
console.log(error);
};
}
Then i am checking on each page if the id session in local storage is the same as "activeID" in firestore,if not then log out.
function checkSession(){
var db = firebase.firestore();
var docRef = db.collection("activeSessions").doc(firebase.auth().currentUser.email);
docRef.get().then(function (doc) {
alert(doc.data().activeID);
alert(localStorage.getItem('userID'));
if (doc.data().activeID != localStorage.getItem('userID')) {
alert("bie bie");
firebase.auth().signOut().then(() => {
window.location.href = "signin.html";
}).catch((error) => {
// An error happened.
});
window.location.href = "accountone.html";
} else{alert("vse ok");}
}).catch(function (error) {
console.log("Error getting document:", error);
});
}
PS: window has to be refreshed to log inactive session out.

How do I link auth users to collection in Firestore?

I'm trying to connect a user to the user collection in firestore. I'm using cloud functions, but I don't think I'm implementing it correctly.
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(() => {
console.log('user created')
exports.createUserDoc = functions.auth.user().onCreate((user) => {
console.log("hi")
const userId = user.uid;
const account = {
posts: []
}
return admin.firestore().collection("Users").doc(userId).add(account)
})
But my console.log(hi) isn't showing up. Am I approaching this correctly? Any advice helps!
Right now what i have done is when a user creates an account
i will log the login information into the database.
The document name is set to the user UID that firebase give the user.
Now you can simply request the data from the database with the user UID as
being your .doc(user.uid).
This is the full code.
var htmlEmail = document.getElementById('email').value;
var htmlPass = document.getElementById('password').value;
var htmlUser = document.getElementById('username').value.toLowerCase();
var auth = firebase.auth();
var promise = auth.createUserWithEmailAndPassword(htmlEmail, htmlPass);
// If there is any error stop the process.
promise.catch(function (error) {
var errorCode = error.code;
console.log(`GOT ERROR: ` + errorCode)
if (errorCode == 'auth/weak-password') return // password to weak. Minimal 6 characters
if (errorCode == 'auth/email-already-in-use') return // Return a email already in use error
});
// When no errors create the account
promise.then(function () {
var userUid = auth.currentUser.uid;
var db = firebase.firestore();
db.collection('users').doc(userUid).set({
email: htmlEmail,
emailVertified: false,
name: htmlUser,
online: false,
onlock: false,
password: htmlPass
});
});
Then when the user logs you can simply request the data over the user.uid.
var auth = firebase.auth();
firebase.auth().onAuthStateChanged(function (user) {
// Lay connection with the database.
var firestore = firebase.firestore();
var db = firestore.collection('users').doc(user.uid);
// Get the user data from the database.
db.get().then(function (db) {
// Catch error if exists.
promise.catch(function (error) {
// Return error
});
promise.then(function () {
// continue when success
});
});
});
There could just be there are better ways. (still learning myself).
But this does the trick for me and works very well.
There are 2 things to keep in mind !
I would recommend Firestore over the real time database as it is faster and more secure.
Make sure your database rules are set correctly, so that no one can view / leak your database information. (as you are logging users personal info). If not set correctly users will be able to view your database and even purge all data.
Hope it helps :)
If you find a better way yourself please let us know in here.
We could learn from that also !
In a simplified way you can do this, everytime a user will signup this function will create a firestore collection with the specific parameters.
signupWithEmail: async (_, { email, password, name }) => {
var user = firebase.auth().createUserWithEmailAndPassword(email,
password).then(cred => {
return
firebase.firestore().collection('USERS').doc(cred.user.uid).set({
email,
name
})
})
return { user }
}

There is no user record corresponding to this identifier. The user may have been deleted

There is no user record corresponding to this identifier. The user may have been deleted.
export const createEmployee = ({ email, password}) => {
return (dispatch) =>{`
firebase.auth().createUserWithEmailAndPassword ( email,password )
.then(
firebase.auth().signInWithEmailAndPassword( email,password )
.then(Actions.profile())
)
};
};
According to the official documentation, after the success of createUserWithEmailAndPassword the user is automatically signed-in.
Create a new account by passing the new user's email address and
password to createUserWithEmailAndPassword:
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
If the new account was created, the user is signed in automatically.
Have a look at the Next steps section below to get the signed in user
details.
[...]
Look also at this SO question.
So in your promise you can just get you already authenticated user like that:
var user = firebase.auth().currentUser;
No need to make signInWithEmailAndPassword call.

How to set identifier on firebase authentication via custom token?

I just implemented the linkedin signup & login using firebase custom auth system through this https://firebase.google.com/docs/auth/admin/create-custom-tokens
It`s working but identifier on firebase is null.
How should I send it? Should I update it after creating the user?
I want to save it on create.
Thanks
Try this:
On your server, before minting the custom token, you can create the user with the email:
// Create the user with email first.
admin.auth().createUser({uid: uid, email: linkedinEmail})
.then(function(userRecord) {
// This will return custom token for that user above.
return admin.auth().createCustomToken(userRecord.uid);
})
.catch(function(error) {
// Some error.
});
Another option using client side code, is to set the email client side after signing in with custom token:
firebase.auth().signInWithCustomToken(customToken)
.then(function(result) {
return firebase.auth().currentUser.updateEmail(linkedinEmail);
})
.catch(function(error) {
// Some error occurred.
});
while creating custom token generate a unique UID at your own and save it in database
and as when there is someone trying o login with details match the credentials in database and fetch the correct UID and create a custom token with it. now with the help of custom token you can login
have a look at the code below
this is a well working code from my node.js project
const functions = require('firebase-functions');
const admin = require('firebase-admin');
module.exports = functions.https.onRequest((req, res) => {
//make a random and distinct uid
//and save it in database with the users credentials
//match them at the time of login
admin.auth().createCustomToken(uid)
.then(function(customToken) {
res.setHeader('Content-Type', 'application/json');
var error = false;
var result = {
"Error": error,
"CustomToken": customToken
};
res.send(JSON.stringify(result));
})
.catch(function(err) {
res.setHeader('Content-Type', 'application/json');
var error = true;
var result = {
"Error": error,
"Message": err
};
res.send(JSON.stringify(result));
});
});

Resources