Thanks to visit codestin.com
Credit goes to Github.com

Skip to content

Conversation

@czechboy0
Copy link
Contributor

Summary

When using SessionsMiddleware and a custom AsyncSessionAuthenticator together, we were seeing that even in cases when no cookie is provided in the request, and no error was thrown by e.g. a guard middleware or the handler, we were still seeing a set-cookie header in the response, which isn't desired.

This lead to many sessions created in the sessions driver for calls that didn't require authentication, nor did it provide any.

This seemed to be a side effect of the way AsyncSessionAuthenticator checked if the session already contained an authenticated value - by using the getter request.session, a session was created lazily.

The fix is to prepend a check to request.hasSession and only using request.session... if one indeed exists.

Test Plan

A regression test was added which exercises this scenario - and it fails without the fix.

@codecov
Copy link

codecov bot commented Oct 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.02%. Comparing base (f7090db) to head (969aceb).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3372      +/-   ##
==========================================
+ Coverage   76.98%   77.02%   +0.04%     
==========================================
  Files         227      227              
  Lines        8697     8697              
==========================================
+ Hits         6695     6699       +4     
+ Misses       2002     1998       -4     
Files with missing lines Coverage Δ
...Vapor/Concurrency/Authentication+Concurrency.swift 69.44% <100.00%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gwynne
Copy link
Member

gwynne commented Oct 13, 2025

I think this is partially intended behavior, in that request.session ought to always exist, but also incorrect behavior, in that a cookie should not be set if 1) no cookie was provided in the request, and 2) the session is still empty when it's time to send the response. As such, I believe the check belongs in SessionsMiddleware.addCookies(to:for:), which does not currently check whether a new session (session.id is nil) is empty.

@0xTim
Copy link
Member

0xTim commented Oct 13, 2025

Yep agree with @gwynne here, if you have a global sessions middleware you should see a set-cookie on every request. We should add the check there since this is the part doing the actual logic to see whether to set a session

@czechboy0
Copy link
Contributor Author

but also incorrect behavior, in that a cookie should not be set if 1) no cookie was provided in the request, and 2) the session is still empty when it's time to send the response. As such, I believe the check belongs in SessionsMiddleware.addCookies(to:for:), which does not currently check whether a new session (session.id is nil) is empty.

Gotcha, so the check in addCookies should be expanded with:

  1. A check for session.id? (This is already used to decide whether to call updateSession or createSession)
  2. A check for session.data being empty? (Is that right? Or could that be considered a breaking change, since up until now, it was possible to use an empty session to keep a user signed in, in theory)

Happy to update the logic, but what is the desired behavior?

@gwynne
Copy link
Member

gwynne commented Oct 13, 2025

@czechboy0 My take is that when creating a new session (session.id is nil), there must be at least one piece of data stored in the session for a session to actually be created. Once the session already exists, it doesn't matter if it's empty or not. @0xTim Does that make sense to you?

@0xTim
Copy link
Member

0xTim commented Oct 13, 2025

Yeah I think if we have a session cookie, then we should always set the session. If there's no session cookie in the request, then we're in a fresh state to decide whether to set the set-cookie header. So yeah I think session ID is the right way to go

@czechboy0
Copy link
Contributor Author

czechboy0 commented Oct 13, 2025

The problem is that when you have an AsyncSessionAuthenticator in your middleware stack after a SessionsMiddleware, even if no cookie was provided in the request, the AsyncSessionAuthenticator creates an empty session (no explicit ID, no data entries) here (because the request.session is lazy and creates an empty session on access):

if let aID = request.session.authenticated(User.self) {

And then, since the session already exists, the SessionsMiddleware asks the session driver to create the session (because no ID is there, but there is a session). At that point, an ID will be created, but still no data is in there (you can see the empty {} in e.g. the Fluent-driven database). And since there's a session, SessionsMiddleware will set set-cookie.

I'm trying to avoid the session being created in the driver at all.

What specifically do you think should change?

@czechboy0
Copy link
Contributor Author

czechboy0 commented Dec 17, 2025

@0xTim @gwynne gentle ping, I'm still hoping for a fix here, as it's polluting the session store

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants