saleor-dashboard/src/auth/AuthProvider.tsx

166 lines
4.4 KiB
TypeScript
Raw Normal View History

2020-05-25 23:38:52 +00:00
import { DEMO_MODE } from "@saleor/config";
import { User } from "@saleor/fragments/types/User";
2019-10-25 12:35:48 +00:00
import useNotifier from "@saleor/hooks/useNotifier";
import { getMutationStatus, maybe } from "@saleor/misc";
2019-09-06 15:30:33 +00:00
import {
isSupported as isCredentialsManagementAPISupported,
login as loginWithCredentialsManagementAPI,
saveCredentials
} from "@saleor/utils/credentialsManagement";
2020-07-13 15:44:02 +00:00
import React, { useContext, useEffect, useState } from "react";
import { useMutation } from "react-apollo";
2020-05-25 23:38:52 +00:00
import { useIntl } from "react-intl";
import { UserContext } from "./";
2020-05-07 11:04:15 +00:00
import {
tokenAuthMutation,
tokenRefreshMutation,
tokenVerifyMutation
2020-05-07 11:04:15 +00:00
} from "./mutations";
import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken";
import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth";
import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken";
2020-05-25 23:38:52 +00:00
import {
displayDemoMessage,
getAuthToken,
removeAuthToken,
setAuthToken
} from "./utils";
2019-06-19 14:40:52 +00:00
2020-07-13 15:44:02 +00:00
interface AuthProviderProps {
children: React.ReactNode;
2019-06-19 14:40:52 +00:00
}
2020-07-14 08:11:43 +00:00
2020-07-13 15:44:02 +00:00
const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
2019-10-25 12:35:48 +00:00
const intl = useIntl();
const notify = useNotifier();
const [tokenAuth, tokenAuthResult] = useMutation<
TokenAuth,
TokenAuthVariables
>(tokenAuthMutation);
const [tokenRefresh] = useMutation<RefreshToken, RefreshTokenVariables>(
tokenRefreshMutation
);
const [tokenVerify, tokenVerifyResult] = useMutation<
VerifyToken,
VerifyTokenVariables
>(tokenVerifyMutation);
const tokenAuthOpts = {
...tokenAuthResult,
status: getMutationStatus(tokenAuthResult)
};
const tokenVerifyOpts = {
...tokenVerifyResult,
status: getMutationStatus(tokenVerifyResult)
};
2020-07-13 15:44:02 +00:00
const [userContext, setUserContext] = useState<undefined | User>(undefined);
const [persistToken] = useState<boolean>(false);
2020-07-13 15:44:02 +00:00
const onLogin = () => {
2020-05-25 23:38:52 +00:00
if (DEMO_MODE) {
displayDemoMessage(intl, notify);
}
};
2019-10-25 12:35:48 +00:00
2020-07-13 15:44:02 +00:00
useEffect(() => {
2019-09-03 13:42:15 +00:00
if (tokenAuthOpts.error || tokenVerifyOpts.error) {
2020-07-13 15:44:02 +00:00
logout();
2019-06-19 14:40:52 +00:00
}
2019-09-03 13:42:15 +00:00
if (tokenAuthOpts.data) {
const user = tokenAuthOpts.data.tokenCreate.user;
2019-06-19 14:40:52 +00:00
// FIXME: Now we set state also when auth fails and returned user is
// `null`, because the LoginView uses this `null` to display error.
2020-07-13 15:44:02 +00:00
setUserContext(user);
2019-06-19 14:40:52 +00:00
if (user) {
2020-07-13 15:44:02 +00:00
setAuthToken(tokenAuthOpts.data.tokenCreate.token, persistToken);
2019-06-19 14:40:52 +00:00
}
} else {
2020-02-13 15:32:55 +00:00
if (maybe(() => tokenVerifyOpts.data.tokenVerify === null)) {
2020-07-13 15:44:02 +00:00
logout();
2020-02-11 16:36:53 +00:00
} else {
2020-02-13 15:32:55 +00:00
const user = maybe(() => tokenVerifyOpts.data.tokenVerify.user);
if (!!user) {
2020-07-13 15:44:02 +00:00
setUserContext(user);
2020-02-11 16:36:53 +00:00
}
2019-06-19 14:40:52 +00:00
}
}
2020-07-13 15:44:02 +00:00
}, [tokenAuthOpts, tokenVerifyOpts]);
2019-06-19 14:40:52 +00:00
2020-07-15 14:09:12 +00:00
useEffect(() => {
const token = getAuthToken();
if (!!token && !userContext) {
verifyToken(token);
} else {
loginWithCredentialsManagementAPI(login);
}
}, []);
2020-07-13 15:44:02 +00:00
const login = async (email: string, password: string) => {
tokenAuth({ variables: { email, password } }).then(result => {
if (result && !result.data.tokenCreate.errors.length) {
2020-05-25 23:38:52 +00:00
if (!!onLogin) {
onLogin();
}
2019-09-11 14:04:41 +00:00
saveCredentials(result.data.tokenCreate.user, password);
}
});
2019-09-03 13:42:15 +00:00
};
2020-07-13 15:44:02 +00:00
const loginByToken = (token: string, user: User) => {
setUserContext(user);
setAuthToken(token, persistToken);
2019-06-19 14:40:52 +00:00
};
2020-07-13 15:44:02 +00:00
const logout = () => {
setUserContext(undefined);
2019-09-06 15:30:33 +00:00
if (isCredentialsManagementAPISupported) {
navigator.credentials.preventSilentAccess();
}
2019-06-19 14:40:52 +00:00
removeAuthToken();
};
2020-07-13 15:44:02 +00:00
const verifyToken = (token: string) => tokenVerify({ variables: { token } });
2019-09-03 13:42:15 +00:00
2020-07-13 15:44:02 +00:00
const refreshToken = async () => {
2020-05-07 11:04:15 +00:00
const token = getAuthToken();
2020-07-13 15:44:02 +00:00
const refreshData = await tokenRefresh({ variables: { token } });
2020-05-07 11:04:15 +00:00
2020-07-13 15:44:02 +00:00
setAuthToken(refreshData.data.tokenRefresh.token, persistToken);
2020-05-07 11:04:15 +00:00
};
2020-07-13 15:44:02 +00:00
return (
<UserContext.Provider
value={{
login,
loginByToken,
logout,
tokenAuthLoading: tokenAuthOpts.loading,
tokenRefresh: refreshToken,
tokenVerifyLoading: tokenVerifyOpts.loading,
user: userContext
}}
>
{children}
</UserContext.Provider>
);
};
export const useAuth = () => {
const user = useContext(UserContext);
const isAuthenticated = !!user.user;
2020-07-13 15:44:02 +00:00
return {
hasToken: !!getAuthToken(),
isAuthenticated,
tokenAuthLoading: user.tokenAuthLoading,
tokenVerifyLoading: user.tokenVerifyLoading,
2020-07-14 08:11:43 +00:00
user: user.user
2020-07-13 15:44:02 +00:00
};
};
2019-06-19 14:40:52 +00:00
2020-07-13 15:44:02 +00:00
export default AuthProvider;