admin管理员组

文章数量:1296290

I am trying to migrate react to react native since I want to make an application next after I made the website, but since some syntaxes of react-native seems to be different than typical react. I am just fairly new to react-native, so I think the problem is with the way react-native handle token. Graphql queries and mutations seems to be working fine but my jwt token and authentication seems to take a hit.

Code


apolloClient.js

import { ApolloClient } from "@apollo/client/core";
import { InMemoryCache } from "@apollo/client/cache";
import { setContext } from "@apollo/client/link/context";
import { createUploadLink } from "apollo-upload-client";
import AsyncStorage from "@react-native-async-storage/async-storage";

const httpLink = createUploadLink({
  uri: "http://localhost:5001/graphql",
});

const authLink = setContext(async () => {
  const token = await AsyncStorage.getItem("jwtToken");
  return {
    headers: {
      Authorization: token ? `Bearer ${token}` : "",
    },
  };
});

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

export default client;

auth.js

import { useReducer, createContext, useEffect } from "react";
import jwtDecode from "jwt-decode";
import AsyncStorage from "@react-native-async-storage/async-storage";

const initialState = {
  user: null,
};

const getToken = async () => {
  try {
    const storedToken = await AsyncStorage.getItem("jwtToken");

    if (storedToken !== null) {
      const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
      const expirationData = decodedToken.exp;
      const currentDate = Date.now / 1000;
      if (expirationData >= currentDate) {
        initialState.user = decodedToken;
      }
    }
  } catch (err) {
    console.log(err);
  }
};

getToken();

const AuthContext = createContext({
  user: null,
  login: (userData) => {},
  logout: () => {},
});

function authReducer(state, action) {
  switch (action.type) {
    case "LOGIN":
      return {
        ...state,
        user: action.payload,
      };
    case "LOGOUT":
      return {
        ...state,
        user: null,
      };
    default:
      return state;
  }
}

function AuthProvider(props) {
  const [state, dispatch] = useReducer(authReducer, initialState);
  //   const router = useRouter();

  function login(userData) {
    AsyncStorage.setItem("jwtToken", userData.token);

    dispatch({
      type: "LOGIN",
      payload: userData,
    });
  }

  function logout() {
    AsyncStorage.removeItem("jwtToken");
    // toast.error("Logged out successfully", shortToastConfig);
    dispatch({ type: "LOGOUT" });
  }

  useEffect(() => {
    if (state.user) {
      const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
      const timeLeft = decodedToken.exp * 1000 - Date.now();
      setTimeout(() => {
        dispatch({
          type: "LOGOUT",
        });
        AsyncStorage.removeItem("jwtToken");
        // toast.error("Session Expired", shortToastConfig);
        // router.push("/login");
      }, timeLeft);
    }
  }, [state]);

  return (
    <AuthContext.Provider
      value={{ user: state.user, login, logout }}
      {...props}
    />
  );
}

export { AuthContext, AuthProvider };

App.js

import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { useFonts } from 'expo-font';
import {
  MyShop,
  SignIn,
  SignUp,
  Navigator,
  Chats,
  ShoppingCart,
  Settings,
  // Country,
  Me,
  Home,
  Categories,
  SubCategories,
  Products,
} from './screens';
import { AuthProvider } from './context/auth';
import client from './context/apolloClient';

export default function App() {
  LogBox.ignoreAllLogs(true);
  const Stack = createStackNavigator();

  return (
    <ApolloProvider client={client}>
      <AuthProvider>
        <NavigationContainer>
          <Stack.Navigator
            screenOptions={{ headerShown: false }}
            initialRouteName="Navigator"
          >
            <Stack.Screen
              name="Navigator"
              ponent={Navigator}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="Home"
              ponent={Home}
              options={{ gestureEnabled: false }}
            />
            {/* <Stack.Screen
            name="Country"
            ponent={Country}
            options={{ gestureEnabled: false }}
          /> */}
            <Stack.Screen
              name="SignIn"
              ponent={SignIn}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="SignUp"
              ponent={SignUp}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="Me"
              ponent={Me}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen name="MyShop" ponent={MyShop} />
            <Stack.Screen name="ShoppingCart" ponent={ShoppingCart} />
            <Stack.Screen name="Chats" ponent={Chats} />
            <Stack.Screen name="Settings" ponent={Settings} />

            {/* Other Pages */}
            <Stack.Screen name="Categories" ponent={Categories} />
            <Stack.Screen name="SubCategories" ponent={SubCategories} />
            <Stack.Screen name="Products" ponent={Products} />
          </Stack.Navigator>
        </NavigationContainer>
      </AuthProvider>
    </ApolloProvider>
  );
}

Error Shown:

If you need more code I can be transparent and edit this post, and if u don't understand what I mean or trying to point out please ment down below if you can, and if you would be kind enough also to give me some few tips and advice on react-native so I could learn more about it. Thank you!!

I am trying to migrate react to react native since I want to make an application next after I made the website, but since some syntaxes of react-native seems to be different than typical react. I am just fairly new to react-native, so I think the problem is with the way react-native handle token. Graphql queries and mutations seems to be working fine but my jwt token and authentication seems to take a hit.

Code


apolloClient.js

import { ApolloClient } from "@apollo/client/core";
import { InMemoryCache } from "@apollo/client/cache";
import { setContext } from "@apollo/client/link/context";
import { createUploadLink } from "apollo-upload-client";
import AsyncStorage from "@react-native-async-storage/async-storage";

const httpLink = createUploadLink({
  uri: "http://localhost:5001/graphql",
});

const authLink = setContext(async () => {
  const token = await AsyncStorage.getItem("jwtToken");
  return {
    headers: {
      Authorization: token ? `Bearer ${token}` : "",
    },
  };
});

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

export default client;

auth.js

import { useReducer, createContext, useEffect } from "react";
import jwtDecode from "jwt-decode";
import AsyncStorage from "@react-native-async-storage/async-storage";

const initialState = {
  user: null,
};

const getToken = async () => {
  try {
    const storedToken = await AsyncStorage.getItem("jwtToken");

    if (storedToken !== null) {
      const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
      const expirationData = decodedToken.exp;
      const currentDate = Date.now / 1000;
      if (expirationData >= currentDate) {
        initialState.user = decodedToken;
      }
    }
  } catch (err) {
    console.log(err);
  }
};

getToken();

const AuthContext = createContext({
  user: null,
  login: (userData) => {},
  logout: () => {},
});

function authReducer(state, action) {
  switch (action.type) {
    case "LOGIN":
      return {
        ...state,
        user: action.payload,
      };
    case "LOGOUT":
      return {
        ...state,
        user: null,
      };
    default:
      return state;
  }
}

function AuthProvider(props) {
  const [state, dispatch] = useReducer(authReducer, initialState);
  //   const router = useRouter();

  function login(userData) {
    AsyncStorage.setItem("jwtToken", userData.token);

    dispatch({
      type: "LOGIN",
      payload: userData,
    });
  }

  function logout() {
    AsyncStorage.removeItem("jwtToken");
    // toast.error("Logged out successfully", shortToastConfig);
    dispatch({ type: "LOGOUT" });
  }

  useEffect(() => {
    if (state.user) {
      const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
      const timeLeft = decodedToken.exp * 1000 - Date.now();
      setTimeout(() => {
        dispatch({
          type: "LOGOUT",
        });
        AsyncStorage.removeItem("jwtToken");
        // toast.error("Session Expired", shortToastConfig);
        // router.push("/login");
      }, timeLeft);
    }
  }, [state]);

  return (
    <AuthContext.Provider
      value={{ user: state.user, login, logout }}
      {...props}
    />
  );
}

export { AuthContext, AuthProvider };

App.js

import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { useFonts } from 'expo-font';
import {
  MyShop,
  SignIn,
  SignUp,
  Navigator,
  Chats,
  ShoppingCart,
  Settings,
  // Country,
  Me,
  Home,
  Categories,
  SubCategories,
  Products,
} from './screens';
import { AuthProvider } from './context/auth';
import client from './context/apolloClient';

export default function App() {
  LogBox.ignoreAllLogs(true);
  const Stack = createStackNavigator();

  return (
    <ApolloProvider client={client}>
      <AuthProvider>
        <NavigationContainer>
          <Stack.Navigator
            screenOptions={{ headerShown: false }}
            initialRouteName="Navigator"
          >
            <Stack.Screen
              name="Navigator"
              ponent={Navigator}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="Home"
              ponent={Home}
              options={{ gestureEnabled: false }}
            />
            {/* <Stack.Screen
            name="Country"
            ponent={Country}
            options={{ gestureEnabled: false }}
          /> */}
            <Stack.Screen
              name="SignIn"
              ponent={SignIn}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="SignUp"
              ponent={SignUp}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen
              name="Me"
              ponent={Me}
              options={{ gestureEnabled: false }}
            />
            <Stack.Screen name="MyShop" ponent={MyShop} />
            <Stack.Screen name="ShoppingCart" ponent={ShoppingCart} />
            <Stack.Screen name="Chats" ponent={Chats} />
            <Stack.Screen name="Settings" ponent={Settings} />

            {/* Other Pages */}
            <Stack.Screen name="Categories" ponent={Categories} />
            <Stack.Screen name="SubCategories" ponent={SubCategories} />
            <Stack.Screen name="Products" ponent={Products} />
          </Stack.Navigator>
        </NavigationContainer>
      </AuthProvider>
    </ApolloProvider>
  );
}

Error Shown:

If you need more code I can be transparent and edit this post, and if u don't understand what I mean or trying to point out please ment down below if you can, and if you would be kind enough also to give me some few tips and advice on react-native so I could learn more about it. Thank you!!

Share Improve this question edited Nov 11, 2022 at 12:26 Mohammad Khan asked Aug 11, 2022 at 17:21 Mohammad KhanMohammad Khan 3631 gold badge4 silver badges21 bronze badges 2
  • 1 Hey Mohammad, can you be a bit more specific about your problem? Describe exactly what doesn't work. How are you sending requests to the app, what response do you get, and what response do you expect? As for the code — the SO munity usually doesn't need more code, but it's important that you share the relevant parts of the code. Too much code is not good either. – Michal Trojanowski Commented Aug 12, 2022 at 6:45
  • This is all 'client' code, but the error is occurring on the server, being seen by the client. it's likely the request never provided the necessary authentication in request itself and that 403 is entirely valid. the entire question is like flying without a plane and asking why the the tree you are about to hit is a maple when you expected an oak forest in this region. its just a react issue, might not even be a server issue, maybe it's just not doing something you assumed, like providing auth – Stof Commented Oct 26, 2022 at 1:30
Add a ment  | 

3 Answers 3

Reset to default 8 +25

AsyncStorage functions that you are using are asynchrounous functions. You will have to wait for them to be resolved.

Usage:

const getData = async () => {
try {
  const value = await AsyncStorage.getItem('@storage_Key')
  if(value !== null) {
    // value previously stored
  }
} catch(e) {
  // error reading value
}

}

// In your case the key is `jwtToken`
const token = await AsyncStorage.getItem("jwtToken");

More info and source here:

https://react-native-async-storage.github.io/async-storage/docs/usage

Try to make your code work with a hardcoded JWT token and then add the async storage functionality.

You need to wait to resolve the Promise when getting something from AsyncStorage.

In AuthProvider change useEffect to

useEffect(() => {
  if (state.user) {
    logout();
  }
}, [state]);

where

const logout = async () => {
  const decodedToken = jwtDecode(await AsyncStorage.getItem("jwtToken"));
  const timeLeft = decodedToken.exp * 1000 - Date.now();
  setTimeout(() => {
    dispatch({
      type: "LOGOUT",
    });
    AsyncStorage.removeItem("jwtToken");
    // toast.error("Session Expired", shortToastConfig);
    // router.push("/login");
  }, timeLeft);
}

If the code in question is the one that you're currently using, then there is still a problem with how you're using asynchronous functions.

E.g. in auth.js:

const storedToken = await AsyncStorage.getItem("jwtToken");

if (storedToken !== null) {
   const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken"));
...
}

You get the stored token in the correct way. But then you call jwtDecode and instead of passing the stored token you pass it an asynchronous function. jwtDecode expects a string. There are at least two places where you do it this way. You should be passing the stored token instead:

const decodedToken = jwtDecode(storedToken);

本文标签: javascriptInvalid Token Specified in React Native (JWT Authentication)Stack Overflow