I want to implement authorization in my client-side application but I've got problem with update Token in React Application with Keycloak.
App.js
import keycloak from "../../keycloak";
const App = () => {
const handleOnEvent = async (event,error) => {
if(event === 'onTokenExpired'){
keycloak.updateToken(300).then(
(response) => {
//I want to update my existing Token
alert("response: ", response )
})
.catch(error => {
console.log("error: ", error)
})
}
}
return (
<>
<ReactKeycloakProvider
authClient={keycloak}
onEvent={(event,error) => handleOnEvent(event,error)}>
<AppRouter/>
</ReactKeycloakProvider>
</>)
}
export default App;
Header
const Header = () => {
const {keycloak,initialized} = useKeycloak()
useEffect(() => {
if(keycloak.authenticated){
alert(JSON.stringify(keycloak))
localStorage.setItem("keycloakToken", keycloak.token); //set keycloak token to localStorag
localStorage.setItem("keycloakRefreshToken", keycloak.refreshToken); // set refresh token
setJWTToken(keycloak.token) //set to axios Authorization Bearer
}
},[keycloak.authenticated])
return(
<>
{
keycloak && !keycloak.authenticated && <UnloggedHeader keycloak={keycloak}/>
}
{
keycloak && keycloak.authenticated && <LoggedHeader keycloak={keycloak}/>
}
</>
)
}
export default Header
UnloggedHeader
function UnloggedHeader({keycloak}){
const signIn = () => {
keycloak.login()
}
return (
<div style={{minWidth: '1100px'}}>
<AppBar position="sticky" color='transparent'>
<Toolbar>
<Button onClick={signIn} variant="contained" color="primary">Login</Button>
<Typography variant="body1" component="h6">Unlogged</Typography>
</Toolbar>
</AppBar>
</div>
);
}
export default UnloggedHeader
LoggedHeader
function LoggedHeader({keycloak}){
let history = useHistory()
const [anchorEl, setAnchorEl] = React.useState(null);
const isMenuOpen = Boolean(anchorEl);
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const [userInfo,setUserInfo] = useState()
useEffect(() => {
keycloak.loadUserInfo().then(userInfo => {
setUserInfo(userInfo)
localStorage.setItem("username", userInfo.preferred_username); // set username of user
})
},[])
const handleMenuClose = () => {
setAnchorEl(null);
};
const handleUserLogoutClick = () => {
keycloak.logout()
history.push("/")
}
return (
<div style={{minWidth: '1100px'}}>
<AppBar position="sticky" color='transparent'>
<Toolbar>
<Typography variant="body1" component="h6">{userInfo !== undefined ? userInfo.preferred_username : "EMPTY"}</Typography>
<ExpandMoreIcon/>
<Button onClick={handleUserLogoutClick} variant="contained" color="primary">Log out</Button>
</Toolbar>
</AppBar>
{renderMenu}
</div>
);
}
export default LoggedHeader
keycloak.js
import Keycloak from 'keycloak-js'
const keycloakConfig = {
url: 'http://10.192.168.72:8080/auth/',
realm: 'Realm12',
clientId: 'client',
}
const keycloak = new Keycloak(keycloakConfig);
export default keycloak
What I need provide to ReactKeycloakProvider to get new access_token when was expired ?
How based on refreshToken value get accessToken? I don't know which method or endpoint due to get this value. I can't find this kind of problem in network.
Please help me !
You can use event onTokens on Provider
<ReactKeycloakProvider
authClient={keycloak}
onTokens={({ token }) => {
// dispatch(setToken(token));
localStorage.setItem("keycloakToken", token);
}}
<AppRouter/>
</ReactKeycloakProvider>
And to trigger the update method, you can listen the event in your app router like this
export default function AppRouter() {
const { initialized, keycloak } = useKeycloak<KeycloakInstance>();
useEffect(() => {
if (keycloak && initialized) {
keycloak.onTokenExpired = () => keycloak.updateToken(600);
}
return () => {
if (keycloak) keycloak.onTokenExpired = () => {};
};
}, [initialized, keycloak]);
return (
<MyPreferedRouter>
<Switch />
</MyPreferedRouter>
);
}
Is working on #react-keycloak/ssr and i used this implementation with redux to have the token in the store
Don't forget to adapt keycloak.updateToken(600);
600 is number of seconds your minValidity
I made some investigation in this point because I couldn't get new token by refresh token, this is what worked with me
I used Keycloak end point:
https://<yourAuthLink>/auth/realms/<relmName>/protocol/openid-connect/token
with headers object
headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
and the body will be like that :
body: "client_id"=<clientId>&"grant_type"="refresh_token"&"refresh_token"=<refreshToken>&"client_secret"=<clientSecret>
this will return response which has access_token which you use as token and refresh_token to use it again before expiration time
it is useful link for this type of endpoint and headers
We use this flow
useEffect(() => {
dispatch(keycloak.token);
// and then save it to localStorage
}, [keycloak.token]);
useEffect(() => {
// jast in case
if(!initialized)
return;
if(!keycloak.authenticated)
return;
keycloak.onTokenExpired = () => {
keycloak.updateToken(50);
};
}, [keycloak.authenticated]);
But here I have a question: if the user sleep for a long time and then need to do some API request, so here I have to ask for refreshed token before request
but useKeycloak hook doesn't work in this case
Related
We link our website in a Tiktok profile similar to https://SKKNBYKIM.COM in this profile https://www.tiktok.com/#kimkardashian. When using the Tiktok mobile app and our website is clicked in a Tiktok profile, sometimes our BeatLoader loading component is displayed indefinitely. However, sometimes the website loads successfully (there doesn't seem to be a pattern).
However, if you visit the Tiktok profile on web browser and click the link to our website in the profile, it always loads. Also, the profile always loads the link when clicked directly through web browser, mobile browser, and from an Instagram profile link.
I suspect when the website doesn't load, it might be due to being unable to authenticate on Firebase since the line {(currUser && !isPullingUser) is never becoming true (verified by changing the BeatLoader component to something else and seeing it was displayed indefinitely as well). Is there any way to determine what the console looks like when the website is opened through the Tiktok app or some other way to diagnose what might be causing the problem?
/* global chrome */
import React, { useEffect } from "react";
import { fade, makeStyles } from "#material-ui/core/styles";
import UserChest from "../../components/chest/userChest";
import Box from "#material-ui/core/Box";
import {
BrowserRouter as Router,
useHistory,
useParams,
} from "react-router-dom";
import { db, firebase } from "../../api/firebase/firebase";
import ProductHeader from "../../components/headers/productHeader";
import { BeatLoader } from "react-spinners";
function Home(props) {
const classes = useStyles();
const [currUser, setCurrUser] = React.useState(null);
const [me, setMe] = React.useState(null);
const [allUsers, setAllUsers] = React.useState([]);
const [isMe, setIsMe] = React.useState(true);
const [isPullingUser, setIsPullingUser] = React.useState(false);
const [isPullingMe, setIsPullingMe] = React.useState(true);
let { username } = useParams();
const history = useHistory();
const handleFirstTimeSignUp = async (user) => {
await db.collection("users").doc(user.email).update({
isFirstSignIn: false,
});
};
const selectUser = () => {
setIsMe(false);
};
/*
getToken creates an https request that automatically authenticates
a user on our chrome extension
*/
const getToken = async (uid) => {
let callToken = firebase.functions().httpsCallable("generateLoginToken")(
uid
);
await callToken
.then((result) => {
// Read result of the Cloud Function.
let token = result.data;
var editorExtensionId = "OUR_CHROME_EXTENSION_EDITOR_ID";
chrome.runtime?.sendMessage(
editorExtensionId,
{ token: token },
function (response) {
if (response?.status === "success") {
console.log("success: auth token sent", response);
} else {
console.log("failure: sending auth token failed", response);
}
}
);
})
.catch((error) => {
console.log(error);
});
};
const getUserbyUsername = async username => {
let user = {};
const snapshot = await db
.collection("users")
.where("username", "==", username)
.get();
snapshot.forEach((doc) => {
user = doc.data();
});
return user;
};
useEffect(() => {
setIsPullingUser(true)
if (username) {
let unsubscribeUser;
(async () => {
let currentUser =
username !== "home"
? await getUserbyUsername(username)
: firebase.auth().currentUser;
if (username === 'home' && !currentUser) {
history.replace("/login")
}
let user = null;
let email = currentUser?.email;
// eslint-disable-next-line no-undef
const queryUser = db.collection("users").doc(email);
unsubscribeUser = queryUser.onSnapshot(
(doc) => {
let userPulled = doc.data();
if (userPulled) {
user = userPulled;
// if(user.username === username || !username)
setCurrUser(user);
setIsPullingUser(false)
}
}, (error) =>
console.log(error)
);
})();
return () => {
unsubscribeUser();
};
} else {
setCurrUser(null);
setIsPullingUser(false)
}
}, [username]);
useEffect(() => {
let currentUser = firebase.auth().currentUser;
if (currentUser && currentUser.uid) {
getToken(currentUser.uid);
let user = null;
let email = currentUser?.email;
// eslint-disable-next-line no-undef
async function getUser(doc) {
let userPulled = doc.data();
if (userPulled) {
user = userPulled;
setMe(user);
setIsPullingUser(false);
setIsPullingMe(false);
if (userPulled.isFirstSignIn) {
handleFirstTimeSignUp(user).then((r) =>
console.log("welcome to company name!")
);
}
} else {
history.push('/username')
}
}
const queryUser = db.collection("users").doc(email)
const unsubscribeUser = queryUser.onSnapshot(getUser, (error) =>
console.log(error)
);
return () => {
console.log('unsubscribe email')
unsubscribeUser();
};
} else {
setIsPullingMe(false);
}
}, []);
return (
<div style={{ minHeight: "100vh" }}>
{(currUser && !isPullingUser) ? (
<div>
<ProductHeader
me={me}
user={currUser}
selectUser={selectUser}
allUsers={allUsers}
tab={"home"}
/>
{ !isPullingMe &&
<UserChest
me={me}
isMe={isMe}
user={currUser}
/>
}
</div>
) :
<Box
style={{ margin: 10 }}
justifyContent={"center"}
alignItems={"center"}
>
<BeatLoader color={"pink"} size={18} margin={2} />
</Box>
}
</div>
);
}
export default Home;
I'm currently building a firebase login system with a verified email screen.
My problem is that I have a reload user button on the verified email screen that updates the user's credentials so that my root directory redirects the user to the AppStack if currentUser.emailVerified === true.
but the reload button isn't being triggered once pressed so that my root directory is still currentUser.emailVerified === false and not redirecting the user to the AppStack.
Login-System/context/AuthContext.js:
import React, { createContext, useContext, useState, useEffect } from 'react';
import { auth } from '../config';
const AuthContext = createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function sendVerification() {
return currentUser.sendEmailVerification();
}
const getUser = () => auth.currentUser;
const reloadUser = () => getUser().reload();
const reload = async () => {
try {
await reloadUser();
const user = getUser();
setCurrentUser(user);
} catch (error) {}
return reload;
};
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user);
setLoading(false);
});
return () => {
unsubscribe();
};
}, []);
const value = {
currentUser,
loading,
reload,
sendVerification,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
Login-System/screens/VerifyEmailScreen.js:
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import { useAuth } from '../contexts/AuthContext';
import { View, Button } from '../components';
import { Colors } from '../config';
export const VerifyEmailScreen = () => {
const { currentUser, reload, sendVerification } = useAuth();
const handleSendVerification = async () => {
try {
await sendVerification();
} catch (error) {}
return handleSendVerification;
};
return (
<>
<View isSafe style={styles.container}>
<View style={styles.center}>
<Text style={styles.screenTitle}>Check your email</Text>
<Text style={styles.screenInfo}>{currentUser.email}</Text>
<Text style={styles.screenInfo}>
We sent you an email with instructions on how to verify your email
address. Click on the link in the email to get started.
</Text>
<Button
style={styles.button}
onPress={() => handleSendVerification()}>
<Text style={styles.buttonText}>Resend</Text>
</Button>
<Button style={styles.button} onPress={reload}>
<Text style={styles.buttonText}>Done</Text>
</Button>
</View>
</View>
</>
);
};
I did some similar works to check if the user email is verified or not you can use this function:
export function getUserEmailVerified() {
const user = firebase.auth().currentUser;
return !!user ? (user.emailVerified ? 'Yes' : 'No') : 'No';
}
To trigger an email verification you can use this method. This method will trigger an email verification and refresh the user.
export function verificationEmail(email, onSuccess) {
refreshUser();
const user = firebase.auth().currentUser;
const finishAction = message => {
onSuccess();
showToast(message);
};
user
.sendEmailVerification()
.then(() => {
refreshUser();
})
.catch(error => finishAction(error.message));
}
And to refresh the user you can use this method.
export function refreshUser() {
let user = firebase.auth().currentUser;
if (!!user && !user?.emailVerified) {
interval = setInterval(() => {
user?.reload().then();
}, 3000);
}
}
You also need to use onUserChanged from firebase to detect whether user information is changed or not. It returns a listener if your user email verified field is changed anyway it will get here is an example.
export function onUserChanged(listener) {
firebase.auth().onUserChanged(listener);
}
I'm trying to make my React web application native using React Native Webview. In order to implement native social oauth, I made a screen that requires users sign in and after the users sign in, a webView with my web application url is rendered:
const App = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
// Check if the app has valid tokens
}, []);
return (
<AuthContext.Provider
value={{
isLoggedIn,
login: () => setIsLoggedIn(true),
logout: () => setIsLoggedIn(false),
}}>
{isLoggedIn ? (
<SafeAreaView style={styles.container}>
<MyWebView />
</SafeAreaView>
) : (
<LoginScreen />
)}
</AuthContext.Provider>
);
};
If users navigate to a page in my domain, my app renders it in MyWebView, otherwise my app opens the os browser with the external url. Also, I want my app to confirm and set isLoggedIn to false so that the app stops rendering MyWebView and goes back to LoginScreen, if users try to navigate to ${BASE_URL}/logout, :
const MyWebView = () => {
const { logout: appLogout } = useContext(AuthContext);
const askForLogout = () => {
const title = 'Logout';
const message = 'Do you want to sign out?';
return new Promise((resolve, reject) => {
Alert.alert(
title,
message,
[
{ text: 'cancel', onPress: () => resolve(false) },
{ text: 'OK', onPress: () => resolve(true) },
],
{ cancelable: false },
);
reject('Error');
});
};
const onLogout = async () => {
try {
const shouldLogout = await askForLogout();
if (!shouldLogout) {
return;
}
await logout(); // This expires tokens
appLogout();
} catch (e) {
alertError();
}
};
return (
<WebView
source={{ uri: BASE_URL }}
sharedCookiesEnabled={true}
startInLoadingState={true}
renderLoading={() => <ActivityIndicator />}
onShouldStartLoadWithRequest={navState => {
const { url } = navState;
if (url.startsWith(BASE_URL)) {
if (url.includes('/logout')) {
onLogout();
return false;
}
return true;
} else {
// External Links
Linking.openURL(url);
return false;
}
}}
/>
);
};
export default MyWebView;
The problem is, onShouldStartLoadWithRequest sometimes (almost always) does not detect navigations using history.push(path) so my app cannot detect whether users navigate to /logout and call the logout function. onNavigationStateChange can detect history.push(path), but with onNavigationStateChange my app cannot stops external links from being rendered in MyWebView (This is bad because some external links use insecure HTTP, causing NSURLErrorDomain)
Any help would be really appreciated. Thank you!
I have an app with redux and redux toolkit.
I try to have a background call to refresh my notifications in the background but every time it is called the whole app gets refreshed.
In my Notification slice I have the following createAsyncThunk:
// Thunk is an async middleware for handling reducers
export const reloadNotifications = createAsyncThunk(
'notifications/reload',
async (userToken: string): Promise<Notification[]> => {
try {
const req = await axios.post(process.env.REACT_APP_GRAPHQL_ENDPOINT as string, {
query: myNotifications()
}, { headers: { "Authorization": `Bearer ${userToken}` } })
// Check data exists before pushing
if (req?.data?.data?.myNotification) {
return req.data.data?.myNotification as Notification[]
} else {
return []
}
} catch (error) {
return []
}
}
)
In my NotificationHeader component I have this:
export const NotificationHeader: React.FC<any> = () => {
const notifications = useSelector(s => s.notifications.notifications);
const [seenNotification] = useMutation(SEEN_NOTIFICATION);
const [location, setLocation] = useLocation();
const dispatch = useDispatch();
const auth = useSelector(s => s.auth);
const handleClick = (notification: Notification) => {
seenNotification({ variables: { notificationID: notification.id } }).then(async ({ data }) => {
setLocation(getUrlNotification(notification, auth.user.id))
}).catch((e: any) => {
setLocation(getUrlNotification(notification, auth.user.id))
})
}
useEffect(() => {
const timer = setTimeout(
() => {
// Only get notifications if i'm logged in
if (auth.isLogged) {
dispatch(reloadNotifications(auth.token))
}
}, 3000);
// This handles componentUnmount to clear the timer
return () => clearTimeout(timer);
});
return (
<Menu placement="bottom-start">
<MenuButton marginRight="10px" variant="secondaryAction" bg="brand.orange" color="brand.purple" as={Button}>
{(notifications && notifications.length > 0 && notifications.length < 10 &&
<SmallBadge content={notifications.length} />
)}
{(notifications && notifications.length > 9 &&
<SmallBadge content="9+" />
)}
<FontAwesomeIcon icon={faBell} />
</MenuButton>
<MenuList commandSpacing="sm" bg="brand.purple" color="brand.orange">
{(notifications && notifications.length > 0) ?
notifications.map(notif => (
<MenuItem key={`notif-${notif.id}`} maxH={20} _focus={{ bg: "brand.orange", color: "brand.purple" }} className="notificationItem">
{(!notif.isSeen) ? <Badge marginRight={2} size="sm" colorScheme="green">NEW</Badge> : undefined}
<Link href="#" onClick={() => handleClick(notif)}>{notif.title}</Link><Text marginLeft={4} marginRight={2} textAlign="right" flex="1" color="brand.gray" as="i" fontSize="xs">{moment(notif.createdAt).format(`DD MMM YYYY`)}</Text>
</MenuItem>
))
: (
<MenuItem isFocusable={false} textAlign="center" maxH={20} _focus={{ bg: "brand.orange", color: "brand.purple" }}>
You have no new notifications
</MenuItem>
)}
</MenuList>
</Menu >
);
}
However with this the interval causes a full refresh of the app even non child components.
I have also tried to add the following middleware to cause the notification interval to be triggered but this caused the full app to crash
export const updateNotificationsMiddleware: Middleware = api => next => action => {
const updateNotifications = async () => {
const { auth } = api.getState() as State;
api.dispatch({
type: 'notifications/reload',
payload: auth.token
});
setTimeout(updateNotifications, 3000);
};
updateNotifications();
return next(action);
};
How can I have a simple API call with redux that will refresh my state every x second without causing a full app refresh.
I have also tried the following from the answer below:
let initialized = false
export const updateNotificationsMiddleware: Middleware = api => next => action => {
const updateNotifications = async () => {
console.log('in middleware')
const { auth } = api.getState() as State;
api.dispatch({
type: 'notifications/reload',
payload: auth.token
});
setTimeout(updateNotifications, 3000);
};
if (!initialized){
initialized = true
updateNotifications();
}
return next(action);
};
I have then updated my thunk to reflect the following:
// Thunk is an async middleware for handling reducers
export const reloadNotifications = createAsyncThunk(
'notifications/reload',
async (userToken: string): Promise<Notification[]> => {
console.log('in action')
try {
const req = await axios.post(process.env.REACT_APP_GRAPHQL_ENDPOINT as string, {
query: myNotifications()
}, { headers: { "Authorization": `Bearer ${userToken}` } })
// Check data exists before pushing
if (req?.data?.data?.myNotification) {
return req.data.data?.myNotification as Notification[]
} else {
return []
}
} catch (error) {
return []
}
}
)
The middleware console.log is indeed shown every 3 second which is awesome but the action is still never called. The console.log does not appear once and the network request also does not get triggered.
Gave your middleware a re-read. You add a timer on every action happening, which probably causes your problem. I think getting it down to doing that only once should solve your problem:
let initialized = false
export const updateNotificationsMiddleware: Middleware = api => next => action => {
const updateNotifications = async () => {
const { auth } = api.getState() as State;
api.dispatch({
type: 'notifications/reload',
payload: auth.token
});
setTimeout(updateNotifications, 3000);
};
if (!initialized){
initialized = true
updateNotifications();
}
return next(action);
};
Consider the following code:
const Home = () => {
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(authUser => {
if(authUser) {
setUser(authUser);
} else {
setUser(null)
}
});
return () => unsubscribe();
}, []);
return (
<div>
{user ? (
<Hero />
) : (
<Login />
)}
</div>
)
}
export default Home
The Login component has all the functions which handles all the Sign Up, Login and Third-Party Authentications using Firebase.
The problems are:
When I reload the page and if the user is already logged in, it shows the component for some time, and then renders the component, which gives a bad UX.
Also, when I sign in using Google or Facebook, again this component is rendered before finally rendering the component.
Please throw some light into this issue. Your help will be highly appreciated!
Edit:
Problem 1 is solved, but problem 2 is not. Here is the relevant code for problem 2:
Login.js
<div style={{ marginBottom: "2%" }}>
<GoogleSignup />
</div>
GoogleSignup.js
import { GoogleLoginButton } from "react-social-login-buttons";
import firebase from "firebase";
import fire from "../fire";
const GoogleSignup = ({ extensionId }) => {
const OnSubmitButton = async () => {
var provider = new firebase.auth.GoogleAuthProvider();
fire
.auth()
.signInWithPopup(provider)
.then((result) => {
const credential = result.credential;
const token = credential.accessToken;
const user = result.user;
})
.catch((error) => {
console.log(error);
});
};
return (
<div>
<GoogleLoginButton
style={{ fontSize: "17px" }}
text={"Continue with Google"}
align={"center"}
onClick={OnSubmitButton}
/>
</div>
);
};
export default GoogleSignup;
These lines:
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(authUser => {
if(authUser) {
setUser(authUser);
} else {
setUser(null)
}
});
return () => unsubscribe();
}, []);
can be replaced with just:
useEffect(() => auth.onAuthStateChanged(setUser), []);
Next, instead of passing in just null to the useState, pass in current user.
const [user, setUser] = useState(null);
becomes
const [user, setUser] = useState(auth.currentUser);
This results in:
const Home = () => {
const [user, setUser] = useState(auth.currentUser);
useEffect(() => auth.onAuthStateChanged(setUser), []);
return (
<div>
{user ? (
<Hero />
) : (
<Login />
)}
</div>
)
}
export default Home
Personally, I tend to use undefined/null/firebase.auth.User using:
const Home = () => {
const [user, setUser] = useState(() => firebase.auth().currentUser || undefined);
const loadingUser = user === undefined;
useEffect(() => firebase.auth().onAuthStateChanged(setUser), []);
if (loadingUser)
return null; // or show loading icon, etc.
return (
<div>
{user ? (
<Hero />
) : (
<Login />
)}
</div>
)
}
export default Home
After the popup has closed, Firebase Authentication still needs to handle the authentication flow of exchanging the provider's authentication token for a Firebase User token. While this is taking place, you should show some form of loading screen in your component. In the below code sample, I change the "Continue with Google" text to "Signing in..." and disable the onClick events for each button while the sign in process takes place.
import { GoogleLoginButton } from "react-social-login-buttons";
import firebase from "firebase";
import fire from "../fire";
const PROVIDER_ID_GOOGLE = firebase.auth.GoogleAuthProvider.PROVIDER_ID;
const ignoreOnClick = () => {};
const GoogleSignup = ({ extensionId }) => {
const [activeSignInMethod, setActiveSignInMethod] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
if (activeSignInMethod === null)
return; // do nothing.
let disposed = false, provider;
switch (activeSignInMethod) {
case PROVIDER_ID_GOOGLE:
provider = new firebase.auth.GoogleAuthProvider();
break;
default:
// this is here to help catch when you've added a button
// but forgot to add the provider as a case above
setError("Unsupported authentication provider");
return;
}
fire.auth()
.signInWithPopup(provider)
.then((result) => {
// const credential = result.credential;
// const token = credential.accessToken;
// const user = result.user;
if (!disposed) {
setError(null);
setActiveSignInMethod(null);
}
})
.catch((error) => {
console.error(`Failed to sign in using ${activeSignInMethod}`, error);
if (!disposed) {
setError("Failed to sign in!");
setActiveSignInMethod(null);
}
});
return () => disposed = true; // <- this is to prevent any "updating destroyed component" errors
}, [activeSignInMethod]);
return (
{ error && (<div key="error">{error}</div>) }
<div key="signin-list">
<GoogleLoginButton
style={{ fontSize: "17px" }}
text={
activeSignInMethod == PROVIDER_ID_GOOGLE
? "Signing in..."
: "Continue with Google"
}
align={"center"}
onClick={
activeSignInMethod === null
? () => setActiveSignInMethod(PROVIDER_ID_GOOGLE)
: ignoreOnClick
}
/>
</div>
);
};
export default GoogleSignup;