I need to have an option for creating accounts for users, and give them the accounts. So the accounts should be created by an admin of some company, the users will login with that account and then they can change the password.
I've found one solution, with Admin SDK but if I understand it correctly, you need your own backend.
Is there any other way? Or do you have another suggestion how to manage this? Basically, a user is suppose to be linked to a company. And he has a role in that company. Admin of the company chooses the role for each user.
The common way is to use the Admin SDK in a callable cloud function to do this. It is pretty straight forward.
If you do it from the front end when you create the user the admin user is logged out and the new user is logged in.
There is however a hack to do it from the front end without being logged out, by using a secondary firebase app such as below.
import * as firebase from 'firebase/app';
const secondaryApp = firebase.initializeApp(environment.firebase, 'Secondary');
async registerUser(email, password: string) {
try {
const userCredential = await secondaryApp.auth().createUserWithEmailAndPassword(email, password)
secondaryApp.auth().signOut(); // signout the user that was just created
// If you wanted to create a document for the user in firestore then you could do it here.
return userCredential;
} catch (err) {
throw err;
}
}
Related
Introduce the problem
I use an email to log in.
If I log out and try to log in to the same email using Google, the UID of the account changes. I can't change back to the old UID. How can I provide multiple providers for an account?
What I tried
I asked this question to ChatGPT but it didn't answer my question. I also googled this problem.
I have read this documentation and used its code, but it didn't work. Not sure if I'm using its code correctly.
I've read this question but it didn't help me.
A minimal, reproducible example
Future<void> signInWithEmailAndPassword(String email, String password) async {
try {
await firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
final credential = EmailAuthProvider.credential(email: email, password: password);
await FirebaseAuth.instance.currentUser?.linkWithCredential(credential);
} on FirebaseAuthException catch (e) {
throw FirebaseAuthException(code: e.code, message: e.message);
}
}
You should be able to achieve this via the Firebase Console. The Firebase Authentication Settings tab allows you to link accounts with the same email.
https://console.firebase.google.com/u/0/project/yourprojectname/authentication/settings
Account linking works by first having a currently signed in user, and then linking an additional provider to it. In steps:
Sign in with the existing provider.
Check that currentUser isn't null.
Create credentials for the additional provider
Link the additional provider to the existing account by calling linkWithCredential.
I'm using AWS Amplify with Cognito for authenticating users in my Flutter Application.
Users can use the app without having to sign up immediately, by using Cognito's Guest Authentication feature. A user can optionally sign up later on using the Amplify.Auth.signUp method.
The problem is that after signing up and then logging in, the created user has another ID than the guest user. Is there a way to keep the old ID across signup? If not, what options do I have besides updating all of the user's database items to the new ID?
I've seen that the js version of amplify supports this behavior by default.
I'm using this code to get the user's ID:
Future<String> getUserId() async {
final currentUser = await Amplify.Auth.fetchAuthSession(options: const CognitoSessionOptions(getAWSCredentials: true)) as CognitoAuthSession;
final uid = currentUser.identityId?.split(':').last;
if (uid == null) {
throw RemoteException(S.current.error_no_user_logged_in);
}
return uid;
}
I would like to force users that previously authenticated with Facebook to sign up using a new provider. The reason for this is that I would like to remove Facebook as an authentication provider. I would unlink the user once the user has been successfully linked with the new provider.
For example, the user is presented with new authentication options and the user selects to continue with email. I have the following code:
func createUserAndSignIn(
username: String,
email: String,
password: String
) async throws -> String {
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
// if user is already logged in (in this case with Facebook)
if let user = Auth.auth().currentUser {
try await user.link(with: credential)
}
do {
let authDataResult = try await Auth.auth().createUser(withEmail: email, password: password)
return authDataResult.user.uid
} catch {
// throw error
}
}
The linking of accounts (user.link(with:)) fails with the following error:
Domain=FIRAuthErrorDomain Code=17014 "This operation is sensitive and requires recent authentication. Log in again before retrying this request." UserInfo={NSLocalizedDescription=This operation is sensitive and requires recent authentication. Log in again before retrying this request., FIRAuthErrorUserInfoNameKey=ERROR_REQUIRES_RECENT_LOGIN}
Would this be even be the correct approach for this?
You have to re-authenticate the user. Using the current credential
if
let user = Auth.auth().currentUser,
let currentAccessToken = AccessToken.current
{
// Prompt the user to re-provide their sign-in credentials
let result = try await user.reauthenticate(
with: FacebookAuthProvider.credential(withAccessToken: currentAccessToken.tokenString)
)
// Then link the user
try await user.link(with: newCredential)
// User can be unlinked from Facebook
try await Auth.auth().currentUser?.unlink(fromProvider: "facebook.com")
}
This is needed for several operations such as updating the user's email, password or deleting the user.
The approach you're taking is close. The error you're getting is because some operations in firebase require a recent authentication to have taken place:
FIRAuthErrorCodeRequiresRecentLogin: Updating a user’s email is a
security sensitive operation that requires a recent login from the
user. This error indicates the user has not signed in recently enough.
To resolve, reauthenticate the user by invoking
reauthenticateWithCredential:completion: on FIRUser. [1]
The steps you want to take are:
Authenticate the user with an existing auth method.
Prompt the user for their email and password
Use the email and password to create an AuthCredential object.
Pass that AuthCredential object to the user's linkWithCredential method.
There's a complete walkthrough for this in the Firebase docs: https://firebase.google.com/docs/auth/web/account-linking#link-email-address-and-password-credentials-to-a-user-account
But the key point is that you have to authenticate the user with an existing provider before you do this, even if they are technically "logged in".
Note that the steps are slightly different if you want to link the user to another Auth provider other than email (such as Google): https://firebase.google.com/docs/auth/web/account-linking#link-federated-auth-provider-credentials-to-a-user-account
After that, if you wish, you can use unlink to remove the Facebook authentication.
I have been looking online but not yet found a way to sign out a user when there account is deleted on the firebase admin console.
Is there a way to setup maybe a cloud function or some kind of way to sign out a user when their account is deleted via the admin console.
If an account is deleted from the admin, the user's refresh token is automatically revoked so there's nothing more to do on the backend. On the frontend, the user's ID token will survive until it's natural expiration so you need a way to notify the client that their token has been revoked.
An easy way to do that is to store the user's status in the Realtime Database and then subscribe to any changes and log out the user out if that value changes.
Let's assume that your database looks like this:
{
"users": {
"userId123": {
"status": "active"
}
}
}
In your frontend code the client subscribes to /users/userId123 on page load and when the status value changes, perform an action.
For example:
firebase.database.ref('/users/userId123/status').onWrite((change, context) => {
if (!change.after.exists()) {
callSignOut();
return null;
}
const val = change.after.val();
if (val !== 'active') {
callSignOut();
return null;
}
});
Completely untested but should get you started.
I have an app where I want to direct newly signed up users to a welcome page. With the core Meteor users it's easy since there are Meteor.loginWithPassword and Accounts.createUser. However for signup/login with Facebook there is only Meteor.loginWithFacebook.
So is there a way to differentiate between the first time user "logs" in with Facebook and all the other times so I can only direct them to that welcome page once?
Accounts.onCreateUser might be what you're looking for.
You could set a flag in this callback to differentiate new users from regular users.
Accounts.onCreateUser(function(options, user) {
user.isNew = true;
if (options.profile)
user.profile = options.profile;
return user;
});
Then use Accounts.onLogin client-side to handle new users being redirected to a welcome page.
Accounts.onLogin(function(){
if(Meteor.user().isNew){
Meteor.users.update(Meteor.userId(),{
$set:{
isNew: false
}
});
Router.go("welcome");
}
});
This is untested pseudo-code but you get the idea.