Quantcast
Channel: Active questions tagged svelte - Stack Overflow
Viewing all articles
Browse latest Browse all 1654

HTTPOnly Cookie not being set in browser localhost

$
0
0

Problem

I have a REST API that has a login endpoint. The login endpoint accepts a username and password, the server responds by sending a HTTPOnly Cookie containing some payload (like JWT).

The approach I always use had been working for a few years until the Set-Cookie header stopped working roughly last week. I have not touched the REST API's source prior to its non-functionality, as I was working on a Svelte-based front-end.

I suspect it has something to do with the Secure attribute being set to false as it is in localhost. However, according to Using HTTP cookies, having an insecure connection should be fine as long as it's localhost. I've been developing REST APIs in this manner for some time now and was surprised to see the cookie no longer being set.

Testing the API with Postman yields the expected result of having the cookie set.

Approaches Used

I tried to recreate the general flow of the real API and stripped it down to its core essentials.

package mainimport ("fmt""io/ioutil""log""net/http""os""os/signal""syscall""time""github.com/gofiber/fiber/v2""github.com/gofiber/fiber/v2/middleware/cors""github.com/golang-jwt/jwt/v4")const idleTimeout = 5 * time.Secondfunc main() {    app := fiber.New(fiber.Config{        IdleTimeout: idleTimeout,    })    app.Use(cors.New(cors.Config{        AllowOrigins:     "*",        AllowHeaders:     "Origin, Content-Type, Accept, Range",        AllowCredentials: true,        AllowMethods:     "GET,POST,HEAD,DELETE,PUT",        ExposeHeaders:    "X-Total-Count, Content-Range",    }))    app.Get("/", hello)    app.Post("/login", login)    go func() {        if err := app.Listen("0.0.0.0:8080"); err != nil {            log.Panic(err)        }    }()    c := make(chan os.Signal, 1)    signal.Notify(c, os.Interrupt, syscall.SIGTERM)    _ = <-c    fmt.Println("\n\nShutting down server...")    _ = app.Shutdown()}func hello(c *fiber.Ctx) error {    return c.SendString("Hello, World!")}func login(c *fiber.Ctx) error {    type LoginInput struct {        Email string `json:"email"`    }    var input LoginInput    if err := c.BodyParser(&input); err != nil {        return c.Status(400).SendString(err.Error())    }    stringUrl := fmt.Sprintf("https://jsonplaceholder.typicode.com/users?email=%s", input.Email)    resp, err := http.Get(stringUrl)    if err != nil {        return c.Status(500).SendString(err.Error())    }    body, err := ioutil.ReadAll(resp.Body)    if err != nil {        return c.Status(500).SendString(err.Error())    }    if len(body) > 0 {        fmt.Println(string(body))    } else {        return c.Status(400).JSON(fiber.Map{"message": "Yeah, we couldn't find that user",        })    }    token := jwt.New(jwt.SigningMethodHS256)    cookie := new(fiber.Cookie)    claims := token.Claims.(jwt.MapClaims)    claims["purpose"] = "Just a test really"    signedToken, err := token.SignedString([]byte("NiceSecret"))    if err != nil {        // Internal Server Error if anything goes wrong in getting the signed token        fmt.Println(err)        return c.SendStatus(500)    }    cookie.Name = "access"    cookie.HTTPOnly = true    cookie.Secure = false    cookie.Domain = "localhost"    cookie.SameSite = "Lax"    cookie.Path = "/"    cookie.Value = signedToken    cookie.Expires = time.Now().Add(time.Hour * 24)    c.Cookie(cookie)    return c.Status(200).JSON(fiber.Map{"message": "You have logged in",    })}

What does this is basically look through JSON Placeholder's Users and if it finds one with a matching email, it sends the HTTPOnly Cookie with some data attached to it.

Seeing as it might be a problem with the library I'm using, I decided to write a Node version with Express.

import axios from 'axios'import express from 'express'import cookieParser from 'cookie-parser'import jwt from 'jsonwebtoken'const app = express()app.use(express.json())app.use(cookieParser())app.use(express.urlencoded({ extended: true }))app.disable('x-powered-by')app.get("/", (req, res) => {    res.send("Hello there!")})app.post("/login", async (req, res, next) => {    try {        const { email } = req.body        const { data } = await axios.get(`https://jsonplaceholder.typicode.com/users?email=${email}`)        if (data) {            if (data.length > 0) {                res.locals.user = data[0]                next()            } else {                return res.status(404).json({                    message: "No results found"                })            }        }    } catch (error) {        return console.error(error)    }}, async (req, res) => {    try {        let { user } = res.locals        const token = jwt.sign({            user: user.name        }, "mega ultra secret sauce 123")        res            .cookie('access',                token,                {                    httpOnly: true,                    secure: false,                    maxAge: 3600                }            )            .status(200)            .json({                message: "You have logged in, check your cookies"            })    } catch (error) {        return console.error(error)    }})app.listen(8000, () => console.log(`Server is up at localhost:8000`))

Both of these do not work on the browsers I've tested them on.

Results

Go responds with this.

HTTP/1.1 200 OKDate: Mon, 21 Feb 2022 05:17:36 GMTContent-Type: application/jsonContent-Length: 32Vary: OriginAccess-Control-Allow-Origin: http://localhost:3000Access-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: X-Total-Count,Content-RangeSet-Cookie: access=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXJwb3NlIjoiSnVzdCBhIHRlc3QgcmVhbGx5In0.8YKepcvnMreP1gUoe_S3S7uYngsLFd9Rrd4Jto-6UPI; expires=Tue, 22 Feb 2022 05:17:36 GMT; domain=localhost; path=/; HttpOnly; SameSite=Lax

For the Node API, this is the response header.

HTTP/1.1 200 OKSet-Cookie: access=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiTGVhbm5lIEdyYWhhbSIsImlhdCI6MTY0NTQyMDM4N30.z1NQcYm5XN-L6Bge_ECsMGFDCgxJi2eNy9sg8GCnhIU; Max-Age=3; Path=/; Expires=Mon, 21 Feb 2022 05:13:11 GMT; HttpOnlyContent-Type: application/json; charset=utf-8Content-Length: 52ETag: W/"34-TsGOkRa49turdlOQSt5gB2H3nxw"Date: Mon, 21 Feb 2022 05:13:07 GMTConnection: keep-aliveKeep-Alive: timeout=5

Client Source

I'm using this as a test form to send and receive data.

<script>    let email = "";    async function handleSubmit() {        try {            let response = await fetch(`http://localhost:8000/login`, {                method: "POST",                body: JSON.stringify({                    email,                }),                headers: {"Content-Type": "application/json",                },            });            if (response) {                console.info(response);                let result = await response.json();                if (result) {                    console.info(result);                }            }        } catch (error) {            alert("Something went wrong. Check your console.");            return console.error(error);        }    }</script><h1>Please Login</h1><svelte:head><title>Just a basic login form</title></svelte:head><form on:submit|preventDefault={handleSubmit}><label for="email">Email:</label><input        type="email"        name="email"        bind:value={email}        placeholder="enter your email"    /></form>

Additional Information

Postman: 9.8.3

Language Versions

Go: 1.17.6

Node.js: v16.13.1

Svelte: 3.44.0

Browsers Used

Mozilla Firefox: 97.0.1

Microsoft Edge: 98.0.1108.56

Chromium: 99.0.4781.0


Viewing all articles
Browse latest Browse all 1654

Trending Articles