I am trying to create a cookie in svelte (and I am also using svelte kit) and access it. I am want to use the cookie for authentication purposes, more specifically, to store a JWT Token.
I am tried implementing pure JS code, such as getCookie() and setCookie() as shown here W3Schools - JavaScript Cookies. But I can't get access to the document. I have also tried serialize from the cookie npm package, as shown below, and I have also tried using browser as shown below.
import { serialize } from "cookie";
import { browser } from '$app/environment';
You can e.g. set a cookie in a form action. If you want to be able to read it in the browser, you have to disable HttpOnly (in general you should avoid this, as it makes cross site scripting vulnerabilities worse).
A simple example:
<!-- +page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
export let form: { error?: string; } | null;
</script>
<form method="post" use:enhance>
<label>Login <input type="text" name="login" /></label>
<label>Password <input type="password" name="password" /></label>
{#if form?.error}<p>{form.error}</p>{/if}
<button type="submit">Login</button>
</form>
// +page.server.ts
import { invalid, redirect } from '#sveltejs/kit';
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();
const login = formData.get('login');
const password = formData.get('password');
if (login == 'admin' && password == '...') {
cookies.set(
'auth', '42',
{
path: '/',
maxAge: 60 * 60 * 24 * 365,
httpOnly: false, // <-- if you want to read it in the browser
},
);
throw redirect(302, '/');
}
return invalid(400, { error: 'Invalid login or password' });
},
}
The cookie can be read from document.cookie, note that this will throw an error during SSR, so you have to check browser or read it in onMount.
Related
I am trying to implement JWT-based user sessions with SvelteKit, and have mostly been following the explanation for form actions given on their website: https://kit.svelte.dev/docs/form-actions
+page.svelte
<form method="POST" action="?/signIn">
<input type="text" name="name" />
<input type="password" name="password" />
<button type="submit">Submit</button>
</form>
+page.server.svelte
import { fail, redirect } from "#sveltejs/kit";
import { signIn } from "$lib/server/database";
export const actions = {
signIn: async ({ cookies, request }) => {
const data = await request.formData();
const name = data.get("name");
const password = data.get("password");
if (!name || !password) {
return fail(400);
}
try {
cookies.set("jwt", await signIn(name, password));
} catch (error) {
return fail(400);
}
throw redirect(303, "/");
},
};
I have tested my signIn method which I import and use here, and it does return a token when called with the correct credentials. So far, so good. However, I noticed that I don't see any cookies in my developer tools. It seems like the cookies.set() call simply does nothing. I'd like to set the returned JWT as a cookie so that I can authenticate my users, so what am I doing wrong?
In case anybody else has this problem: While the cookie was set as it was supposed to when using Chrome, it wasn't in Safari. I solved this by setting the secure option to false, even though the SvelteKit docs state that this is done automatically on localhost.
I have set up Email.js to make a contact page for a website built with Next.js. It works completely fine when run locally, but does not work when hosted. The form does not even reset when the submit button is clicked. I do this in the sendEmail function. The error handler does not trigger either in the .then block. I get this error in the browser console:
Uncaught The user ID is required. Visit https://dashboard.emailjs.com/admin/integration
Here is how I send the emails:
export default function Book(props) {
const form = useRef();
const [sentMessage, setSentMessage] = useState();
const sendEmail = (e) => {
e.preventDefault();
emailjs
.sendForm(
props.SERVICE_ID,
props.EMAIL_TEMPLATE_ID,
form.current,
props.USER_ID
)
.then(
function (response) {
setSentMessage("Message sent successfully!");
},
function (error) {
setSentMessage("Message failed please email directly.");
}
);
document.getElementById("form").reset();
};
return (
<div className={styles.container}>
<div className={styles.formContainer}>
<form
className={styles.form}
ref={form}
onSubmit={sendEmail}
id="form"
>
<h3>Name (required):</h3>
<input type="text" required={true} name="user_name"></input>
<h3>Email (required):</h3>
<input type="email" required={true} name="user_email"></input>
<h3>Phone number (required):</h3>
<input type="number" required={true} name="phone_number"></input>
<h3>Message (optional):</h3>
<textarea name="message"></textarea>
<button type="submit" value="Send">
Submit
</button>
{sentMessage ? <p>{sentMessage}</p> : <p></p>}
</form>
</div>
</div>
);
}
export async function getServerSideProps() {
return {
props: {
USER_ID: process.env.USER_ID,
EMAIL_TEMPLATE_ID: process.env.EMAIL_TEMPLATE_ID,
SERVICE_ID: process.env.SERVICE_ID,
},
};
}
I have a .env.local file with the template id, user id and service id that all work fine locally. I use next-env and dotenv-load in the next.config.js file like so:
dotenvLoad();
const withNextEnv = nextEnv();
module.exports = withNextEnv({
reactStrictMode: true,
webpack(config) {
config.module.rules.push({
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ["#svgr/webpack"],
});
return config;
},
});
I saw some problems online that people had with Gmail and remote email servers, so I switched the account to have no 2 factor authentication and use less secure apps as well. That had no effect.
All you need to do is set up the environment variables in the next.js dashboard then rebuild the site so they take effect.
So I am writing a frontend project in VueJS. I already have a custom express backend serving an API. It's not necessarily an issue, but when I use axios, the cookies are being passed with ever request; even if I set 'withCredentials: false' and by default.
<template>
<div>
<h1>Login Vue</h1>
<input
type="text"
name="username"
v-model="input.username"
placeholder="Username"
/>
<input
type="password"
name="password"
v-model="input.password"
placeholder="Password"
/>
<button type="button" v-on:click="login()">Login</button>
<button type="button" v-on:click="getMe()">GetMe</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'HelloWorld',
props: {
msg: String,
},
data: function() {
return {
input: {
username: '',
password: '',
},
};
},
methods: {
login: async function() {
const user = await axios.post(
`/api/v1/users/login`,
{
username: this.input.username,
password: this.input.password,
},
{ withCredentials: true }
);
console.log(user);
},
getMe: async function() {
const me = await axios.get(`/api/v1/users/me`, {
withCredentials: false,
});
console.log(me);
},
},
};
</script>
You can see the two async methods; the 'getMe' method will still send cookies to the backend even if set false. The cookie is set from the backend on login, it's a httpOnly cookie with an JSON token in it for backend authentication. Obviously in the real world, I would want to send credentials; but I noticed it was sending cookies by default and when told false.
Is there something I am missing? If I use the Fetch() API and use "credentials: 'omit'" the cookies are not sent to the backend.
This is a brand new clean, VueJS 2 project created from the CLI. The only 'special' thing I am doing it a custom proxy so requests are proxied to the backend.
// vue.config.js;
module.exports = {
devServer: {
proxy: {
'/': {
target: 'http://localhost:3010',
},
},
},
};
If I console.log req.cookies in the backend on the GET request i get:
{
authToken: 'RANDOM JSON TOKEN'
}
withCredentials only applies to cross-origin requests (which have to ask for explicit permission (per the CORS specification) before including cookies).
Try configuring the client upfront explicitly, by setting "axios.defaults.withCredentials = false", instead of when making a call.
Also, i recall hearing that some JS frameworks overwrite this setting anyway, when assembling XMLHttpRequest object.
I am trying to setup an OAuth service from my Node.js back-end to my Vue front-end. Currently, the back-end appears to be working correctly; I am able to authenticate successfully with my google account and then am redirected to the appropriate UI View upon authentication. The cookie is also being stored.
My node service is running on localhost:3000
My Vue UI is running on localhost:8080
I can access the cookie using
document.cookie, and it shows:
"express:sess=<Data here>; express:sess.sig=<Data here>"
The problem is that I am using vue-cookies, and can't retrieve the cookie using window.$cookies.get(...);
In my main.js file I am importing vue-cookies:
import VueCookies from "vue-cookies";
Vue.use(VueCookies);
window.$cookies.config("7D", "", "localhost");
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount("#app");
Here is my router/index.js where I am using that code:
import Vue from "vue";
...
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
debugger;
if (!window.$cookies) {
next({
path: "/login",
params: { nextUrl: to.fullPath }
});
} else {
const cookie = window.$cookies;
if (to.matched.some(record => record.meta.isAdmin)) {
if (cookie.isAdmin) {
next();
} else {
next({
path: "/login",
params: { nextUrl: to.fullPath }
});
}
} else {
next();
}
}
} else {
next();
}
});
If I try to log window.$cookies.get("express:sess"), it returns null. Not sure what I am doing wrong. I know that window.$cookies is defined from the console as well, because I can see all the methods/properties on that object.
If I create a cookie from the developer tools, with a name of "test", I can retrieve it:
> window.$cookies.get("test")
> "value"
EDIT:
If I change the cookie that I manually create to have a name of test:test, the output is null! Does the : character have something to do with why I can't retrieve the cookie??
> window.$cookies.get("test:test")
> null
EDIT 2:
I can do the following and see the cookies are there, but cannot access them for some reason.
window.$cookies.keys()
(3) […]
0: "express:sess"
1: "express:sess.sig"
2: "test"
length: 3
Your problem relies on how vue-cookie manages cookies. if you take a look a the set method you can see that vue-cookie encodes your values before storing the cookie.
Example:
let's say you store a cookie using vue-cookie:
$cookies.set('express:sess')
this will store the name of the cookie as express%3Asess
which is the result of encodeURIComponent('express:sess')
I recommend using a different library or just using native browser API's
I have a
This is my key code:
var that = this
// clear sessionid, csrftoken
that.$Cookies.remove('sessionid');
that.$Cookies.remove('csrftoken');
// login
that.$http.post(Urls.users.login(), params).then((response) => {
setTimeout(loading, 0)
that.$Cookies.set('token', response.data.key);
that.get_user_info()
}).catch((response) => {
debugger
setTimeout(loading, 0)
}
)
in the main.js I configure like this, so the the component I can use the this.$Cookies:
import Cookies from 'js-cookie';
Object.defineProperty(Vue.prototype, "$Cookies", { value: Cookies })
You see I have removed the token and csrftoken, but when I access the login api, there still has the token and csrftoken in the request:
Whether I clear the token and csrftoken in the this.$Cookies it did not clear success essentially.
I am so interested in this question. Why I have deleted some cookie, but when I send a request, the cookie is Still in header?
So I find you use js-cookie, and I go to the github and get this:
Delete a cookie valid to the path of the current page:
Cookies.set('name', 'value', { path: '' });
Cookies.remove('name'); // fail!
Cookies.remove('name', { path: '' }); // removed!
IMPORTANT! When deleting a cookie, you must pass the exact same path and domain attributes that were used to set the cookie, unless you're relying on the default attributes.
Hope can help you~