存储在 cookie 中的 React apollo 客户端身份验证令牌

问题描述 投票:0回答:0

我是 React 和 Apollo Client 的新手,想开发一个基于令牌的安全认证。后端是用 Laravel 构建的。每次用户登录时,都会发送不记名令牌。我设法将令牌存储在反应 cookie 中。虽然我想实现一些场景:

  1. 我第一次运行该应用程序时,cookie 丢失,用户未经身份验证(无法访问受保护的路由)-它有效
  2. 我第一次运行应用程序时,我在 cookie 中手动添加了错误的令牌,用户未经身份验证(无法访问受保护的路由)- 它有效

如果我使用正确的凭据登录,则会生成一个令牌并将其存储在 cookie 中。现在我可以访问受保护的路由,因为用户已通过身份验证。这里我们有 3 个场景:

  1. 我手动从 cookie 中删除令牌,这样用户就被认为是未经身份验证的——它有效
  2. 我手动更新 cookie 中的 token 值,用户被认为是未经身份验证的 - 它不起作用
  3. 我手动删除 cookie,刷新页面并在 cookie 中添加一个错误的标记用户被认为是未经身份验证的 - 它不起作用

我想缓存有问题,但我不确定。你可以在下面找到我的

authContext.tsx

import React, { createContext, useState, useEffect } from 'react';
import { ME } from '../graphql/authentication/queries';
import { useCookies } from "react-cookie";
import { useApolloClient } from '@apollo/client';

interface AuthContextProps {
    token: string | null,
    loading: boolean,
    isAuthenticated: boolean,
    setAuthenticated: (authenticationToken: string) => void,
    logout: () => void,
}

const TOKEN_NAME = "authToken";

const AuthContext = createContext<AuthContextProps>({
    token: null,
    loading: true,
    isAuthenticated: false,
    setAuthenticated: (authenticationToken: string) => { },
    logout: () => { },
});

interface AuthProviderProps {
    children: React.ReactNode;
}

const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
    const [loading, setLoading] = useState<boolean>(true);
    const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
    const [token, setToken] = useState<string | null>(null);
    const [cookies, setCookie, removeCookie] = useCookies([TOKEN_NAME]);
    const client = useApolloClient();

    useEffect(() => {
        const token = cookies[TOKEN_NAME];

        if (token) {
            const fetchUser = async () => {
                try {
                    const { data } = await client.query({
                        query: ME,
                        fetchPolicy: 'network-only',
                    });

                    const me = data?.me;
                    console.log(me);
                    if (me) {
                        setIsAuthenticated(true);
                        setToken(token);
                        setLoading(false);
                    } else {
                        setIsAuthenticated(false);
                        setLoading(false);
                    }
                } catch (error) {
                    setIsAuthenticated(false);
                    setLoading(false);
                }
            };

            fetchUser();
        } else {
            setLoading(false);
        }
    }, [cookies, client]);

    const setAuthenticated = (authenticationToken: string) => {
        console.log(authenticationToken);
        setCookie(TOKEN_NAME, authenticationToken, {
            path: "/",
            httpOnly: false, // set to true
            sameSite: "strict",
            maxAge: 60 * 60 * 24 * 30
        });
        setIsAuthenticated(true);
        setLoading(true);
    };

    const logout = () => {
        removeCookie(TOKEN_NAME);
        client.clearStore();
        client.resetStore();
        setIsAuthenticated(false);
    };

    return (
        <AuthContext.Provider
            value={{
                token,
                loading,
                isAuthenticated,
                setAuthenticated,
                logout,
            }}
        >
            {children}
        </AuthContext.Provider>
    );
};

export { AuthContext, AuthProvider };

和我的客户

// @ts-nocheck
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
import { getCSRFToken } from '../csrf-token';
import { Cookies } from 'react-cookie';
import { setContext } from '@apollo/client/link/context';

let csrfToken = await getCSRFToken();

const cookies = new Cookies();
let authToken = cookies.get('authToken');
console.log(authToken); // I can see the wrong token here
const authLink = setContext((_, { headers }) => {
    return {
        headers: {
            ...headers,
            authorization: authToken ? `Bearer ${authToken}` : "",
        }
    }
});

const httpLink = new HttpLink({
    uri: '[THE_URL]',
    headers: {
        "X-XSRF-TOKEN": csrfToken,
    },
    credentials: 'include',
});

const cache = new InMemoryCache({});


let client = new ApolloClient({
    link: authLink.concat(httpLink),
    cache: cache,
});

export default client;

我已经尝试在上下文中更新缓存(在

useEffect
中)但是除
network-only
之外的任何其他内容都会导致应用程序将用户重定向到登录页面,尽管cookie中有正确的令牌。

最后,这是我的

index.tsx
档案

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ApolloProvider } from '@apollo/client';
import { BrowserRouter } from 'react-router-dom';
import client from './apollo-client/apollo-client';
import { AuthProvider } from './context/authContext';
import { CookiesProvider } from 'react-cookie';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  <ApolloProvider client={client}>
    <CookiesProvider>
      <BrowserRouter>
        <AuthProvider>
          <React.StrictMode>
            <App />
          </React.StrictMode>
        </AuthProvider>
      </BrowserRouter>
    </CookiesProvider>
  </ApolloProvider>
);

reportWebVitals();

提前谢谢你!如果您想从实施中获得更多背景信息,请告诉我

reactjs cookies apollo-client react-apollo
© www.soinside.com 2019 - 2024. All rights reserved.