2020-05-25 23:38:52 +00:00
|
|
|
import { DEMO_MODE } from "@saleor/config";
|
2019-10-25 12:35:48 +00:00
|
|
|
import useNotifier from "@saleor/hooks/useNotifier";
|
2020-05-25 23:38:52 +00:00
|
|
|
import { 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-05-25 23:38:52 +00:00
|
|
|
import React from "react";
|
2019-09-03 13:42:15 +00:00
|
|
|
import { MutationFunction, MutationResult } from "react-apollo";
|
2020-05-25 23:38:52 +00:00
|
|
|
import { useIntl } from "react-intl";
|
2020-05-14 09:30:32 +00:00
|
|
|
|
|
|
|
import { UserContext } from "./";
|
2020-05-07 11:04:15 +00:00
|
|
|
import {
|
2020-05-14 09:30:32 +00:00
|
|
|
TokenRefreshMutation,
|
2020-05-07 11:04:15 +00:00
|
|
|
TypedTokenAuthMutation,
|
2020-05-14 09:30:32 +00:00
|
|
|
TypedVerifyTokenMutation
|
2020-05-07 11:04:15 +00:00
|
|
|
} from "./mutations";
|
2020-05-14 09:30:32 +00:00
|
|
|
import { RefreshToken, RefreshTokenVariables } from "./types/RefreshToken";
|
2019-06-19 14:40:52 +00:00
|
|
|
import { TokenAuth, TokenAuthVariables } from "./types/TokenAuth";
|
|
|
|
import { User } from "./types/User";
|
|
|
|
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
|
|
|
|
|
|
|
interface AuthProviderOperationsProps {
|
2019-09-02 19:23:37 +00:00
|
|
|
children: (props: {
|
|
|
|
hasToken: boolean;
|
|
|
|
isAuthenticated: boolean;
|
|
|
|
tokenAuthLoading: boolean;
|
|
|
|
tokenVerifyLoading: boolean;
|
|
|
|
user: User;
|
|
|
|
}) => React.ReactNode;
|
2019-06-19 14:40:52 +00:00
|
|
|
}
|
2019-11-07 11:34:54 +00:00
|
|
|
const AuthProviderOperations: React.FC<AuthProviderOperationsProps> = ({
|
|
|
|
children
|
2019-10-25 12:35:48 +00:00
|
|
|
}) => {
|
|
|
|
const intl = useIntl();
|
|
|
|
const notify = useNotifier();
|
|
|
|
|
2020-05-25 23:38:52 +00:00
|
|
|
const handleLogin = () => {
|
|
|
|
if (DEMO_MODE) {
|
|
|
|
displayDemoMessage(intl, notify);
|
|
|
|
}
|
|
|
|
};
|
2019-10-25 12:35:48 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<TypedTokenAuthMutation>
|
|
|
|
{(...tokenAuth) => (
|
|
|
|
<TypedVerifyTokenMutation>
|
|
|
|
{(...tokenVerify) => (
|
|
|
|
<TokenRefreshMutation>
|
|
|
|
{(...tokenRefresh) => (
|
|
|
|
<AuthProvider
|
|
|
|
tokenAuth={tokenAuth}
|
|
|
|
tokenVerify={tokenVerify}
|
|
|
|
tokenRefresh={tokenRefresh}
|
|
|
|
onLogin={handleLogin}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</AuthProvider>
|
|
|
|
)}
|
|
|
|
</TokenRefreshMutation>
|
|
|
|
)}
|
|
|
|
</TypedVerifyTokenMutation>
|
|
|
|
)}
|
|
|
|
</TypedTokenAuthMutation>
|
|
|
|
);
|
|
|
|
};
|
2019-06-19 14:40:52 +00:00
|
|
|
|
|
|
|
interface AuthProviderProps {
|
2019-09-02 19:23:37 +00:00
|
|
|
children: (props: {
|
|
|
|
hasToken: boolean;
|
|
|
|
isAuthenticated: boolean;
|
|
|
|
tokenAuthLoading: boolean;
|
|
|
|
tokenVerifyLoading: boolean;
|
|
|
|
user: User;
|
|
|
|
}) => React.ReactNode;
|
2019-09-03 13:42:15 +00:00
|
|
|
tokenAuth: [
|
|
|
|
MutationFunction<TokenAuth, TokenAuthVariables>,
|
|
|
|
MutationResult<TokenAuth>
|
|
|
|
];
|
|
|
|
tokenVerify: [
|
|
|
|
MutationFunction<VerifyToken, VerifyTokenVariables>,
|
|
|
|
MutationResult<VerifyToken>
|
|
|
|
];
|
2020-05-07 11:04:15 +00:00
|
|
|
tokenRefresh: [
|
|
|
|
MutationFunction<RefreshToken, RefreshTokenVariables>,
|
|
|
|
MutationResult<RefreshToken>
|
|
|
|
];
|
2020-05-25 23:38:52 +00:00
|
|
|
onLogin?: () => void;
|
2019-06-19 14:40:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
interface AuthProviderState {
|
|
|
|
user: User;
|
|
|
|
persistToken: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
class AuthProvider extends React.Component<
|
|
|
|
AuthProviderProps,
|
|
|
|
AuthProviderState
|
|
|
|
> {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2019-12-02 10:49:14 +00:00
|
|
|
this.state = { persistToken: false, user: undefined };
|
2019-06-19 14:40:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillReceiveProps(props: AuthProviderProps) {
|
|
|
|
const { tokenAuth, tokenVerify } = props;
|
2019-09-03 13:42:15 +00:00
|
|
|
const tokenAuthOpts = tokenAuth[1];
|
|
|
|
const tokenVerifyOpts = tokenVerify[1];
|
|
|
|
|
|
|
|
if (tokenAuthOpts.error || tokenVerifyOpts.error) {
|
2019-06-19 14:40:52 +00:00
|
|
|
this.logout();
|
|
|
|
}
|
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.
|
|
|
|
this.setState({ user });
|
|
|
|
if (user) {
|
|
|
|
setAuthToken(
|
2019-09-03 13:42:15 +00:00
|
|
|
tokenAuthOpts.data.tokenCreate.token,
|
2019-06-19 14:40:52 +00:00
|
|
|
this.state.persistToken
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
2020-02-13 15:32:55 +00:00
|
|
|
if (maybe(() => tokenVerifyOpts.data.tokenVerify === null)) {
|
2020-02-11 16:36:53 +00:00
|
|
|
this.logout();
|
|
|
|
} else {
|
2020-02-13 15:32:55 +00:00
|
|
|
const user = maybe(() => tokenVerifyOpts.data.tokenVerify.user);
|
|
|
|
if (!!user) {
|
2020-02-11 16:36:53 +00:00
|
|
|
this.setState({ user });
|
|
|
|
}
|
2019-06-19 14:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
const { user } = this.state;
|
|
|
|
const token = getAuthToken();
|
|
|
|
if (!!token && !user) {
|
2019-09-03 13:42:15 +00:00
|
|
|
this.verifyToken(token);
|
2019-09-05 14:05:56 +00:00
|
|
|
} else {
|
2019-09-11 14:04:41 +00:00
|
|
|
loginWithCredentialsManagementAPI(this.login);
|
2019-06-19 14:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 14:05:56 +00:00
|
|
|
login = async (email: string, password: string) => {
|
2019-10-25 12:35:48 +00:00
|
|
|
const { tokenAuth, onLogin } = this.props;
|
2019-09-03 13:42:15 +00:00
|
|
|
const [tokenAuthFn] = tokenAuth;
|
|
|
|
|
2019-09-05 14:05:56 +00:00
|
|
|
tokenAuthFn({ 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-05 14:05:56 +00:00
|
|
|
}
|
|
|
|
});
|
2019-09-03 13:42:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
loginByToken = (token: string, user: User) => {
|
|
|
|
this.setState({ user });
|
|
|
|
setAuthToken(token, this.state.persistToken);
|
2019-06-19 14:40:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
logout = () => {
|
|
|
|
this.setState({ user: undefined });
|
2019-09-06 15:30:33 +00:00
|
|
|
if (isCredentialsManagementAPISupported) {
|
2019-09-05 14:05:56 +00:00
|
|
|
navigator.credentials.preventSilentAccess();
|
|
|
|
}
|
2019-06-19 14:40:52 +00:00
|
|
|
removeAuthToken();
|
|
|
|
};
|
|
|
|
|
2019-09-03 13:42:15 +00:00
|
|
|
verifyToken = (token: string) => {
|
|
|
|
const { tokenVerify } = this.props;
|
|
|
|
const [tokenVerifyFn] = tokenVerify;
|
|
|
|
|
|
|
|
return tokenVerifyFn({ variables: { token } });
|
|
|
|
};
|
|
|
|
|
2020-05-07 11:04:15 +00:00
|
|
|
refreshToken = async () => {
|
|
|
|
const { tokenRefresh } = this.props;
|
|
|
|
const [tokenRefreshFn] = tokenRefresh;
|
|
|
|
const token = getAuthToken();
|
|
|
|
|
|
|
|
const refreshData = await tokenRefreshFn({ variables: { token } });
|
|
|
|
|
|
|
|
setAuthToken(refreshData.data.tokenRefresh.token, this.state.persistToken);
|
|
|
|
};
|
|
|
|
|
2019-06-19 14:40:52 +00:00
|
|
|
render() {
|
|
|
|
const { children, tokenAuth, tokenVerify } = this.props;
|
2019-09-03 13:42:15 +00:00
|
|
|
const tokenAuthOpts = tokenAuth[1];
|
|
|
|
const tokenVerifyOpts = tokenVerify[1];
|
2019-06-19 14:40:52 +00:00
|
|
|
const { user } = this.state;
|
|
|
|
const isAuthenticated = !!user;
|
2019-09-02 19:23:37 +00:00
|
|
|
|
2019-06-19 14:40:52 +00:00
|
|
|
return (
|
|
|
|
<UserContext.Provider
|
2019-09-02 19:23:37 +00:00
|
|
|
value={{
|
|
|
|
login: this.login,
|
2019-09-03 13:42:15 +00:00
|
|
|
loginByToken: this.loginByToken,
|
2019-09-02 19:23:37 +00:00
|
|
|
logout: this.logout,
|
2019-09-03 13:42:15 +00:00
|
|
|
tokenAuthLoading: tokenAuthOpts.loading,
|
2020-05-07 11:04:15 +00:00
|
|
|
tokenRefresh: this.refreshToken,
|
2019-09-03 13:42:15 +00:00
|
|
|
tokenVerifyLoading: tokenVerifyOpts.loading,
|
2019-09-02 19:23:37 +00:00
|
|
|
user
|
|
|
|
}}
|
2019-06-19 14:40:52 +00:00
|
|
|
>
|
|
|
|
{children({
|
|
|
|
hasToken: !!getAuthToken(),
|
|
|
|
isAuthenticated,
|
2019-09-03 13:42:15 +00:00
|
|
|
tokenAuthLoading: tokenAuthOpts.loading,
|
|
|
|
tokenVerifyLoading: tokenVerifyOpts.loading,
|
2019-06-19 14:40:52 +00:00
|
|
|
user
|
|
|
|
})}
|
|
|
|
</UserContext.Provider>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default AuthProviderOperations;
|