dio
dio copied to clipboard
Flutter Web: Preflight check doesn't send cookie, creates new session (NodeJS backend)
New Issue Checklist
- [X] I have searched for a similar issue in the project and found none
Issue Info
Info | Value | |
---|---|---|
Platform Name | e.g. Flutter Web | |
Platform Version | e.g. Flutter 2.10.4 / Chrome 102 | |
Dio Version | e.g. 4.06 | |
Android Studio / Xcode Version | Android Studio 2022.1.1 | |
Repro rate | 100% | |
Repro with our demo prj | e.g. does it happen with our demo project? | |
Demo project link | e.g. link to a demo project that highlights the issue |
Issue Description and Steps
Please fill in the detailed description of the issue (full output of any stack trace, compiler error, ...) and the steps to reproduce the issue.
I have posted the same issue to the Flutter issue tracker.
I detail below how I can use an alternative method in the server, so preflight isn't triggering the full request, but then the main-request gives me a different session ID each time. The method I'm demonstrating here shows that the sessions are persisting, and that that Flutter-Web is getting, and sometimes sending the cookie.
Steps to Reproduce
- I've stripped the demo server (Node JS) down to what I think is the bare minimum
import express from "express";
const app: express.Application = module.exports = express(),
http = require("http"),
session = require('express-session');
app.use(session(
{
secret: 'demo',
resave: true,
saveUninitialized: true,
cookie: {
path: '/',
crossDomain: true,
httpOnly: true,
secure: false,
maxAge: 1000000000,
},
rolling: true,
}));
app.use('/', (req, res, next) => {
res.header({
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Origin': req.headers.origin,
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Credentials': 'true'
});
console.log(req.method, req.session.id);
res.end();
});
const server = http.createServer({
rejectUnauthorized: false,
host: '0.0.0.0'
}, app).listen(3000, function () {
console.log(`Demo Server started.`);
});
- I make a request in Dio like this.
Dio dio = Dio();
dio.options.baseUrl = baseURL;
dio.options.connectTimeout = 5000;
dio.options.extra['withCredentials'] = true;
Map<String, String> sendHeaders = {};
sendHeaders['Content-Type'] = 'application/json';
await dio.post(url, options: Options(headers: sendHeaders));
Expected results:
Both the preflight and the request will trigger the app.use('/'), so I expect to see...
Request 1 within same browser-session
OPTIONS [sessionID]
POST [same session ID]
Request 2 within same browser-session
OPTIONS [same sessionID]
POST [same session ID]
Request 3 within same browser-session
OPTIONS [same sessionID]
POST [same session ID]
Actual results:
Request 1 within same browser-session
OPTIONS [sessionID#1]
POST [session ID#2]
Request 2 within same browser-session
OPTIONS [sessionID#3]
POST [same session ID#2]
Request 3 within same browser-session
OPTIONS [sessionID#4]
POST [same session ID#2]
Notes
NodeJS has a utility called CORS which I can use instead of the above app.use, and that stops this duplicate output. I'm deliberately taking advantage of it here.
If I do that, and the browser only sends one request, then that main request does not send the cookie, and each session is different, making maintaining login state impossible.
This is frustrating, because this is easy in Android/IOS/etc. You catch the cookie on the first request and send it with subsequent requests.
![image](https://user-images.githubusercontent.com/42046012/171414854-72fd527b-1fa8-4bd9-a524-e3e8fd3527c4.png)
My temporary solution is this in the server
res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', 'true');
console.log(req.method, req.session.id)
if (req.method == "GET" || req.method == "POST") {
return next();
} else {
req.session.destroy(() => {});
res.end();
}
It seems like this is an issue only present in Debug build. Release build only sends one request (I'm not getting any OPTIONS calls from the above console.log). And all session IDs for the same session are the same.
It still absolutely makes debugging a pain, the steps involved aren't very obvious.