Cannot set cookies correctly

Sorry that I am new to backend. Please correct me for anything I get wrong.

I originally put the jwt refresh token in local storage, but after I done some research, I think it may be better to put it in the cookies.

So now I would like to store refresh token in cookies. Thus I added the “response.cookie” line to my login function.

 // ... user data get here ...

 let token
 try {
     token = await auth.withRefreshToken().attempt(email, password) 
     console.log('token: '+JSON.stringify(token))
 } catch (e) {
     console.log(e)
     return response.status(401).send({ message:'Incorrect password' })
 }

Object.assign(user, token)
console.log('cookies: '+token.refreshToken)
response.cookie('refresh_token', token.refreshToken, {httpOnly: true})
return response.json(user)

Then I tried to call in postman. I get everything alright in the console log. I also get the user data returned correctly.

Problem 1:
However, when I checked the cookies tab, the “refresh_token” cookie is not the same as the refresh token I get.
e.g. Within the same call, I get a refresh token “e2a2ef00221e63895189469fac1f53d8Picrp10ArCzESVg0ySv7UMR/IuH+3mupEEgBfjHD48UJtsGz8Mj/BleK4SMhwIuz” generated ;
but in “refresh_token” cookies it is “22bda52b9fa72efcbfe90d69604501afV9bfpTNsWoxdnZKqDD8hIURx1FmcglVYA8N5OE7mNLMMclfxtaBa%2B85mVGIDwwLmR5tO%2Fw1RPf4EWqeOqbcarsXFu%2BOSsUHHYZ%2BI5Jxj4SpovyhuknIyfQoxHyg7%2BZPqJskJ3yIZHpjWaLx2IFEvWbQuI2VEaGQ7ZVkU5ALAP2OMIHRGIsUjJ9MO1fplPMzsW2SZ7pPQ%2FVBtmgVaYACpvA%3D%3D”.
(and I have checked that it is also not the same as the decrypted token in database)
So where does the code in cookies come from? How can I fix it?

Problem 2:
If I try to call the api in Chrome, I cannot even get any cookies. (I see nothing under “Cookies” in the “Application” tab in the inspector.)
Do I need to get the cookies in the frontend manually? I am using Reactjs as my frontend app. But since the cookie is set to httpOnly, so shouldn’t it be set automatically after the api is called? What should I do?

Can anyone help me? Thank you.

1 Like

Hi @windy0108 :wave:

Problem 1 :

The response.cookie() method encrypt the cookie.

If you want to get plain token, you need to use response.plainCookie()

Problem 2 :

Your react app and adonis app are on the same domain?

1 Like

Hello, thank you for your reply.

For Problem 1: I see… I’ve tried and it works. Thanks!

And for Problem 2:
My react app is on localhost:3000 and adonis app is on localhost:3333.
So it needs to be on same port?
I tried to add a line “proxy”: “http://localhost:3333 in package.json in React app as stated in the solution here.
It seems that not working as I can’t the cookies though.

I also tried to see if the proxy is working, so after adding proxy I set the ‘origin’ in cors.js to false in Adonis. Then when I send api I get this error:

Access to XMLHttpRequest at 'http://127.0.0.1:3333/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains the invalid value 'false'.
xhr.js:178 POST http://127.0.0.1:3333/login net::ERR_FAILED

I don’t know if I test it correctly, but seems it’s not okay?

Sorry for late answer, lot of work :tired_face:

Cors error

add these lines to your cors.js:

...
module.exports = {
  origin: (origin) => {
    // Allow all connection on dev mode
    if (process.env.NODE_ENV === 'development') {
      return true;
    }

    // Production : allow only from your domain
    if (origin.includes('your.domain')) {
      return true;
    }

    return false;
  },
...

It seems that not working as I can’t the cookies though.

What’s your adonis cookie configuration (domain)?

1 Like

Thank you. Nvm we have some timezone difference LOL

What’s your adonis cookie configuration ( domain )?

Sorry are you talking about the session.js in config folder?

cookie: {
    httpOnly: true,
    sameSite: false,
    path: '/'
  },

It was set as default. And I have just tried to set the path to path: 'http://localhost:3000' or add a line domain:'http://localhost:3000' but seems not working.

1 Like

haha yes probably :laughing:

I try your session.js on one of my projects (nuxt) and it works.

If cookies are not setted, I think the problem is in your react app.
Try fix cors error with connect-src : https://content-security-policy.com/connect-src
and tell me if it’s still not working.

1 Like

I am not quite sure if I have done it correctly.
I just tried to add a meta tag in the html file in the React app:

<meta http-equiv="Content-Security-Policy" 
    content="connect-src 'self' http://127.0.0.1:3333/ ">

Seems nothing changed.
And I find that Adonis also get CSP in its shield.js so I also add something like this:

directives: {
      scriptSrc: ['self', 'http://127.0.0.1:3000']
    },

And seems nothing changed.

I try your session.js on one of my projects (nuxt) and it works.

So with the Adonis default setting it should be working?

Btw I am using axios to send api in React app, will there be something related?
And do I need to receive the cookies “manually” in the front end app? Should the httpOnly cookies be set by the response returned or by me?

1 Like

And I find that Adonis also get CSP in its shield.js

Only use this feature when you use adonis for front-end apps (Edge views)

So with the Adonis default setting it should be working?

Yes

Btw I am using axios to send api in React app, will there be something related?

I also use axios without problems

And do I need to receive the cookies “manually” in the front end app?

Nope. It’s automatically set by browser

Can you share you react & adonis project repo? (here or PM). It’s not easy to help you with only question/answer :blush:

Really sorry for the late reply, got distracted by job.
I have just added you to the github repo named dreamyhometown. Sorry if it makes too sudden because I am not quite sure how should I share it to you… :sweat_smile:

1 Like

thx perfect :smile:
I’ll take a look at it :+1: (probably tomorrow - lot of work)

I think I finally found a solution :tada:

Set axios withCredentials to true & configure your adonis cors.js

Cors.js

module.exports = {
  ...
  credentials: true,
  ...
}

your API/api.js:

export const api = axios.create({
  baseURL: "http://127.0.0.1:3333/",
  responseType: "json",
  withCredentials: true
});

Does that solve your problem?

1 Like

Sorry for late reply again and thank you for you help. I have just tidy up a bit and pushed the changes to github.

I have tried to add this before and I just try it again, seems not working. Should I find the cookies in the ‘Cookies’ under ‘Application’ tab in the ‘Inspector’?

If you don’t mind, could you please try to run and see if it works on your side?

  1. click ‘登入’ on the top right corner
  2. enter ‘t@t.com’ as email and ‘Test123456’ as password
  3. click the ‘登入’ button beneath

This is the login method and that should set the cookies in my thought. I have tested the api using Postman and it should work fine, but I can’t see the cookies in my chrome browser.

Sorry for late reply again and thank you for you help. I have just tidy up a bit and pushed the changes to github.

No problem, you’re welcome :slight_smile: . I will check this :+1:

I have tried to add this before and I just try it again, seems not working. Should I find the cookies in the ‘Cookies’ under ‘Application’ tab in the ‘Inspector’?

Yep

This is the login method and that should set the cookies in my thought. I have tested the api using Postman and it should work fine, but I can’t see the cookies in my chrome browser.

Okay, it’s weird :thinking:. I check your new code tomorrow (lot of work now :confounded:)

Thank you :pray: please just try it when you are free.

Wanna update a bit, I got a warning message when I am developing other part.

A cookie associated with a cross-site resource at http://127.0.0.1/ was set without the SameSiteattribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set withSameSite=NoneandSecure. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.

I am trying to call another api "forget password"and I got this. However this api should not involve any cookies and I did not set cookies in this api. I think it is quite strange. (and neither did I see any cookies in chrome) Is that normal?

…and btw I just think of a problem that login will need some database setup and maybe that’s troublesome for you? Should I make up an api to set cookies for testing?

1 Like

I try your new app and it works perfectly (cookies and login). Try to open react app with 127.0.0.1 not localhost.
When you call adonis api with 127.0.0.1 cookies are setted on 127.0.0.1 not on localhost. It’s important to have the api and react app on the same “domain”.

I am trying to call another api "forget password"and I got this. However this api should not involve any cookies and I did not set cookies in this api. I think it is quite strange.

Every route automatically set cookies (if you set cookies as default driver). To disable globally, remove Adonis/Middleware/Session in globalMiddleware (kernel.js) and manually add the session middleware to all routes that need it.

(and neither did I see any cookies in chrome) Is that normal?

Yep. Cookies are available only on 127.0.0.1

A cookie associated with a cross-site resource at http://127.0.0.1/ was set without the SameSite attribute...

Useful doc : https://web.dev/samesite-cookies-explained/
You can easly configure cookies on your config/session.js.
Example (personal project) :

cookie: {
    httpOnly: true,
    path: '/',
    sameSite: 'strict',
    secure: Env.get('NODE_ENV') === 'production'
  },

…and btw I just think of a problem that login will need some database setup and maybe that’s troublesome for you? Should I make up an api to set cookies for testing?

No it’s okay, thx :blush:

2 Likes

Try to open react app with 127.0.0.1 not localhost .

OMG! That solves the problem! :tada: :tada: :tada:
Understood now. I am so silly :joy: Thank you so much!

Every route automatically set cookies (if you set cookies as default driver). To disable globally, remove Adonis/Middleware/Session in globalMiddleware ( kernel.js ) and manually add the session middleware to all routes that need it.

So if I want to set refresh token in cookies then I should disable it globally, otherwise it’s changing every time.

I updated the kernel.js like this:

const globalMiddleware = [
  'Adonis/Middleware/BodyParser',
  /*'Adonis/Middleware/Session',*/
  'Adonis/Middleware/Shield',
  'Adonis/Middleware/AuthInit',
  'App/Middleware/ConvertEmptyStringsToNull',
]
const namedMiddleware = {
  auth: 'Adonis/Middleware/Auth',
  guest: 'Adonis/Middleware/AllowGuestOnly',
  session: 'Adonis/Middleware/Session'
}

and route.js like this:

Route.post('/login', 'AuthController.login').middleware(['session'])

Then I receive an error when I call login:

"E_RUNTIME_ERROR: Session store is not initiated yet. Make sure that you have included the session middleware inside the list of global middleware.
> More details: https://err.sh/adonisjs/errors/E_RUNTIME_ERROR"

Did I miss anything?

And btw how do you think about storing the jwt access token in react context and refresh token in cookies? Is it an acceptable way to do so? (as a website that does not involve credential data but I don’t want it to be too insecure)

2 Likes

OMG! That solves the problem! :tada: :tada: :tada:
Understood now. I am so silly :joy: Thank you so much!

Yeah! :smiley::tada: You’re welcome :wink:

So if I want to set refresh token in cookies then I should disable it globally, otherwise it’s changing every time.

No you can keep cookies enabled globally. Do you need to get refresh_token on your react app side?
Cookies changes every requests but their content not. Idk why adonis do that but content is always the same (there must be a reason)

Then I receive an error when I call login:

Ohhhh my bad :sweat_smile:, some providers needs session middleware (shield, …). So you must
keep session enable globally

And btw how do you think about storing the jwt access token in react context and refresh token in cookies? Is it an acceptable way to do so? (as a website that does not involve credential data but I don’t want it to be too insecure)

!!! (personal opinion - I dont’ work in security - feel free to correct me)
I think you can only use session authentication (more easy, faster developement).
But yes, why not… It’s secure as long as the cookie httpOnly is true

I don’t know React, this links can be useful :

My philosophy: never trust client (user & front app)

1 Like