This looks like the issue in #10853, which you answered as fixed by [PR #26017](https://github.com/abpframework/abp/pull/26017). We see it on 10.6.1, with one addition: when the client closes the connection right after reading the token response, the session is not just saved late, it is never saved.
Environment
- ABP 10.6.1 (commercial), Blazor Server app with the built-in auth server (
AbpAccountPublicWebOpenIddictModule,Volo.Abp.Account.Pro.Public.Web.OpenIddict10.6.1) - .NET 10, IIS 10 in-process hosting, EF Core with SQL Server, multi-tenancy on,
IsDynamicClaimsEnabled = true - Standard middleware order:
UseAuthentication→UseAbpOpenIddictValidation→UseMultiTenancy→UseUnitOfWork→UseDynamicClaims→UseAuthorization→ … →UseConfiguredEndpoints - Client: Android app using the password grant (
/connect/token), then Bearer calls to[Authorize]application services. It closes the connection as soon as it has read the token response, which is normal for mobile HTTP clients.
Problem
The first API call after a successful login fails with 401 and WWW-Authenticate: ... error_description="SessionExpired". /connect/token returned 200 and AbpSecurityLogs has LoginSucceeded, but AbpSessions has no row for the token’s session_id (and OpenIddictTokens has no row either). Every later call with that token fails the same way.
Reproduction
We reproduced it with a small script that logs in with the password grant and then calls an [Authorize] endpoint with the token, on a new connection each time. Results on the same server and database:
Login connection closed right after reading the token, then the endpoint is checked every 100 ms for 10 s: never succeeds. 5 of 5 rounds, 101 checks each, all
SessionExpired. NoAbpSessionsrow is ever created.Login connection kept open, then the endpoint is checked every 10 ms: the first checks are rejected with
SessionExpired. A check 35–50 ms after the token response passes, with the same token. 5 of 5 rounds:| Round | Last rejected check (ms after token) | First passing check (ms after token) | | ----- | ------------------------------------ | ------------------------------------ | | 1 | – | 25 | | 2 | 32 | 50 | | 3 | 21 | 46 | | 4 | 22 | 36 | | 5 | 26 | 44 |
SQL after a mixed run of 15 logins: only the 5 logins whose connection stayed open have an
AbpSessionsrow. The 10 that closed the connection haveLoginSucceededinAbpSecurityLogsbut no session. So the token is sent before the session is saved. If the client disconnects in that window, the session is never saved.
Our reading of the cause
- The session is created by
OpenIddictCreateIdentitySession(aProcessSignInContexthandler) throughIdentitySessionManager.CreateAsync. That runs while theSignInresult executes, after the token controller’s unit-of-work action filter has finished. The insert therefore stays in the request-wide unit of work reserved byAbpUnitOfWorkMiddleware. AbpUnitOfWorkMiddlewarecompletes that unit of work afterawait next(context), which is after OpenIddict has written the token response. It passesICancellationTokenProvider.Token, i.e.HttpContext.RequestAborted.- When the client closes the connection after reading the response,
RequestAbortedfires, the commit is cancelled and the session (and token entry) are rolled back. The security log survives because it is saved in its own unit of work. OpenIddictValidateIdentitySessionValidationHandler/IdentitySessionCheckerthen rejects every request with that token asSessionExpired.
Workarounds
Client side: keep the login connection open about 2 s before closing it. This works, but every client has to know about it.
Server side, tested with the same script (5 rounds per variant):
| | No change |
ICancellationTokenProvider.Use(CancellationToken.None)around/connect/token, beforeUseUnitOfWork()|/connect/tokeninAbpAspNetCoreUnitOfWorkOptions.IgnoredUrls| | --- | --------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | Connection closed, then polled | session never saved | saved 0–41 ms after the token | there at the first check | | Connection kept open, then polled | saved 20–64 ms after the token | saved 6–92 ms after the token | there at the first check | | Call right after the token | 15 of 15 rejected | 13 of 15 rejected | 30 of 30 accepted (two runs) |The cancellation override stops the session from being lost, but it is still saved after the token.
IgnoredUrlssaves it before the token is sent and fixes both. We useIgnoredUrlsuntil we can upgrade to a release with the fix.
Question
In which ABP release is the fix from #10853 (PR #26017) included? Is it in a 10.6.x patch, or only in 10.7 or later?
1 Answer(s)
-
0
The fix referenced in #10853 (implemented by PR #26017) was not released in 10.6.1.
Based on the available verified information, it was scheduled for “an upcoming release” at the time of the #10853 answer, but the exact version (e.g., 10.6.2/10.6.3 vs 10.7.0+) is not explicitly stated in any verified source I can access here.
Since you can still reproduce “session never saved when the client disconnects” on 10.6.1, the safe/confirmed conclusion is: it is not included in 10.6.1; the earliest version containing it cannot be definitively confirmed from verified sources in this environment.
Practically: if you need a deterministic server-side behavior today, your current mitigation of adding
/connect/tokentoAbpAspNetCoreUnitOfWorkOptions.IgnoredUrls(or equivalent “commit-before-flush” approach) is consistent with the framework-side fix described in #10853, and you should keep it until you upgrade to a version where you can confirm the fix is present by testing.
Links (validated):
- https://abp.io/support/questions/10853
- https://github.com/abpframework/abp/pull/26017
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)