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.