mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Fix user not always logged when opening export after relaunching the app (#3205)
* Also fix signup/login dialog closing before the signup/login is entirely finished
This commit is contained in:
@@ -20,9 +20,9 @@ export type AuthenticatedUser = {|
|
||||
onEdit: () => void,
|
||||
onChangeEmail: () => void,
|
||||
onCreateAccount: () => void,
|
||||
onRefreshUserProfile: () => void,
|
||||
onRefreshFirebaseProfile: () => void,
|
||||
onSendEmailVerification: () => void,
|
||||
onRefreshUserProfile: () => Promise<void>,
|
||||
onRefreshFirebaseProfile: () => Promise<void>,
|
||||
onSendEmailVerification: () => Promise<void>,
|
||||
getAuthorizationHeader: () => Promise<string>,
|
||||
|};
|
||||
|
||||
@@ -38,9 +38,9 @@ export const initialAuthenticatedUser = {
|
||||
onEdit: () => {},
|
||||
onChangeEmail: () => {},
|
||||
onCreateAccount: () => {},
|
||||
onRefreshUserProfile: () => {},
|
||||
onRefreshFirebaseProfile: () => {},
|
||||
onSendEmailVerification: () => {},
|
||||
onRefreshUserProfile: async () => {},
|
||||
onRefreshFirebaseProfile: async () => {},
|
||||
onSendEmailVerification: async () => {},
|
||||
getAuthorizationHeader: () => Promise.reject(new Error('Unimplemented')),
|
||||
};
|
||||
|
||||
|
@@ -15,7 +15,6 @@ import Authentication, {
|
||||
} from '../Utils/GDevelopServices/Authentication';
|
||||
import { User as FirebaseUser } from 'firebase/auth';
|
||||
import LoginDialog from './LoginDialog';
|
||||
import { watchPromiseInState } from '../Utils/WatchPromiseInState';
|
||||
import { showWarningBox } from '../UI/Messages/MessageBox';
|
||||
import { sendSignupDone } from '../Utils/Analytics/EventSender';
|
||||
import AuthenticatedUserContext, {
|
||||
@@ -67,11 +66,51 @@ export default class AuthenticatedUserProvider extends React.Component<
|
||||
changeEmailDialogOpen: false,
|
||||
changeEmailInProgress: false,
|
||||
};
|
||||
_automaticallyUpdateUserProfile = true;
|
||||
|
||||
componentDidMount() {
|
||||
this._resetAuthenticatedUser();
|
||||
this.props.authentication.setOnUserLogoutCallback(this._fetchUserProfile);
|
||||
this._fetchUserProfile();
|
||||
|
||||
// Listen to when the user log out so that we reset the user profile.
|
||||
this.props.authentication.setOnUserLogoutCallback(
|
||||
this._fetchUserProfileWithoutThrowingErrors
|
||||
);
|
||||
|
||||
// When the authenticated user changes, we need to react accordingly
|
||||
// This can happen:
|
||||
// - at the startup, because the authenticated Firebase user was not ready yet
|
||||
// when this component mounted. So we'll fetch the user profile.
|
||||
// - at the login, signup, profile edit. These methods are taking care of
|
||||
// fetching the user profile by themselves, so they will disable the automatic
|
||||
// refresh.
|
||||
// - at any other moment (Firebase user was updated), in which case it's probably
|
||||
// not a problem to fetch again the user profile.
|
||||
this.props.authentication.setOnUserUpdateCallback(() => {
|
||||
if (this._automaticallyUpdateUserProfile) {
|
||||
console.info(
|
||||
'Fetching user profile as the authenticated user changed...'
|
||||
);
|
||||
this._fetchUserProfileWithoutThrowingErrors();
|
||||
} else {
|
||||
console.info(
|
||||
'The authenticated user changed, but not fetching the user profile again (deactivated).'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.props.authentication.getFirebaseUserSync()) {
|
||||
// The user is logged already: fetch its user profile (because the "user update"
|
||||
// won't trigger, as registered too late).
|
||||
console.info(
|
||||
'Fetching user profile as authenticated user found at startup...'
|
||||
);
|
||||
this._automaticallyUpdateUserProfile = false;
|
||||
this._fetchUserProfileWithoutThrowingErrors();
|
||||
this._automaticallyUpdateUserProfile = true;
|
||||
} else {
|
||||
// Don't do anything. Either no user is logged (nothing to do)
|
||||
// or is being logged (the "user udpate" callback will trigger later).
|
||||
}
|
||||
}
|
||||
|
||||
_resetAuthenticatedUser() {
|
||||
@@ -84,7 +123,9 @@ export default class AuthenticatedUserProvider extends React.Component<
|
||||
onChangeEmail: () => this.openChangeEmailDialog(true),
|
||||
onCreateAccount: () => this.openCreateAccountDialog(true),
|
||||
onRefreshUserProfile: this._fetchUserProfile,
|
||||
onRefreshFirebaseProfile: this._reloadFirebaseProfile,
|
||||
onRefreshFirebaseProfile: async () => {
|
||||
await this._reloadFirebaseProfile();
|
||||
},
|
||||
onSendEmailVerification: this._doSendEmailVerification,
|
||||
getAuthorizationHeader: () =>
|
||||
this.props.authentication.getAuthorizationHeader(),
|
||||
@@ -92,12 +133,13 @@ export default class AuthenticatedUserProvider extends React.Component<
|
||||
}));
|
||||
}
|
||||
|
||||
_reloadFirebaseProfile = (callback?: FirebaseUser => void) => {
|
||||
_reloadFirebaseProfile = async (): Promise<?FirebaseUser> => {
|
||||
const { authentication } = this.props;
|
||||
|
||||
authentication.getFirebaseUser((err, firebaseUser: ?FirebaseUser) => {
|
||||
if (err && err.unauthenticated) {
|
||||
return this.setState(({ authenticatedUser }) => ({
|
||||
try {
|
||||
const firebaseUser = await authentication.getFirebaseUser();
|
||||
if (!firebaseUser) {
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
authenticated: false,
|
||||
@@ -107,75 +149,97 @@ export default class AuthenticatedUserProvider extends React.Component<
|
||||
subscription: null,
|
||||
},
|
||||
}));
|
||||
} else if (err || !firebaseUser) {
|
||||
console.error('Unable to fetch user profile', err);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
authenticated: true,
|
||||
firebaseUser,
|
||||
},
|
||||
}),
|
||||
() => {
|
||||
if (!firebaseUser || !callback) return;
|
||||
callback(firebaseUser);
|
||||
}
|
||||
);
|
||||
});
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
authenticated: true,
|
||||
firebaseUser,
|
||||
},
|
||||
}));
|
||||
return firebaseUser;
|
||||
} catch (error) {
|
||||
console.error('Unable to fetch the authenticated Firebase user:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
_fetchUserProfile = () => {
|
||||
_fetchUserProfileWithoutThrowingErrors = async () => {
|
||||
try {
|
||||
await this._fetchUserProfile();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Error while fetching the user profile - but ignoring it.',
|
||||
error
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
_fetchUserProfile = async () => {
|
||||
const { authentication } = this.props;
|
||||
|
||||
this._reloadFirebaseProfile((firebaseUser: FirebaseUser) => {
|
||||
authentication
|
||||
.getUserProfile(authentication.getAuthorizationHeader)
|
||||
.then(user =>
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
profile: user,
|
||||
},
|
||||
}))
|
||||
);
|
||||
getUserUsages(
|
||||
authentication.getAuthorizationHeader,
|
||||
firebaseUser.uid
|
||||
).then(usages =>
|
||||
// First ensure the Firebase authenticated user is up to date
|
||||
// (and let the error propagate if any).
|
||||
const firebaseUser = await this._reloadFirebaseProfile();
|
||||
if (!firebaseUser) return;
|
||||
|
||||
// Fetching user profile related information, but independently from
|
||||
// the user profile itself, to not block in case one of these calls
|
||||
// fails.
|
||||
getUserUsages(authentication.getAuthorizationHeader, firebaseUser.uid).then(
|
||||
usages =>
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
usages,
|
||||
},
|
||||
}))
|
||||
);
|
||||
getUserSubscription(
|
||||
authentication.getAuthorizationHeader,
|
||||
firebaseUser.uid
|
||||
).then(subscription =>
|
||||
})),
|
||||
error => {
|
||||
console.error('Error while loading user usages:', error);
|
||||
}
|
||||
);
|
||||
getUserSubscription(
|
||||
authentication.getAuthorizationHeader,
|
||||
firebaseUser.uid
|
||||
).then(
|
||||
subscription =>
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
subscription,
|
||||
},
|
||||
}))
|
||||
);
|
||||
getUserLimits(
|
||||
authentication.getAuthorizationHeader,
|
||||
firebaseUser.uid
|
||||
).then(limits =>
|
||||
})),
|
||||
error => {
|
||||
console.error('Error while loading user subscriptions:', error);
|
||||
}
|
||||
);
|
||||
getUserLimits(authentication.getAuthorizationHeader, firebaseUser.uid).then(
|
||||
limits =>
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
limits,
|
||||
},
|
||||
}))
|
||||
);
|
||||
});
|
||||
})),
|
||||
error => {
|
||||
console.error('Error while loading user limits:', error);
|
||||
}
|
||||
);
|
||||
|
||||
// Load and wait for the user profile to be fetched.
|
||||
// (and let the error propagate if any).
|
||||
const userProfile = await authentication.getUserProfile(
|
||||
authentication.getAuthorizationHeader
|
||||
);
|
||||
|
||||
this.setState(({ authenticatedUser }) => ({
|
||||
authenticatedUser: {
|
||||
...authenticatedUser,
|
||||
profile: userProfile,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
_doLogout = () => {
|
||||
@@ -183,119 +247,136 @@ export default class AuthenticatedUserProvider extends React.Component<
|
||||
this._resetAuthenticatedUser();
|
||||
};
|
||||
|
||||
_doLogin = (form: LoginForm) => {
|
||||
_doLogin = async (form: LoginForm) => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
watchPromiseInState(this, 'loginInProgress', () =>
|
||||
authentication.login(form).then(
|
||||
() => {
|
||||
this._fetchUserProfile();
|
||||
this.openLoginDialog(false);
|
||||
},
|
||||
(authError: AuthError) => {
|
||||
this.setState({
|
||||
authError,
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
this.setState({
|
||||
loginInProgress: true,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = false;
|
||||
try {
|
||||
await authentication.login(form);
|
||||
await this._fetchUserProfileWithoutThrowingErrors();
|
||||
this.openLoginDialog(false);
|
||||
} catch (authError) {
|
||||
this.setState({ authError });
|
||||
}
|
||||
this.setState({
|
||||
loginInProgress: false,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = true;
|
||||
};
|
||||
|
||||
_doEdit = (form: EditForm) => {
|
||||
_doEdit = async (form: EditForm) => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
watchPromiseInState(this, 'editInProgress', () =>
|
||||
authentication
|
||||
.editUserProfile(authentication.getAuthorizationHeader, form)
|
||||
.then(
|
||||
() => {
|
||||
this._fetchUserProfile();
|
||||
this.openEditProfileDialog(false);
|
||||
},
|
||||
(authError: AuthError) => {
|
||||
this.setState({
|
||||
authError,
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
this.setState({
|
||||
editInProgress: true,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = false;
|
||||
try {
|
||||
await authentication.editUserProfile(
|
||||
authentication.getAuthorizationHeader,
|
||||
form
|
||||
);
|
||||
await this._fetchUserProfileWithoutThrowingErrors();
|
||||
this.openEditProfileDialog(false);
|
||||
} catch (authError) {
|
||||
this.setState({ authError });
|
||||
}
|
||||
this.setState({
|
||||
editInProgress: false,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = true;
|
||||
};
|
||||
|
||||
_doCreateAccount = (form: RegisterForm) => {
|
||||
_doCreateAccount = async (form: RegisterForm) => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
watchPromiseInState(this, 'createAccountInProgress', () =>
|
||||
authentication.createFirebaseAccount(form).then(
|
||||
() => {
|
||||
authentication
|
||||
.createUser(authentication.getAuthorizationHeader, form)
|
||||
.then(() => {
|
||||
this._fetchUserProfile();
|
||||
this.openLoginDialog(false);
|
||||
sendSignupDone(form.email);
|
||||
});
|
||||
},
|
||||
(authError: AuthError) => {
|
||||
this.setState({
|
||||
authError,
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
this.setState({
|
||||
createAccountInProgress: true,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = false;
|
||||
try {
|
||||
await authentication.createFirebaseAccount(form);
|
||||
|
||||
try {
|
||||
await authentication.createUser(
|
||||
authentication.getAuthorizationHeader,
|
||||
form
|
||||
);
|
||||
} catch (error) {
|
||||
// Ignore this error - this is a best effort call
|
||||
// and the user profile will be created on demand later
|
||||
// by the API when fetched.
|
||||
}
|
||||
|
||||
await this._fetchUserProfileWithoutThrowingErrors();
|
||||
this.openLoginDialog(false);
|
||||
sendSignupDone(form.email);
|
||||
} catch (authError) {
|
||||
this.setState({ authError });
|
||||
}
|
||||
this.setState({
|
||||
createAccountInProgress: false,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = true;
|
||||
};
|
||||
|
||||
_doForgotPassword = (form: LoginForm) => {
|
||||
_doForgotPassword = async (form: LoginForm) => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
watchPromiseInState(this, 'forgotPasswordInProgress', () =>
|
||||
authentication.forgotPassword(form).then(
|
||||
() => {
|
||||
this.openResetPassword(true);
|
||||
},
|
||||
(authError: AuthError) => {
|
||||
this.setState({
|
||||
authError,
|
||||
});
|
||||
showWarningBox(
|
||||
"Unable to send you an email to reset your password. Please double-check that the email address that you've entered is valid."
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
_doSendEmailVerification = () => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
authentication.sendFirebaseEmailVerification(() => {
|
||||
this.openEmailVerificationPendingDialog(true);
|
||||
this.setState({
|
||||
forgotPasswordInProgress: true,
|
||||
});
|
||||
try {
|
||||
await authentication.forgotPassword(form);
|
||||
this.openResetPassword(true);
|
||||
} catch (authError) {
|
||||
this.setState({ authError });
|
||||
showWarningBox(
|
||||
"Unable to send you an email to reset your password. Please double-check that the email address that you've entered is valid."
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
forgotPasswordInProgress: false,
|
||||
});
|
||||
};
|
||||
|
||||
_doChangeEmail = (form: ChangeEmailForm) => {
|
||||
_doSendEmailVerification = async () => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
watchPromiseInState(this, 'changeEmailInProgress', () =>
|
||||
authentication
|
||||
.changeEmail(authentication.getAuthorizationHeader, form)
|
||||
.then(
|
||||
() => {
|
||||
this._fetchUserProfile();
|
||||
this.openChangeEmailDialog(false);
|
||||
},
|
||||
(authError: AuthError) => {
|
||||
this.setState({
|
||||
authError,
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
await authentication.sendFirebaseEmailVerification();
|
||||
this.openEmailVerificationPendingDialog(true);
|
||||
};
|
||||
|
||||
_doChangeEmail = async (form: ChangeEmailForm) => {
|
||||
const { authentication } = this.props;
|
||||
if (!authentication) return;
|
||||
|
||||
this.setState({
|
||||
changeEmailInProgress: true,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = false;
|
||||
try {
|
||||
await authentication.changeEmail(
|
||||
authentication.getAuthorizationHeader,
|
||||
form
|
||||
);
|
||||
await this._fetchUserProfileWithoutThrowingErrors();
|
||||
this.openChangeEmailDialog(false);
|
||||
} catch (authError) {
|
||||
this.setState({ authError });
|
||||
}
|
||||
this.setState({
|
||||
changeEmailInProgress: false,
|
||||
});
|
||||
this._automaticallyUpdateUserProfile = true;
|
||||
};
|
||||
|
||||
openEmailVerificationPendingDialog = (open: boolean = true) => {
|
||||
@@ -394,7 +475,11 @@ export default class AuthenticatedUserProvider extends React.Component<
|
||||
authenticatedUser={this.state.authenticatedUser}
|
||||
onClose={() => {
|
||||
this.openEmailVerificationPendingDialog(false);
|
||||
this.state.authenticatedUser.onRefreshFirebaseProfile();
|
||||
this.state.authenticatedUser
|
||||
.onRefreshFirebaseProfile()
|
||||
.catch(() => {
|
||||
// Ignore any error, we can't do much.
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@@ -18,7 +18,7 @@ import { getEmailErrorText } from './CreateAccountDialog';
|
||||
type Props = {|
|
||||
firebaseUser: FirebaseUser,
|
||||
onClose: () => void,
|
||||
onChangeEmail: (form: ChangeEmailForm) => void,
|
||||
onChangeEmail: (form: ChangeEmailForm) => Promise<void>,
|
||||
changeEmailInProgress: boolean,
|
||||
error: ?AuthError,
|
||||
|};
|
||||
|
@@ -20,7 +20,7 @@ import { UsernameField, isUsernameValid } from './UsernameField';
|
||||
type Props = {|
|
||||
onClose: () => void,
|
||||
onGoToLogin: () => void,
|
||||
onCreateAccount: (form: RegisterForm) => void,
|
||||
onCreateAccount: (form: RegisterForm) => Promise<void>,
|
||||
createAccountInProgress: boolean,
|
||||
error: ?AuthError,
|
||||
|};
|
||||
|
@@ -22,7 +22,7 @@ import TextField from '../UI/TextField';
|
||||
type Props = {|
|
||||
profile: Profile,
|
||||
onClose: () => void,
|
||||
onEdit: (form: EditForm) => void,
|
||||
onEdit: (form: EditForm) => Promise<void>,
|
||||
editInProgress: boolean,
|
||||
error: ?AuthError,
|
||||
|};
|
||||
|
@@ -27,7 +27,11 @@ export default function EmailVerificationPendingDialog({
|
||||
!!authenticatedUser.firebaseUser &&
|
||||
!!authenticatedUser.firebaseUser.emailVerified;
|
||||
useInterval(
|
||||
() => authenticatedUser.onRefreshFirebaseProfile(),
|
||||
() => {
|
||||
authenticatedUser.onRefreshFirebaseProfile().catch(() => {
|
||||
// Ignore any error, will be retried anyway.
|
||||
});
|
||||
},
|
||||
isVerified ? null : 3900
|
||||
);
|
||||
|
||||
|
@@ -21,8 +21,8 @@ import { ColumnStackLayout } from '../UI/Layout';
|
||||
type Props = {|
|
||||
onClose: () => void,
|
||||
onGoToCreateAccount: () => void,
|
||||
onLogin: (form: LoginForm) => void,
|
||||
onForgotPassword: (form: LoginForm) => void,
|
||||
onLogin: (form: LoginForm) => Promise<void>,
|
||||
onForgotPassword: (form: LoginForm) => Promise<void>,
|
||||
loginInProgress: boolean,
|
||||
error: ?AuthError,
|
||||
resetPasswordDialogOpen: boolean,
|
||||
|
@@ -27,7 +27,11 @@ export default function SubscriptionPendingDialog({
|
||||
!!authenticatedUser.subscription &&
|
||||
!!authenticatedUser.subscription.planId;
|
||||
useInterval(
|
||||
() => authenticatedUser.onRefreshUserProfile(),
|
||||
() => {
|
||||
authenticatedUser.onRefreshUserProfile().catch(() => {
|
||||
// Ignore any error, will be retried anyway.
|
||||
});
|
||||
},
|
||||
hasPlan ? null : 3900
|
||||
);
|
||||
|
||||
|
@@ -58,35 +58,36 @@ export type AuthError = {
|
||||
};
|
||||
|
||||
export default class Authentication {
|
||||
firebaseUser: ?FirebaseUser = null;
|
||||
user: ?Profile = null;
|
||||
auth: Auth;
|
||||
_onUserLogoutCallback: ?() => void = null;
|
||||
_onUserLogoutCallback: ?() => void | Promise<void> = null;
|
||||
_onUserUpdateCallback: ?() => void | Promise<void> = null;
|
||||
|
||||
constructor() {
|
||||
const app = initializeApp(GDevelopFirebaseConfig);
|
||||
this.auth = getAuth(app);
|
||||
onAuthStateChanged(this.auth, user => {
|
||||
if (user) {
|
||||
// User has been updated. No need to fetch more info,
|
||||
// this is handled directly by the corresponding actions (edit, signup, login...)
|
||||
this.firebaseUser = user;
|
||||
// User has logged in or changed.
|
||||
if (this._onUserUpdateCallback) this._onUserUpdateCallback();
|
||||
} else {
|
||||
// User has logged out.
|
||||
this.firebaseUser = null;
|
||||
if (this._onUserLogoutCallback) this._onUserLogoutCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setOnUserLogoutCallback = (cb: () => void) => {
|
||||
setOnUserLogoutCallback = (cb: () => void | Promise<void>) => {
|
||||
this._onUserLogoutCallback = cb;
|
||||
};
|
||||
|
||||
setOnUserUpdateCallback = (cb: () => void | Promise<void>) => {
|
||||
this._onUserUpdateCallback = cb;
|
||||
};
|
||||
|
||||
createFirebaseAccount = (form: RegisterForm): Promise<void> => {
|
||||
return createUserWithEmailAndPassword(this.auth, form.email, form.password)
|
||||
.then(userCredentials => {
|
||||
this.firebaseUser = userCredentials.user;
|
||||
// The user is now stored in `this.auth`.
|
||||
sendEmailVerification(userCredentials.user);
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -101,7 +102,8 @@ export default class Authentication {
|
||||
): Promise<void> => {
|
||||
return getAuthorizationHeader()
|
||||
.then(authorizationHeader => {
|
||||
if (!this.firebaseUser) {
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser) {
|
||||
console.error(
|
||||
'Cannot create the user as it is not logged in any more.'
|
||||
);
|
||||
@@ -112,13 +114,13 @@ export default class Authentication {
|
||||
return axios.post(
|
||||
`${GDevelopUserApi.baseUrl}/user`,
|
||||
{
|
||||
id: this.firebaseUser.uid,
|
||||
id: currentUser.uid,
|
||||
email: form.email,
|
||||
username: form.username,
|
||||
},
|
||||
{
|
||||
params: {
|
||||
userId: this.firebaseUser.uid,
|
||||
userId: currentUser.uid,
|
||||
},
|
||||
headers: {
|
||||
Authorization: authorizationHeader,
|
||||
@@ -126,8 +128,8 @@ export default class Authentication {
|
||||
}
|
||||
);
|
||||
})
|
||||
.then(response => {
|
||||
this.user = response.data;
|
||||
.then(() => {
|
||||
// User successfully created
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error while creating user:', error);
|
||||
@@ -138,7 +140,7 @@ export default class Authentication {
|
||||
login = (form: LoginForm): Promise<void> => {
|
||||
return signInWithEmailAndPassword(this.auth, form.email, form.password)
|
||||
.then(userCredentials => {
|
||||
this.firebaseUser = userCredentials.user;
|
||||
// The user is now stored in `this.auth`.
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error while login:', error);
|
||||
@@ -150,58 +152,66 @@ export default class Authentication {
|
||||
return sendPasswordResetEmail(this.auth, form.email);
|
||||
};
|
||||
|
||||
getFirebaseUser = (cb: (any, ?FirebaseUser) => void) => {
|
||||
if (!this.isAuthenticated()) return cb({ unauthenticated: true });
|
||||
getFirebaseUser = async (): Promise<?FirebaseUser> => {
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// In order to fetch the latest firebaseUser properties (like emailVerified)
|
||||
// we have to call the reload method.
|
||||
this.auth.currentUser.reload().then(() => cb(null, this.firebaseUser));
|
||||
await currentUser.reload();
|
||||
return this.auth.currentUser;
|
||||
};
|
||||
|
||||
sendFirebaseEmailVerification = (cb: () => void): Promise<void> => {
|
||||
return this.auth.currentUser.reload().then(() => {
|
||||
if (!this.firebaseUser || this.firebaseUser.emailVerified) return;
|
||||
sendEmailVerification(this.auth.currentUser).then(
|
||||
() => {
|
||||
cb();
|
||||
},
|
||||
(error: Error) => {
|
||||
showErrorBox({
|
||||
message: 'An email has been sent recently, please try again later.',
|
||||
rawError: error,
|
||||
errorId: 'email-verification-send-error',
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
sendFirebaseEmailVerification = async (): Promise<void> => {
|
||||
{
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser)
|
||||
throw new Error(
|
||||
'Tried to send verification email while not authenticated.'
|
||||
);
|
||||
|
||||
await currentUser.reload();
|
||||
}
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser || currentUser.emailVerified) return;
|
||||
|
||||
try {
|
||||
sendEmailVerification(currentUser);
|
||||
} catch (error) {
|
||||
showErrorBox({
|
||||
message:
|
||||
'An email has been sent recently, check your inbox or please try again later.',
|
||||
rawError: error,
|
||||
errorId: 'email-verification-send-error',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
changeEmail = (
|
||||
changeEmail = async (
|
||||
getAuthorizationHeader: () => Promise<string>,
|
||||
form: ChangeEmailForm
|
||||
) => {
|
||||
return updateEmail(this.firebaseUser, form.email)
|
||||
.then(() => sendEmailVerification(this.firebaseUser))
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser)
|
||||
throw new Error('Tried to change email while not authenticated.');
|
||||
|
||||
return updateEmail(currentUser, form.email)
|
||||
.then(() => sendEmailVerification(currentUser))
|
||||
.then(() => {
|
||||
console.log('Email successfully changed in Firebase.');
|
||||
return getAuthorizationHeader();
|
||||
})
|
||||
.then(authorizationHeader => {
|
||||
if (!this.firebaseUser) {
|
||||
console.error(
|
||||
'Cannot finish editing the user email as it is not logged in any more.'
|
||||
);
|
||||
throw new Error(
|
||||
'Cannot finish editing the user email as it is not logged in any more.'
|
||||
);
|
||||
}
|
||||
return axios.patch(
|
||||
`${GDevelopUserApi.baseUrl}/user/${this.firebaseUser.uid}`,
|
||||
`${GDevelopUserApi.baseUrl}/user/${currentUser.uid}`,
|
||||
{
|
||||
email: form.email,
|
||||
},
|
||||
{
|
||||
params: {
|
||||
userId: this.firebaseUser.uid,
|
||||
userId: currentUser.uid,
|
||||
},
|
||||
headers: {
|
||||
Authorization: authorizationHeader,
|
||||
@@ -218,28 +228,21 @@ export default class Authentication {
|
||||
});
|
||||
};
|
||||
|
||||
getUserProfile = (getAuthorizationHeader: () => Promise<string>) => {
|
||||
getUserProfile = async (getAuthorizationHeader: () => Promise<string>) => {
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser)
|
||||
throw new Error('Tried to get user profile while not authenticated.');
|
||||
|
||||
return getAuthorizationHeader()
|
||||
.then(authorizationHeader => {
|
||||
if (!this.firebaseUser) {
|
||||
console.error(
|
||||
'Cannot get the user profile as it is not logged in any more.'
|
||||
);
|
||||
throw new Error(
|
||||
'Cannot get the user profile as it is not logged in any more.'
|
||||
);
|
||||
}
|
||||
return axios.get(
|
||||
`${GDevelopUserApi.baseUrl}/user/${this.firebaseUser.uid}`,
|
||||
{
|
||||
params: {
|
||||
userId: this.firebaseUser.uid,
|
||||
},
|
||||
headers: {
|
||||
Authorization: authorizationHeader,
|
||||
},
|
||||
}
|
||||
);
|
||||
return axios.get(`${GDevelopUserApi.baseUrl}/user/${currentUser.uid}`, {
|
||||
params: {
|
||||
userId: currentUser.uid,
|
||||
},
|
||||
headers: {
|
||||
Authorization: authorizationHeader,
|
||||
},
|
||||
});
|
||||
})
|
||||
.then(response => response.data)
|
||||
.catch(error => {
|
||||
@@ -248,30 +251,26 @@ export default class Authentication {
|
||||
});
|
||||
};
|
||||
|
||||
editUserProfile = (
|
||||
editUserProfile = async (
|
||||
getAuthorizationHeader: () => Promise<string>,
|
||||
form: EditForm
|
||||
) => {
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser)
|
||||
throw new Error('Tried to edit user profile while not authenticated.');
|
||||
|
||||
return getAuthorizationHeader()
|
||||
.then(authorizationHeader => {
|
||||
if (!this.firebaseUser) {
|
||||
console.error(
|
||||
'Cannot finish editing the user as it is not logged in any more.'
|
||||
);
|
||||
throw new Error(
|
||||
'Cannot finish editing the user as it is not logged in any more.'
|
||||
);
|
||||
}
|
||||
const { username, description } = form;
|
||||
return axios.patch(
|
||||
`${GDevelopUserApi.baseUrl}/user/${this.firebaseUser.uid}`,
|
||||
`${GDevelopUserApi.baseUrl}/user/${currentUser.uid}`,
|
||||
{
|
||||
username,
|
||||
description,
|
||||
},
|
||||
{
|
||||
params: {
|
||||
userId: this.firebaseUser.uid,
|
||||
userId: currentUser.uid,
|
||||
},
|
||||
headers: {
|
||||
Authorization: authorizationHeader,
|
||||
@@ -287,7 +286,7 @@ export default class Authentication {
|
||||
};
|
||||
|
||||
getFirebaseUserSync = (): ?FirebaseUser => {
|
||||
return this.firebaseUser;
|
||||
return this.auth.currentUser || null;
|
||||
};
|
||||
|
||||
logout = () => {
|
||||
@@ -301,14 +300,14 @@ export default class Authentication {
|
||||
});
|
||||
};
|
||||
|
||||
getAuthorizationHeader = (): Promise<string> => {
|
||||
if (!this.firebaseUser)
|
||||
return Promise.reject(new Error('User is not authenticated'));
|
||||
getAuthorizationHeader = async (): Promise<string> => {
|
||||
const { currentUser } = this.auth;
|
||||
if (!currentUser) throw new Error('User is not authenticated.');
|
||||
|
||||
return this.firebaseUser.getIdToken().then(token => `Bearer ${token}`);
|
||||
return currentUser.getIdToken().then(token => `Bearer ${token}`);
|
||||
};
|
||||
|
||||
isAuthenticated = (): boolean => {
|
||||
return !!this.firebaseUser;
|
||||
return !!this.auth.currentUser;
|
||||
};
|
||||
}
|
||||
|
@@ -1,25 +0,0 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
export const watchPromiseInState = (
|
||||
component: React.Component<*, *>,
|
||||
stateField: string,
|
||||
fn: () => Promise<any>
|
||||
) => {
|
||||
component.setState({
|
||||
[stateField]: true,
|
||||
});
|
||||
return fn()
|
||||
.then(value => {
|
||||
component.setState({
|
||||
[stateField]: false,
|
||||
});
|
||||
return value;
|
||||
})
|
||||
.catch(err => {
|
||||
component.setState({
|
||||
[stateField]: false,
|
||||
});
|
||||
throw err;
|
||||
});
|
||||
};
|
@@ -102,13 +102,13 @@ export const fakeIndieAuthenticatedUser: AuthenticatedUser = {
|
||||
onEdit: () => {},
|
||||
onChangeEmail: () => {},
|
||||
onCreateAccount: () => {},
|
||||
onRefreshUserProfile: () => {
|
||||
onRefreshUserProfile: async () => {
|
||||
console.info('This should refresh the user profile');
|
||||
},
|
||||
onRefreshFirebaseProfile: () => {
|
||||
onRefreshFirebaseProfile: async () => {
|
||||
console.info('This should refresh the firebase profile');
|
||||
},
|
||||
onSendEmailVerification: () => {
|
||||
onSendEmailVerification: async () => {
|
||||
console.info('This should send the email verification');
|
||||
},
|
||||
getAuthorizationHeader: () => Promise.resolve('fake-authorization-header'),
|
||||
@@ -126,13 +126,13 @@ export const fakeNoSubscriptionAuthenticatedUser: AuthenticatedUser = {
|
||||
onEdit: () => {},
|
||||
onChangeEmail: () => {},
|
||||
onCreateAccount: () => {},
|
||||
onRefreshUserProfile: () => {
|
||||
onRefreshUserProfile: async () => {
|
||||
console.info('This should refresh the user profile');
|
||||
},
|
||||
onRefreshFirebaseProfile: () => {
|
||||
onRefreshFirebaseProfile: async () => {
|
||||
console.info('This should refresh the firebase profile');
|
||||
},
|
||||
onSendEmailVerification: () => {
|
||||
onSendEmailVerification: async () => {
|
||||
console.info('This should send the email verification');
|
||||
},
|
||||
getAuthorizationHeader: () => Promise.resolve('fake-authorization-header'),
|
||||
@@ -150,13 +150,13 @@ export const fakeAuthenticatedAndEmailVerifiedUser: AuthenticatedUser = {
|
||||
onEdit: () => {},
|
||||
onChangeEmail: () => {},
|
||||
onCreateAccount: () => {},
|
||||
onRefreshUserProfile: () => {
|
||||
onRefreshUserProfile: async () => {
|
||||
console.info('This should refresh the user profile');
|
||||
},
|
||||
onRefreshFirebaseProfile: () => {
|
||||
onRefreshFirebaseProfile: async () => {
|
||||
console.info('This should refresh the firebase profile');
|
||||
},
|
||||
onSendEmailVerification: () => {
|
||||
onSendEmailVerification: async () => {
|
||||
console.info('This should send the email verification');
|
||||
},
|
||||
getAuthorizationHeader: () => Promise.resolve('fake-authorization-header'),
|
||||
@@ -174,13 +174,13 @@ export const fakeAuthenticatedButLoadingAuthenticatedUser: AuthenticatedUser = {
|
||||
onEdit: () => {},
|
||||
onChangeEmail: () => {},
|
||||
onCreateAccount: () => {},
|
||||
onRefreshUserProfile: () => {
|
||||
onRefreshUserProfile: async () => {
|
||||
console.info('This should refresh the user profile');
|
||||
},
|
||||
onRefreshFirebaseProfile: () => {
|
||||
onRefreshFirebaseProfile: async () => {
|
||||
console.info('This should refresh the firebase profile');
|
||||
},
|
||||
onSendEmailVerification: () => {
|
||||
onSendEmailVerification: async () => {
|
||||
console.info('This should send the email verification');
|
||||
},
|
||||
getAuthorizationHeader: () => Promise.resolve('fake-authorization-header'),
|
||||
@@ -198,13 +198,13 @@ export const fakeNotAuthenticatedAuthenticatedUser: AuthenticatedUser = {
|
||||
onEdit: () => {},
|
||||
onChangeEmail: () => {},
|
||||
onCreateAccount: () => {},
|
||||
onRefreshUserProfile: () => {
|
||||
onRefreshUserProfile: async () => {
|
||||
console.info('This should refresh the user profile');
|
||||
},
|
||||
onRefreshFirebaseProfile: () => {
|
||||
onRefreshFirebaseProfile: async () => {
|
||||
console.info('This should refresh the firebase profile');
|
||||
},
|
||||
onSendEmailVerification: () => {
|
||||
onSendEmailVerification: async () => {
|
||||
console.info('This should send the email verification');
|
||||
},
|
||||
getAuthorizationHeader: () => Promise.resolve('fake-authorization-header'),
|
||||
|
Reference in New Issue
Block a user