Get token from endpoint - react-native

guys, I'm new in RN. I have a default login endpoint smth like this
export const authApi = api.injectEndpoints({
endpoints: build => ({
login: build.mutation({
query: credentials => ({
url: apiEndpoint.login,
method: 'POST',
body: credentials,
}),
}),
}),
});
export const {useLoginMutation} = authApi;
and i want to get auth token from store after login to prepare headers, but i don't know how to
export const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: apiConfig.apiBaseUrl,
prepareHeaders: (headers, {getState}) => {
const token = getState().api;
if (token) {
headers.set('Authorization', token);
}
headers.set('Accept', 'application/json');
return headers;
},
}),
endpoints: () => ({}),
reducerPath: 'api',
});
What is the correct way to make this? And another question if I want to make it persistent what can I use? I read about AsyncStorage but it's not a secure way

Related

how to transfer an object to a header fetchBaseQuery

I'm working with api for movies, and to create a request to the server, I need to pass it to the header object
{
'X-API-KEY': 'there is apikey',
'Content-Type': 'application/json',
}
I make requests using RTK Query
export const filmsApi = createApi({
reducerPath: 'awaitsFilms',
baseQuery: fetchBaseQuery({
baseUrl: url,
}),
endpoints: (build) => ({
getAwaitsFilms: build.query({
query: () => `top?type=TOP_AWAIT_FILMS`,
}),
}),
})
how do I pass this header object to fetchBaseQuery?
Documentation
import type { RootState } from './store'
const baseQuery = fetchBaseQuery({
baseUrl: '/',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token
// If we have a token set in state, let's assume that we should be passing it.
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
},
})

Rtk query always returns cached data. InvalidatesTags not doing anything

Can somebody tell me why I still get the cached data after I invalidate the getUser query?
api.ts:
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: REACT_APP_API_URL,
prepareHeaders: (headers, { getState }) => {
headers.set('Accept', 'application/json');
const token = (getState() as RootState).auth.token;
if (token) {
headers.set('Authorization', token);
}
return headers;
},
}),
tagTypes: [
'UserGet',
'UserPost',
],
endpoints: () => ({}),
});
userGetApi.ts:
const userGetApi = api.injectEndpoints({
endpoints: (builder) => ({
getUserData: builder.query<UserData, void>({
query: () => '/users/me',
providesTags: ['UserGet'],
}),
}),
overrideExisting: true,
});
export const { useGetUserDataQuery } = userGetApi;
userPostApi.ts:
const userPostApi = api.injectEndpoints({
endpoints: (builder) => ({
saveUser: builder.mutation<void, OnboardingEntry>({
query: (userEntries) => {
const formData = Object.keys(userEntries).reduce((formData, key) => {
formData.append(key, userEntries[key].toString());
return formData;
}, new FormData());
return {
url: '/users/update',
method: 'POST',
body: formData,
};
},
invalidatesTags: ['UserGet'],
}),
}),
overrideExisting: true,
});
export const { useSaveUserMutation } = userPostApi;
The 2 hooks I call:
const { data: { data } = {}, isLoading, isError, isSuccess } = useGetUserDataQuery();
const [saveUser, { isLoading: postIsLoading, isSuccess: postIsSuccess }] = useSaveUserMutation();
After calling saveUser(...), I get redirected to another page. When I revisit the page, I expect to see the updated user data from useGetUserDataQuery(), but I see the previous data. Even when I close and reopen the app, I still get the old data!
So what am I doing wrong here? I'm using 'ProvidesTags' & 'InvalidatesTags' as stated in the docs.
So after days of pure frustration I found the solution: Api headers.
baseQuery: fetchBaseQuery({
baseUrl: REACT_APP_API_URL,
prepareHeaders: (headers, { getState }) => {
headers.set('Accept', 'application/json');
headers.set('Cache-Control', 'no-cache');
headers.set('Pragma', 'no-cache');
headers.set('Expires', '0');
const token = (getState() as RootState).auth.token;
if (token) {
headers.set('Authorization', token);
}
return headers;
},
}),
Hopefully this answer will help others as well

Axios interceptor is not getting the current user auth token from vuex store

I'm using Axios to send user input to DRF api and it returns an auth token. I'm saving the token in vuex store. In another component. I'm trying to request another api endpoint with Axios with the latest token in the request headers. The issue I'm having is that Axios will either send the request with no token at all or with the token of the previous user that was logged in. It does not get the current token from the vuex store. I used Axios interceptors hoping that would help but it did not.
Login.vue
<script>
export default {
name: 'Login',
data () {
return{
email: null,
password: null,
token: '',
}
},
props: {
},
methods: {
submitForm () {
this.$store.dispatch('loginUser',{
email: this.email,
password: this.password
}).then(() =>{
this.$router.push({ name: 'List' })
}) .catch(err =>{
console.log(err)
})
},
}
}
</script>
store/index.js
import axios from 'axios'
import { createStore } from 'vuex'
export default createStore({
state: {
token: localStorage.getItem('token'),
},
mutations: {
getToken(state, token) {
localStorage.setItem('token', token)
state.token = token
}
},
actions: {
loginUser({ commit }, data){
axios({
method: 'POST',
url: 'http://localhost:8000/auth/login/',
headers: {'Content-Type': 'application/json'},
data: {
'email': data.email,
'password': data.password,
}
}).then(response =>{
commit('getToken', response.data['key'])
})
}
},
modules: {
}
})
List.vue
<script>
import axios from 'axios'
import store from '/src/store'
export default {
name:'List',
data () {
return {
entry: []
}
},
created() {
axios.interceptors.request.use(function (config){
let token = store.state.token
config.headers['Authorization'] = 'Token ' + token;
return config;
})
axios({
method: 'GET',
url: 'http://localhost:8000/journal/',
headers: {'Content-Type': 'application/json'},
}).then(response =>{
this.entry = response.data
}) .catch(err =>{
console.log(err)
})
}
}
</script>
I thought the point of the interceptor was to get the token before actually making the get request, but it does not seem to be doing that.
Not exactly sure why this works but rewriting my loginUser action like this solves my issue.
actions: {
loginUser({ commit }, data){
return new Promise ((resolve, reject) => {
axios({
method: 'POST',
url: 'http://localhost:8000/auth/login/',
headers: {'Content-Type': 'application/json'},
data: {
'email': data.email,
'password': data.password,
}
}).then(response =>{
commit('getToken', response.data['key'])
resolve()
}).catch(err => {
reject(err)
})
})
}
},
I think it's because return new Promise basically interrupts the the initial promise in Login.vue making sure the client doesn't make an api request without the correct token from the server but I'm not sure.

Permission denied after page refresh in vuejs?

I'm trying to implement authentication in vuejs 3. I'm django and django as a backend and simple jwt for generating token. Every things is working fine. Generated token are in this format.
And here is my auth store , by the way I'm using axios and vuex
import axios from "axios";
import { API_ENDPOINTS } from "../../constants/API";
const authStore = {
state: {
status: "",
access_token: localStorage.getItem("access_token") || "",
refresh_token: localStorage.getItem("refresh_token") || "",
},
mutations: {
auth_request(state) {
state.status = "loading";
},
auth_success(state, access_token, refresh_token, user) {
state.status = "success";
state.access_token = access_token;
state.refresh_token = refresh_token;
state.user = user;
},
auth_error(state) {
state.status = "error";
},
logout(state) {
state.status = "";
state.access_token = "";
state.refresh_token = "";
},
},
actions: {
login({ commit }, user) {
return new Promise((resolve, reject) => {
commit("auth_request");
axios({
url: API_ENDPOINTS.CREATE_TOKEN,
data: user,
method: "POST",
})
.then((resp) => {
console.log(resp);
const access_token = resp.data.access;
const refresh_token = resp.data.refresh;
const user = resp.data.user;
localStorage.setItem("access_token", access_token);
localStorage.setItem("refresh_token", refresh_token);
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${access_token}`;
commit("auth_success", access_token, refresh_token, user);
resolve(resp);
})
.catch((err) => {
commit("auth_error");
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
reject(err);
});
});
},
logout({ commit }) {
// eslint-disable-next-line no-unused-vars
return new Promise((resolve, reject) => {
commit("logout");
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
delete axios.defaults.headers.common["Authorization"];
resolve();
});
},
},
getters: {
isLoggedIn: (state) => !!state.access_token,
authStatus: (state) => state.status,
},
};
export default authStore;
Above code is working fine, only issue is that whenever I refresh my page, I can't able to perform any operation, Like get or post.
Even token is available in localstorage.
Note:-
I think I'm missing some things like refresh token, I think i need to use refresh token, but I've no any idea that how can i use refresh token if refresh token is problem.
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${access_token}`;
This code is noly called when user login, so maybe you should call it when the token is availble at localstorage.

ra-postgraphile: Intercept the error api response

I am using ra-postgraphile as dataprovider for react-admin project and i need to intercept the error api response. Is there any error object or function available for that? if yes, Can I get a documentation.
Also posted an issue in ra-postgraphile repo.
Source Code
const httpLink = createHttpLink({
uri: Config.postgraphileUrl
});
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
'Content-Type': 'application/json'
}
}));
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
useEffect(() => {
(async () => {
const dataProvider = await pgDataProvider(client);
setDataProvider(() => dataProvider);
})();
}, []);