Reputation: 2880
I'm handling both anonymous and Google accounts using AngularFire2. If a user logs in using a Google account, I'd like to convert their anonymous account to their permanent (Google) account, so they can keep using the app seamlessly.
It seems easy enough using the Firebase API, but I'm not seeing the ability to do this in AngularFire2.
For Firebase, you get the AuthCredential for the new auth provider and then use the link method to convert the account:
var credential = firebase.auth.GoogleAuthProvider.credential(
googleUser.getAuthResponse().id_token);
auth.currentUser.link(credential).then(function(user) {
console.log("Anonymous account successfully upgraded", user);
}, function(error) {
console.log("Error upgrading anonymous account", error);
});
Is this possible in AngularFire2?
Upvotes: 3
Views: 1560
Reputation: 6225
Thrown if there already exists an account with the email address asserted by the credential. Resolve this by calling firebase.auth.Auth.fetchSignInMethodsForEmail with the error.email and then asking the user to sign in using one of the returned providers. Once the user is signed in, the original credential retrieved from the error.credential can be linked to the user with firebase.User.linkWithCredential to prevent the user from signing in again to the original provider via popup or redirect. If you are using redirects for sign in, save the credential in session storage and then retrieve on redirect and repopulate the credential using for example firebase.auth.GoogleAuthProvider.credential depending on the credential provider id and complete the link.
A good solution would be to ➡️ link multiple auth providers to an account. User can signup/signin with any method and we will authenticate them and update their records. So the same user can log in with multiple auth providers.
// Sign in with Google
GoogleAuth() {
return this.authLogin(new GoogleAuthProvider()).then(() => {});
}
// Sign in with GitHub
GithubAuth() {
return this.authLogin(new GithubAuthProvider()).then(() => {});
}
// Auth logic to run auth providers
authLogin(provider: any) {
return this.afAuth
.signInWithPopup(provider)
.then((result) => {
this.setUserData(result.user).then(() => {
this.router.navigate(['dashboard']);
});
})
.catch(error => {
const code = error.code;
const credential = error.credential;
if (code === 'auth/account-exists-with-different-credential') {
// Get other Auth providers user has used before (e.g google.com)
this.afAuth.fetchSignInMethodsForEmail(error.email).then(result => {
const provider = this.getAuthProvider(result[0]);
// Log in the user with other provider used before
this.authLogin(provider).then(result => {
this.afAuth.authState.pipe(take(1)).subscribe(user => {
if (user) {
user.linkWithCredential(credential).then(() => {
console.log('Credential linked successfully: ', credential);
});
}
});
});
});
}
});
}
More on this over here: https://medium.com/p/8a0f192458f8
Upvotes: 0
Reputation: 9298
Full example in AngularFire2 v4. It will upgrade an anonymous user to the Google provider, keeping the same auth UID.
import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit {
user: Observable<firebase.User>;
constructor(private afAuth: AngularFireAuth) { }
ngOnInit() {
this.user = this.afAuth.authState;
}
anonymousLogin() {
return this.afAuth.auth.signInAnonymously()
}
anonymousUpgrade() {
const provider = new firebase.auth.GoogleAuthProvider()
firebase.auth().currentUser.linkWithPopup(provider)
}
}
Upvotes: 1
Reputation: 2279
here is a working example;
import * as firebase from 'firebase':
upgradeAnonymous() {
let credential = new firebase.auth.GoogleAuthProvider();
firebase.auth().currentUser.linkWithPopup(credential).then(function(user) {
console.log("Anonymous account successfully upgraded", user);
}, function(error) {
console.log("Error upgrading anonymous account", error);
})
}
Upvotes: 0
Reputation: 58400
There should be no reason you cannot use the underlying Firebase API, as the Firebase App that's created by AngularFire2 is made available for injection.
You can use the injected app to access any methods of the auth()
(or storage()
) instances that are not exposed by AngularFire2.
For the purposes of an example, you could inject it into an app component like this:
import { Component, Inject } from "@angular/core";
import { AngularFire, FirebaseApp } from "angularfire2";
import * as firebase from "firebase";
@Component({
selector: "app",
template: ...
})
export class AppComponent {
constructor(
// Inject AngularFire2:
private angularFire: AngularFire,
// Inject the Firebase App instance:
@Inject(FirebaseApp) private firebaseApp: firebase.app.App
) {
// Perform some sort of login.
...
angularFire.auth.subscribe((state) => {
// AngularFire2's auth observable will emit when the authentication
// state changes and if the state is non-null, there will be a
// current user. At this point, you should be able to do with the
// injected app what it was you were doing with the SDK.
if (state) {
var credential = firebase.auth
.GoogleAuthProvider
.credential(googleUser.getAuthResponse().id_token);
firebaseApp.auth()
.currentUser
.link(credential)
.then((user) => {
console.log("Anonymous account successfully upgraded", user);
})
.catch((error) => {
console.log("Error upgrading anonymous account", error);
});
}
});
}
}
You don't have to use the auth
observable; you could also put the code into a promise chain using the promise returned by AngularFire2's auth.login
method.
As noted in the comments below, the GoogleAuthProvider
class is in the firebase.app
namespace (not the app instance). Also, as the current user is made available by AngularFire2 in the (confusingly named) auth
property of the FirebaseAuthState
, you don't need to inject the app instance to do what you want to do with the Google provider. You just need the firebase
import:
this.angularFire.auth.login({
email: "[email protected]",
password: "password",
})
.then((authState: FirebaseAuthState) => {
authState.auth.linkWithRedirect(new firebase.auth.GoogleAuthProvider());
});
Upvotes: 3