Refactor AuthProvider

This commit is contained in:
Dawid Tarasiuk 2020-07-13 17:44:02 +02:00 committed by dominik-zeglen
parent b633e2f470
commit 03a2fff2a5

View file

@ -7,8 +7,7 @@ import {
login as loginWithCredentialsManagementAPI, login as loginWithCredentialsManagementAPI,
saveCredentials saveCredentials
} from "@saleor/utils/credentialsManagement"; } from "@saleor/utils/credentialsManagement";
import React from "react"; import React, { useContext, useEffect, useState } from "react";
import { MutationFunction, MutationResult } from "react-apollo";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { UserContext } from "./"; import { UserContext } from "./";
@ -17,9 +16,6 @@ import {
useTokenRefreshMutation, useTokenRefreshMutation,
useTokenVerifyMutation useTokenVerifyMutation
} from "./mutations"; } from "./mutations";
import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken";
import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth";
import { VerifyToken, VerifyTokenVariables } from "./types/VerifyToken";
import { import {
displayDemoMessage, displayDemoMessage,
getAuthToken, getAuthToken,
@ -27,126 +23,61 @@ import {
setAuthToken setAuthToken
} from "./utils"; } from "./utils";
interface AuthProviderOperationsProps { interface AuthProviderProps {
children: (props: { children: React.ReactNode;
hasToken: boolean;
isAuthenticated: boolean;
tokenAuthLoading: boolean;
tokenVerifyLoading: boolean;
user: User;
}) => React.ReactNode;
} }
const AuthProviderOperations: React.FC<AuthProviderOperationsProps> = ({ const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
children
}) => {
const intl = useIntl(); const intl = useIntl();
const notify = useNotifier(); const notify = useNotifier();
const tokenAuth = useTokenAuthMutation({}); const [tokenAuth, tokenAuthOpts] = useTokenAuthMutation({});
const tokenRefresh = useTokenRefreshMutation({}); const [tokenRefresh] = useTokenRefreshMutation({});
const tokenVerify = useTokenVerifyMutation({}); const [tokenVerify, tokenVerifyOpts] = useTokenVerifyMutation({});
const handleLogin = () => { const [userContext, setUserContext] = useState<undefined | User>(undefined);
const [persistToken] = useState<boolean>(false);
const onLogin = () => {
if (DEMO_MODE) { if (DEMO_MODE) {
displayDemoMessage(intl, notify); displayDemoMessage(intl, notify);
} }
}; };
return ( useEffect(() => {
<AuthProvider const token = getAuthToken();
tokenAuth={tokenAuth} if (!!token && !userContext) {
tokenVerify={tokenVerify} verifyToken(token);
tokenRefresh={tokenRefresh} } else {
onLogin={handleLogin} loginWithCredentialsManagementAPI(login);
> }
{children} }, []);
</AuthProvider>
);
};
interface AuthProviderProps {
children: (props: {
hasToken: boolean;
isAuthenticated: boolean;
tokenAuthLoading: boolean;
tokenVerifyLoading: boolean;
user: User;
}) => React.ReactNode;
tokenAuth: [
MutationFunction<TokenAuth, TokenAuthVariables>,
MutationResult<TokenAuth>
];
tokenVerify: [
MutationFunction<VerifyToken, VerifyTokenVariables>,
MutationResult<VerifyToken>
];
tokenRefresh: [
MutationFunction<RefreshToken, RefreshTokenVariables>,
MutationResult<RefreshToken>
];
onLogin?: () => void;
}
interface AuthProviderState {
user: User;
persistToken: boolean;
}
class AuthProvider extends React.Component<
AuthProviderProps,
AuthProviderState
> {
constructor(props) {
super(props);
this.state = { persistToken: false, user: undefined };
}
componentWillReceiveProps(props: AuthProviderProps) {
const { tokenAuth, tokenVerify } = props;
const tokenAuthOpts = tokenAuth[1];
const tokenVerifyOpts = tokenVerify[1];
useEffect(() => {
if (tokenAuthOpts.error || tokenVerifyOpts.error) { if (tokenAuthOpts.error || tokenVerifyOpts.error) {
this.logout(); logout();
} }
if (tokenAuthOpts.data) { if (tokenAuthOpts.data) {
const user = tokenAuthOpts.data.tokenCreate.user; const user = tokenAuthOpts.data.tokenCreate.user;
// FIXME: Now we set state also when auth fails and returned user is // FIXME: Now we set state also when auth fails and returned user is
// `null`, because the LoginView uses this `null` to display error. // `null`, because the LoginView uses this `null` to display error.
this.setState({ user }); setUserContext(user);
if (user) { if (user) {
setAuthToken( setAuthToken(tokenAuthOpts.data.tokenCreate.token, persistToken);
tokenAuthOpts.data.tokenCreate.token,
this.state.persistToken
);
} }
} else { } else {
if (maybe(() => tokenVerifyOpts.data.tokenVerify === null)) { if (maybe(() => tokenVerifyOpts.data.tokenVerify === null)) {
this.logout(); logout();
} else { } else {
const user = maybe(() => tokenVerifyOpts.data.tokenVerify.user); const user = maybe(() => tokenVerifyOpts.data.tokenVerify.user);
if (!!user) { if (!!user) {
this.setState({ user }); setUserContext(user);
} }
} }
} }
} }, [tokenAuthOpts, tokenVerifyOpts]);
componentDidMount() { const login = async (email: string, password: string) => {
const { user } = this.state; tokenAuth({ variables: { email, password } }).then(result => {
const token = getAuthToken();
if (!!token && !user) {
this.verifyToken(token);
} else {
loginWithCredentialsManagementAPI(this.login);
}
}
login = async (email: string, password: string) => {
const { tokenAuth, onLogin } = this.props;
const [tokenAuthFn] = tokenAuth;
tokenAuthFn({ variables: { email, password } }).then(result => {
if (result && !result.data.tokenCreate.errors.length) { if (result && !result.data.tokenCreate.errors.length) {
if (!!onLogin) { if (!!onLogin) {
onLogin(); onLogin();
@ -156,65 +87,57 @@ class AuthProvider extends React.Component<
}); });
}; };
loginByToken = (token: string, user: User) => { const loginByToken = (token: string, user: User) => {
this.setState({ user }); setUserContext(user);
setAuthToken(token, this.state.persistToken); setAuthToken(token, persistToken);
}; };
logout = () => { const logout = () => {
this.setState({ user: undefined }); setUserContext(undefined);
if (isCredentialsManagementAPISupported) { if (isCredentialsManagementAPISupported) {
navigator.credentials.preventSilentAccess(); navigator.credentials.preventSilentAccess();
} }
removeAuthToken(); removeAuthToken();
}; };
verifyToken = (token: string) => { const verifyToken = (token: string) => tokenVerify({ variables: { token } });
const { tokenVerify } = this.props;
const [tokenVerifyFn] = tokenVerify;
return tokenVerifyFn({ variables: { token } }); const refreshToken = async () => {
};
refreshToken = async () => {
const { tokenRefresh } = this.props;
const [tokenRefreshFn] = tokenRefresh;
const token = getAuthToken(); const token = getAuthToken();
const refreshData = await tokenRefreshFn({ variables: { token } }); const refreshData = await tokenRefresh({ variables: { token } });
setAuthToken(refreshData.data.tokenRefresh.token, this.state.persistToken); setAuthToken(refreshData.data.tokenRefresh.token, persistToken);
}; };
render() { return (
const { children, tokenAuth, tokenVerify } = this.props; <UserContext.Provider
const tokenAuthOpts = tokenAuth[1]; value={{
const tokenVerifyOpts = tokenVerify[1]; login,
const { user } = this.state; loginByToken,
const isAuthenticated = !!user; logout,
tokenAuthLoading: tokenAuthOpts.loading,
tokenRefresh: refreshToken,
tokenVerifyLoading: tokenVerifyOpts.loading,
user: userContext
}}
>
{children}
</UserContext.Provider>
);
};
return ( export const useAuth = () => {
<UserContext.Provider const user = useContext(UserContext);
value={{ const isAuthenticated = !!user;
login: this.login,
loginByToken: this.loginByToken,
logout: this.logout,
tokenAuthLoading: tokenAuthOpts.loading,
tokenRefresh: this.refreshToken,
tokenVerifyLoading: tokenVerifyOpts.loading,
user
}}
>
{children({
hasToken: !!getAuthToken(),
isAuthenticated,
tokenAuthLoading: tokenAuthOpts.loading,
tokenVerifyLoading: tokenVerifyOpts.loading,
user
})}
</UserContext.Provider>
);
}
}
export default AuthProviderOperations; return {
hasToken: !!getAuthToken(),
isAuthenticated,
tokenAuthLoading: user.tokenAuthLoading,
tokenVerifyLoading: user.tokenVerifyLoading,
user
};
};
export default AuthProvider;