Cvmcosta / ltijs

Turn your application into a fully integratable LTI 1.3 tool provider.
https://cvmcosta.github.io/ltijs/
Apache License 2.0
300 stars 67 forks source link

Invalid session cookies on prod environment #63

Closed GreenOn closed 3 years ago

GreenOn commented 3 years ago

Hi, We are not completely sure if this is a ltijs issue, but we tried everything we can and not able to get this working.

Here's our setup for our prod environment:

// Setup
lti.setup(process.env.LTI_KEY,
  {
    url: 'mongodb+xxxxxxxxx/' + process.env.DB_NAME,
    connection: { user: process.env.DB_USER, pass: process.env.DB_PASS }
  }, {
    staticPath: path.join(__dirname, './public'), // Path to static files
    cookies: {
      secure: true, // Set secure to true if the testing platform is in a different domain and https is being used
      sameSite: '', // Set sameSite to 'None' if the testing platform is in a different domain and https is being used
      domain: '.xyz.com'
    },
    devMode: false // Set DevMode to true if the testing platform is in a different domain and https is not being used
  })

Now, when we do a fetch request for the /info route like this...

const getLTIInfo = async(ltiToken) => {
  const response = await fetch(`https://lti.xyz.com/info?ltik=${ltiToken}`, {
    method: 'GET',
    credentials: "same-origin",
    headers: {
      'Content-Type': 'application/json'
    }
  });
  return response.json();
}

LTIJS is unable to find the session cookies. Any idea on what we are not doing right here? I thought setting the credentials to same-origin should automatically pick up the cookies from the browser from the same origin, but it is not.

Cvmcosta commented 3 years ago

Hello, the issue is probably in the cookie configuration, i see you set the cookie as secure: true but not sameSite: 'None', some browsers will not allow cross domain cookies to be set without both secure: true and sameSite: 'None'.

GreenOn commented 3 years ago

Thanks. Let me switch it and give it a shot.

GreenOn commented 3 years ago

Hmm.. still no luck. I read it somewhere that if the cookie has HTTPonly checked, it is not visible to send it to pass it another browser. Here's the cookie after the changes: https://prnt.sc/va7mzt

Cvmcosta commented 3 years ago

Have you tried changing credentials: "same-origin" to credentials: "include" and add mode: "cors" to your request's options?

When httpOnly is set, javascript cant view the cookies, but it is able to pass them along.

GreenOn commented 3 years ago

That makes sense. Added mode: 'cors' and credentials: 'include'. No luck yet. I may have to explore more headers like cache.

Cvmcosta commented 3 years ago

I assume that if you turn devMode to true everything works well, right? If so, you should probably check the cookies tab in the specific requests in the chrome network inspector, maybe that will give you a better idea of what's not working.

Screenshot from 2020-10-30 13-25-00

GreenOn commented 3 years ago

Yes, if I set devMode to true all good since it bypasses the check.

Cvmcosta commented 3 years ago

If you can't find the issue, i am available tomorrow if you want to have a quick chat on google meet to try and find the source of the problem. Or, if you can, you could create a student account for me on your test LMS just so i can try to inspect the network requests.

GreenOn commented 3 years ago

Thanks a lot. I will spend more time exploring this and let you know. I think the issue may be with the cors issue. These are always tricky to find.

Cvmcosta commented 3 years ago

Okay! Glad to help!

GreenOn commented 3 years ago

Issue could be on the express server receiving this request. Express docs says the response header should set Access-Control-Allow-Credentials to true.
These are the fetch API docs: https://prnt.sc/vaak8v. It is not clear to me if this is taken care of or not. If fetch API doesn't work, I can try a different route. I took the server example repo and built on top.

Cvmcosta commented 3 years ago

Can you check if the cookies are being set in the application inspector of the browser? This way we can identify if the issue is setting the cookies or passing them back. And both the server and client are on the same domain, correct? (Even if in different subdomains)

The Access-Control-Allow-Credentials header is set on ltijs exactly like exemplified in the Express docs. Using the cors plugin.

GreenOn commented 3 years ago

I am able to see the session cookies in the browser cookies. These are correctly set from the launch. Issue is sending them back with the fetch GET request. Yes, all use the same domain with separate subdomains.

Cvmcosta commented 3 years ago

Are the cookies visible on the tabs of both subdomains on the application inspector?
Screenshot from 2020-10-30 16-22-20

GreenOn commented 3 years ago

Yes. Here's the cookies for our ltijs server: https://prnt.sc/vac3p0 And here's the cookies on the Tool that is making the GET request: https://prnt.sc/vac5l0. The LTI session cookie is shared correctly on all the URL's that use the same origin.

Cvmcosta commented 3 years ago

Since everything is being set correctly and both domains have access to the cookie, the issue can only be on the request itself. Can you show me the ltijs logs when the error occurs?

GreenOn commented 3 years ago

Sure. Here it is: https://prnt.sc/vaern6

GreenOn commented 3 years ago

I will try a different API to do the get request and see if it works. I've been trying with fetch. I don't know if it makes a difference but worth a shot.

Cvmcosta commented 3 years ago

Okay, let me know how it goes. I would suggest using ky, that's the library used in the demo client.

GreenOn commented 3 years ago

Somehow ky is not working with the setup we have. I did try passing Cookie: manually as part of the headers and it worked. Ltijs should work with fetch. It's the one of the most dependendable api. My guess there's still something on the express side that is not right.

Cvmcosta commented 3 years ago

For anyone experiencing a similar issue, the problem was that the ltijs route was being called from a backend server without access to the cookies set on the client-side application.