How to send JWT header token and client data in POST request using axios - express

I want to send a JWT token to express server with axios POST method.
What I have tried is:
let data = data
let head = {header: { Token: localStorage.getItem("token") }}
axios
.post("http://localhost:3003/api/v/helllo", data, head)
.then((result) => {
console.table(result);
})
.catch((err) => {
console.error(err);
});

Usually, when working with JWT - Authorization header is used. Also pay attention that instead of header - headers field should be used:
let head = {
headers: {
Authorization: 'Bearer ' + localStorage.getItem("token")
}
};
Beware, that storing tokens in local storage is not secure.

Related

Authenticating Sveltekit with JWT API using cookies

I'm trying to authenticate my Sveltekit front-end with JWT using an HTTPonly cookie for security reasons, but it's not working.
Error: "Authentication credentials were not provided."
I can't see the cookie in the storage after login.
My Login code:
<script>
import { goto } from '$app/navigation';
let username = '';
let password = '';
const submit = async () => {
await fetch('https://myAPI/auth/jwt/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
username,
password
})
});
goto('/auth/me');
};
</script>
I must say that the user registration is working fine.
<script>
import { goto } from '$app/navigation';
let username = '';
let password = '';
let email = '';
let first_name = '';
let last_name = '';
const submitForm = async () => {
await fetch('https://myAPi/auth/users/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username,
password,
email,
first_name,
last_name
})
});
goto('/');
};
</script>
I believe I now have enough elements to provide a more accurate answer.
Your API returns a JWT access token upon successful login, but does not set any cookie containing that token. In fact, your API is not reliant on cookies at all since the protected route does not expect a cookie containing the JWT token, but instead an Authorization header containing the token.
This is why I was so insistant on you providing a detailed implementation of your back-end.
In the tutorial you followed and linked in your comment, the author explicitly declares his intent to use cookies to authenticate. This choice is reflected on the front-end (through the use of the credentials: include option in fetch) but also on the back-end, as demonstrated, for example, in the Laravel implementation of his API (line 35), or in its Node implementation (lines 40-43). In both cases, you can see how a 'jwt' cookie is explicitly set and returned by the back-end.
The author also explicitly uses the cookie to read back and verify the token when a request is made to a protected route (see lines 52-54 in the Node example above, for instance).
Your API, however, as I have stated above, does not rely on the same mechanism, but instead expects an 'Authorization' request header to be set.
So you have 2 options here. The simpler option is to adapt your client-side code to function with the Auth mechanism provided by your API. This means storing your token in, for example, sessionStorage, and correctly setting the Authorization header when making requests to protected endpoints:
// login.svelte
<script>
import { goto } from '$app/navigation';
let username = '';
let password = '';
const submit = async () => {
const result = await fetch('https://myAPI/auth/jwt/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username,
password
})
});
const data = await result.json();
const { refresh, access } = data;
sessionStorage.setItem('token', access);
goto('/auth/me');
};
</script>
// auth/me.svelte
<script>
import { onMount } from 'svelte';
onMount(async () => {
// read token from sessionStorage
const token = sessionStorage.getItem('token');
const result = await fetch('https://myAPI/auth/users/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `JWT ${token}`
}
});
const data = await result.json();
console.log(data);
});
</script>
The alternative option is to modify the Auth mechanism in your API from an 'Authorization' header based mechanism to a cookie based mechanism, if this is really what you want, but this would impact other existing services relying on your API, if any.

Problem setting API client authorization key in axios

The API is developed using Laravel, I am currently implementing authorization logic using Laravel Passport. the client application is a Vuejs application, Http calls are done using axios.
Passport is perfectly returning a token (i'm using client credentials type of grants). axios offers a way to set default headers by setting axios.defaults.headers.common array. Here is my axios call (implemented in bootstrap.js)
async function a() {
var ret = "";
await axios
.post("/oauth/token", {
"client_id": 7,
"client_secret": "2GmvfxQev7AnUyfq0Srz4jJaMQyWSt1iVZtukRR6",
"grant_type": "client_credentials",
"scope": "*"
})
.then((resp) => {
ret = resp.data.access_token;
})
return ret;
}
a().then((res) => {
console.log(res) //this perfectly loggs the token to the console.
axios.defaults.headers.common["Authorization"] = "Bearer " + res
})
However, all subsequent axios calls are missing the Bearer token header.
You may try to create an axios instance with custom config:
https://github.com/axios/axios#creating-an-instance
Example:
const axios = require('axios').create({
headers: {
'Authorization': 'Bearer: ' + token
}
});
and use it just like you would normally do:
axios.get(url).then(resp => {
//response handler
});
axios.post(url, data).then(resp => {
//response handler
});

Accessing the response from one GET request within another

I'm working with Vue to interact with an external API on a Drupal website, but in order to do so dynamically per Drupal user, I need to get a token from Drupal first. To do so, I'm trying to do two GET requests. The first gets me a bearer token out of Drupal, and the second uses it to authenticate the third-party API request.
Below is what I'm trying – I'm able to get the token successfully in the first request's response, but not when I try to use it in the header of my second request. If I try hardcoding the token that I see in the console log, it does work, so I know none of that is the issue. It's just that this.jwt['data']['token'] in the second request's headers seems to not pull back the token.
What do I need to adjust in order to access the token from the first response as part of the headers of my second request?
created() {
axios
.get('/jwt/token')
.then(response => {
this.jwt = response
console.log(this.jwt['data']['token']) // this does show what I want to access later
})
},
mounted() {
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: "Bearer " + this.jwt['data']['token'] } // ...but this doesn't work
})
.then(response => {
this.comments = response
})
},
It's likely the response to the token request has not finished by the time the component mounts, at which point this.jwt is not yet assigned.
I would move the token request into the mounted hook, fetching comments only when the token request succeeds:
export default {
mounted() {
axios
.get('/jwt/token')
.then(tokenResp => {
this.jwt = tokenResp
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: 'Bearer ' + this.jwt['data']['token'] }
})
.then(commentsResp => {
this.comments = commentsResp
})
})
}
}

vue-resource interceptor for auth headers

I am trying to set up a Vuejs fronted application (vue-cli webpack template) to sit on top of my Laravel API.
I am able to get a successful response from the API with vue-resource by providing the correct auth token, for example:
methods: {
getUser () {
this.$http.get('http://localhost:8000/api/user',
{
headers: {
'Authorization': 'Bearer eyJ0e.....etc',
'Accept': 'application/json'
}
}).then((response) => {
this.name = response.data.name
});
},
However, I am now trying to set up interceptors so that the user's auth token will automatically be added for each request.
Based on the vue-resource readme I am trying this in my main.js:
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.headers['Authorization'] = 'Bearer eyJ0e.....etc'
request.headers['Accept'] = 'application/json'
next()
})
And then back in my component I now just have:
this.$http.get('http://localhost:8000/api/user').then((response) => {
this.name = response.data.name
});
Problem:
When I specify the headers in the get itself, I get a successful response, but when I pass them through the interceptor I get back a 401 Unauthorized from the server. How can I fix this to respond successfully while using the interceptor?
Edit:
When I use dev-tools to view the outgoing requests I see the following behavior:
When making the request by supplying the headers to $http.get, I make a successful OPTIONS request and then a successful GET request with the Authentication header being supplied to the GET request.
However, when I remove the headers from the $http.get directly and move them to the interceptor, I only make a GET request and the GET does not contain the Authentication header, thus it comes back as a 401 Unauthorized.
It turns out my problem was the syntax for which I was setting the headers in the interceptor.
It should be like this:
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.headers.set('Authorization', 'Bearer eyJ0e.....etc')
request.headers.set('Accept', 'application/json')
next()
})
While I was doing this:
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.headers['Authorization'] = 'Bearer eyJ0e.....etc'
request.headers['Accept'] = 'application/json'
next()
})
Add this option:
Vue.http.options.credentials = true;
And use the interceptors for global way:
Vue.http.interceptors.push(function(request, next) {
request.headers['Authorization'] = 'Basic abc' //Base64
request.headers['Accept'] = 'application/json'
next()
});

httponly cookie not available in subsequent xhr requests

Background
I have a restful backend, React + Redux frontend and I'm trying to protect against CSRF and XSS attacks.
The frontend requests a CSRF token from the API. The API response sets the CSRF token in a HttpOnly cookie and also in the response body. The redux reducer saves the token (from the response body) to the redux store.
If I request the token in the main container's componentDidMount(), everything works, but the concern is this is a one shot. Instead, as the requests to the API go through a custom middleware, I would prefer the middleware to request the CSRF token if it doesn't exist locally.
Issue
The flow is as follows (Tested on Chrome 50 and Firefox 47):
CSRF token requested. Token stored in HttpOnly cookie and redux store
Original API call requested with X-CSRF-Token header set. cookie not sent
Receive 403 from API due to missing cookie. API responds with new HttpOnly cookie. The Javascript can't see this cookie, so the redux store is not updated.
Additional API calls requested with X-CSRF-Token header from step 2. and cookie from step 3.
Receive 403 due to mismatched cookie vs X-CSRF-Token
If I add a delay before step 2 with window.setTimeout, the cookie is still not sent, so I don't think it's a race condition with the browser not having enough time to save the cookie?
Action Creator
const login = (credentials) => {
return {
type: AUTH_LOGIN,
payload: {
api: {
method: 'POST',
url: api.v1.auth.login,
data: credentials
}
}
};
};
Middleware
/**
* Ensure the crumb and JWT authentication token are wrapped in all requests to the API.
*/
export default (store) => (next) => (action) => {
if (action.payload && action.payload.api) {
store.dispatch({ type: `${action.type}_${PENDING}` });
return ensureCrumb(store)
.then((crumb) => {
const state = store.getState();
const requestConfig = {
...action.payload.api,
withCredentials: true,
xsrfCookieName: 'crumb',
xsrfHeaderName: 'X-CSRF-Token',
headers: {
'X-CSRF-Token': crumb
}
};
if (state.auth.token) {
requestConfig.headers = { ...requestConfig.headers, Authorization: `Bearer ${state.auth.token}` };
}
return axios(requestConfig);
})
.then((response) => store.dispatch({ type:`${action.type}_${SUCCESS}`, payload: response.data }))
.catch((response) => store.dispatch({ type: `${action.type}_${FAILURE}`, payload: response.data }));
}
return next(action);
};
/**
* Return the crumb if it exists, otherwise requests a crumb
* #param store - The current redux store
* #returns Promise - crumb token
*/
const ensureCrumb = (store) => {
const state = store.getState();
return new Promise((resolve, reject) => {
if (state.crumb.token) {
return resolve(state.crumb.token);
}
store.dispatch({ type: CRUMB_PENDING });
axios.get(api.v1.crumb)
.then((response) => {
store.dispatch({ type: CRUMB_SUCCESS, payload: { token: response.data.crumb } });
window.setTimeout(() => resolve(response.data.crumb), 10000);
// return resolve(response.data.crumb);
})
.catch((error) => {
store.dispatch({ type: CRUMB_FAILURE });
return reject(error);
});
});
};
This was caused because I was creating a new axios client on each request, if I reuse the same axios client for all API requests, the cookie is saved correctly and used in subsequent requests.