I am looking for way to access cookie in my nuxt fetch. Here is my code;
async fetch() {
const { store, route } = this.$nuxt.context
const { data } = await axios.get(
`${process.env.baseUrl}/user-saved-homes/?ordering=${route.query.ordering}`,
{
headers: {
Authorization: 'Token ' + my_token,
},
}
)
store.commit('ADD_SAVED_HOMES', data.results)
},
I need fetch because I was access to $fetchState.pending. But I have tried looking for a way to access cookie in the nuxt fetch, but I haven't found any. Please, I need help here
Since fetch by default runs on the server, I can't use js-cookies(javascript library), to get the token. So the best way to make this happen is for nuxt fetch to run on the client side.
So adding fetchOnServer: false enabled me to use js-cookies to get the token
so my code is now;
<script>
import axios from 'axios'
import Cookies from 'js-cookies'
export default {
async fetch() {
const { store, route } = this.$nuxt.context
const { data } = await axios.get(
`${process.env.baseUrl}/user-saved-homes/`,
{
headers: {
Authorization: 'Token ' + Cookies.getItem('token'),
},
}
)
store.commit('ADD_SAVED_HOMES', data.results)
},
fetchOnServer: false,
}
Related
I am calling an API defined using RTK Query, within a React Native + Redux Toolkit + Expo app. This is secured with an authentication / authorization system in place i.e. access token (short expiration) and refresh token (longer expiration).
I would like to avoid checking any access token expiration claim (I've seen people suggesting to use a Redux middleware). Rather, if possible, I'd like to trigger the access token renewal when the API being requested returns a 403 response code, i.e. when the access token is expired.
This is the code calling the API:
const SearchResults = () => {
// get the SearchForm fields and pass them as the request body
const { fields, updateField } = useUpdateFields();
// query the RTKQ service
const { data, isLoading, isSuccess, isError, error } =
useGetDataQuery(fields);
return ( ... )
the RTK Query API is defined as follows:
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import * as SecureStore from "expo-secure-store";
import { baseUrl } from "~/env";
export const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({
baseUrl: baseUrl,
prepareHeaders: async (headers, { getState }) => {
// retrieve the access_token from the Expo SecureStore
const access_token = await SecureStore.getItemAsync("access_token");
if (access_token) {
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json");
}
return headers;
},
}),
endpoints: (builder) => ({
getData: builder.query({
// body holds the fields passed during the call
query: (body) => {
return {
url: "/data",
method: "POST",
body: body,
};
},
}),
}),
});
export const { useGetDataQuery } = api;
I understand that when the API returns isError = true and error = something 403 I need to renew the access token within the Expo SecureStore (and there's a function already in place for that). However I have no idea about how can I query the RTKQ API again, on the fly, when it returns a 403 response code, and virtually going unnoticed by the user.
Can someone please point me in the right direction?
I got the hang of it, massive thanks to #phry! I don't know how I could have missed this example from RTKQ docs but I'm a n00b for a reason after all.
This being said, here's how to refactor the RTKQ api to renew the access token on the fly, in case some other react native beginner ever has this problem. Hopefully this is a reasonable way of doing this
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import * as SecureStore from "expo-secure-store";
import { baseUrl } from "~/env";
import { renewAccessToken } from "~/utils/auth";
// fetchBaseQuery logic is unchanged, moved out of createApi for readability
const baseQuery = fetchBaseQuery({
baseUrl: baseUrl,
prepareHeaders: async (headers, { getState }) => {
// retrieve the access_token from the Expo SecureStore
const access_token = await SecureStore.getItemAsync("access_token");
if (access_token) {
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json");
}
return headers;
},
});
const baseQueryWithReauth = async (args, api) => {
let result = await baseQuery(args, api);
if (result.error) {
/* try to get a new token if the main query fails: renewAccessToken replaces
the access token in the SecureStore and returns a response code */
const refreshResult = await renewAccessToken();
if (refreshResult === 200) {
// then, retry the initial query on the fly
result = await baseQuery(args, api);
}
}
return result;
};
export const apiToQuery = createApi({
reducerPath: "apiToQuery",
baseQuery: baseQueryWithReauth,
endpoints: (builder) => ({
getData: builder.query({
// body holds the fields passed during the call
query: (body) => {
return {
url: "/data",
method: "POST",
body: body,
};
},
}),
}),
});
export const { useGetDataQuery } = apiToQuery;
I need to POST to a third party api route to receive an access token to be authorized to fetch data from their endpoints, but I'm not sure how to use the token. They are using Swagger:
export default async function() {
const res = await fetch('url', {
method:'POST',
headers: {
accept: 'application/json'
}
});
const data = await res.json()
console.log(data)
}
I get in response:
{
access_token: 'my token...',
...
}
But I'm not sure how I'd use this response to authorize fetching data. Do I need to pass the token to the headers in the fetch?
export async function getStaticProps() {
const res = await fetch( `url`, {
headers: {
accept: 'application/json',
}
});
const data = await JSON.stringify(res);
return {
props: {
items: data
},
};
}
I can't seem to find much info on this, or I'm just searching for the wrong things. I'm not sure if I'm understanding this correctly
Likely you'll need to send that token in an Authorization header with the request, something like this:
const res = await fetch(`url`, {
headers: {
accept: 'application/json',
Authorization: `Bearer ${token}`,
}
});
const json = await res.json()
const data = JSON.stringify(json);
The API docs should be able to tell you specifics though, if you're still having issues please post the API docs if they're public!
I have a Home.js component that signs the user up to the API and logs in and then gets the token received from the response authorization header and saves it in the state 'token' variable.
This token will be used in all other components to access the API when requests are made, so what is the best way of using this value for all other components?
Home.js:
const SIGNUP_URL = 'http://localhost:8080/users/signup';
const LOGIN_URL = 'http://localhost:8080/login';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated:false,
token: ''
};
}
componentDidMount() {
const payload = {
"username": "hikaru",
"password": "JohnSmith72-"
};
fetch(SIGNUP_URL, {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then((data) => {
console.log(data);
});
fetch(LOGIN_URL, {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response =>
this.setState({token: response.headers.get("Authorization"), isAuthenticated:true})
)
}
For example the userList component which will fetch the user data from the API, but requires the API token stored in the Home component's token state variable to send the request successfully via the authorization header.
Thanks for any help
You can create a custom function called authenticated_request for example. This function could fetch your token from the CookieStorage in case of web or async storage in case of react-native or even if you have it in some state management library. Doesn't matter. Use this function instead of the fetch function and call fetch inside it. Think of it as a higher order function for your network requests.
const authenticated_request(url, config) {
fetch(url, {
...config,
headers: {
...config.headers,
Authorization: getToken()
}
});
}
You can also leverage the usage of something like axios and use request interceptors to intercept requests and responses. Injecting your token as needed.
You should be using AuthContext and localStorage to do this, save the token in the state or localStorage and make a config file which uses the same token when calling an api i have done it in axios. Axios has a concept of interceptors which allows us to attach token to our api calls, Im saving the token in the localStorage after a successfull login and then using the same token from localStorage to add to every call which needs a token, if the api doesnt need a token (some apis can be public) i can use axios directly, check out the below code:
import axios from 'axios';
let apiUrl = '';
let imageUrl = '';
if(process.env.NODE_ENV === 'production'){
apiUrl = `${process.env.REACT_APP_LIVE_URL_basePath}/web/v1/`;
}else{
apiUrl = `http://127.0.0.1:8000/web/v1/`;
}
const config = {
baseURL: apiUrl,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
"Access-Control-Allow-Origin": "https://www.*******.com",
'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE',
},
};
const authAxios = axios.create(config);
authAxios.interceptors.request.use(async function(config) {
config.headers.Authorization = localStorage.getItem('access_token') ?
`Bearer ${localStorage.getItem('access_token')}` :
``;
return config;
});
export { apiUrl, axios, authAxios };
now on making api call u can do something like below:
import { apiUrl, authAxios } from '../config/config'
export async function saveAssignment(data) {
try {
const res = await authAxios.post(apiUrl + 'assignment/save-assignment', data)
return res.data;
}
catch(e){
}
}
here pay attention im not using axios to make api call but using authAxios to make calls(which is exported from the config file) which will have token in the header.
(You can also use a third party library like Redux but the concept remains the same)
You need a centralized state that's what State Management libraries are for. You can use third-party libraries such as Redux, or simply use React's own context. You can search on google for state management in React and you'll find a lot of helpful recourses
You can place the token into a cookie if your app is SSR. To do that, you have to create the following functions:
export const eraseCookie = (name) => {
document.cookie = `${name}=; Max-Age=-99999999;`;
};
export const getCookie = (name) => {
const pairs = document.cookie.split(';');
const pair = pairs.find((cookie) => cookie.split('=')[0].trim() === name);
if (!pair) return '';
return pair.split('=')[1];
};
export const setCookie = (name, value, domain) => {
if (domain) {
document.cookie = `${name}=${value};path=/`;
} else {
document.cookie = `${name}=${value}`;
}
};
You can also place your token into the local storage:
Set into local storage via built-in function:
localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
Get the token via built-in function:
const token = localStorage.getItem('token');
I am trying to use a header token with axios. However I am presented with a CORS error as I am clearly not passing over the token correctly (moving to a not authorized feed works)
Here is my http-common.js file
const token = `08E1B4C220E671AC6A48`
// my user app token from micro.blog 08E1B4C220E671AC6A48
export const HTTP = axios.create({
// baseURL: 'https://micro.blog/feeds/adamprocter.json'
baseURL: 'https://micro.blog',
headers: {
Authorization: `Bearer ${token}`
}
})
and here is my Timeline.vue component
import { HTTP } from '#/http-common'
export default {
components: {
MicroPosts
},
data() {
return {
posts: []
}
},
created() {
// no auth get = HTTP.get('')
HTTP.get('/account/verify')
.then(response => {
//console.log(response.data)
this.posts = response.data.items
})
.catch(error => {
console.log('caught error' + error.response)
})
}
}
The URL is correct but the token is failing (I believe)
POST /account/verify — Accepts an app token (which I have set up) and returns an auth token and other details.
This is the API documentation which is a little sparse but
http://help.micro.blog/2017/api-json/
http://help.micro.blog/2018/api-authentication/
I am sure it is something obvious, any help much appreciated.
The documentation says /account/verify accepts POST. You are sending a GET.
I'm using VueSession in my project. I created a login component and I'm passing data to my backend (Django, returns JWT token). Here is my problem. My login works fine, it returns JWT but when I want to get data from other endpoints I'm getting error 401 (Authentication credentials were not provided). When I'm using curl in my terminal everything works fine.
curl -X POST -d "username=test&password=test" http://localhost:8000/api/token/auth/
it returns token
curl -H "Authorization: JWT <my_token>" http://localhost:8000/protected-url/
and it returns data from website
Here is what I set up in my Vue project.
Login.vue
<script>
import Vue from 'vue'
export default {
name: 'Login',
data () {
return {
username: '',
password: ''
}
},
methods: {
login: function (username, password) {
let user_obj = {
"username": username,
"password": password
}
this.$http.post('http://192.168.1.151:8000/api/token/auth', user_obj)
.then((response) => {
console.log(response.data)
this.$session.start()
this.$session.set('jwt', response.data.token)
Vue.http.headers.common['Authorization'] = 'JWT' + response.data.token
// this.$router.push('/')
})
.catch((error_data) => {
console.log(error_data)
})
}
}
}
</script>
HereIWantUserGETRequest.vue
<script>
export default {
data() {
return {
msg: "Welcome",
my_list: []
}
},
beforeCreate() {
// IF SESSION DOESN'T EXIST
if (!this.$session.exists()) {
this.$router.push('/account/login')
}
},
mounted() {
this.getData()
},
methods: {
getData: function() {
this.$http.get('http://192.168.1.151:8000/api/user/data')
.then((response) => {
console.log(response.data)
this.my_list = response.data
})
.catch((error_data) => {
console.log(error_data)
})
}
}
}
</script>
And of course I set up VueSession and VueResource in main.js
import VueSession from 'vue-session'
import VueResource from 'vue-resource'
Vue.use(VueResource)
Vue.use(VueSession)
Edit
Vue.http.headers.common['Authorization'] = 'JWT' + response.data.token
with
Vue.http.headers.common['Authorization'] = 'JWT ' + response.data.token
Hope it will help you
You don't actually store your jwt token anywhere in your browser (with a cookie or a localStorage). Therefore Vue has the token in memory only for the runtime of that page (in the sense of a one page app) you've request your jwt token in. According to the github docs of VueSession the option to store the token in your browser is false by default. Just set it to true like this:
#main.js
var options = {
persist: true
}
Vue.use(VueSession, options)
I personally don't use this library. I usually do it from scratch using axios, Vuex and localStorage. It's really not that difficult, the pattern is described pretty well here.
The problem was with this line Vue.http.headers.common['Authorization'] = 'JWT ' + response.data.token. To fix it I needed to at this to my main.js file:
if (this.$session.exists()) {
var token = this.$session.get('jwt')
console.log(token)
Vue.http.headers.common['Authorization'] = 'JWT ' + token
}
And now it's working.