We can definitely go down the route of fixing the issue on our side but we want you to be aware as this seems like a security vulnerability that needs patched. Thanks!
Summary ABP Commercial's Idle Session Timeout feature (Account Pro module, @volo/abp.ng.account) is implemented entirely client-side. The configured timeout is enforced by browser JavaScript only; the server does not track last-activity, does not refuse stale access tokens, and does not revoke them on idle. Any client-side failure (browser closed without logout, JavaScript disabled, extension interference, attacker bypassing the SPA by calling the API directly with a captured token) leaves the access token usable until its absolute exp — typically hours after the documented "idle" threshold has passed.
This makes the Idle Session Timeout a UX hint, not a security control, which is not how it is documented and not how enterprise customers (banking, healthcare, regulated SaaS) interpret it. It fails to meet the OWASP, ASVS, and NIST 800-63B session-management requirements that those customers are typically held to by their own auditors.
The cross-tab fix in v9.3.x → v10.x (BroadcastChannel-based coordination) is a real and welcome improvement at the UX layer. It does not change the underlying security posture.
Environment ABP version verified: @volo/abp.ng.account@10.0.1 (compiled output inspected directly) UI: Angular (commercial Account Pro module) Auth server: OpenIddict (JWT access tokens by default) Likely affected: all versions from v9.1 (when the Idle Session Timeout feature was introduced) forward Reproduction Configure Idle Session Timeout: Setting > Account > Idle Session Timeout, enabled, set timeout to e.g. 30 minutes.
Log in to the Angular SPA. Capture the bearer access token from the browser session (any of: DevTools Application → Local Storage, network request inspection, or a debugging proxy).
Stop interacting with the SPA. Wait past the idle threshold (e.g., 35 minutes). Observe the SPA's modal fire and "log out" the user.
From a separate process (curl, Postman, or any HTTP client), issue an authenticated API request using the captured bearer token — for example:
curl -H "Authorization: Bearer <captured-token>" https://your-host/api/account/my-profile Observe: the request succeeds. The token remains valid until its OpenIddict-configured exp (default 1 hour, often configured much longer for "remember me" / refresh-token flows).
Expected behavior After the configured idle threshold has elapsed without server-observed activity, the server should refuse the access token. Acceptable implementations:
Sliding refresh with idle gate. Short-lived access tokens (minutes). The refresh endpoint refuses to re-issue if now - LastActivityTime > IdleThreshold. LastActivityTime is updated on every successful authenticated request. Reference tokens + introspection. OpenIddict supports this. Idle revocation becomes a state change on the token record. Custom OpenIddict event handler on ProcessAuthenticationContext that checks a server-side LastActivityTime per user/session. Any of these would close the gap. The current timer(idleTimeoutMinutes * SECONDS) in IdleSessionService.startIdleTimeout does not.
Evidence (@volo/abp.ng.account@10.0.1) From the compiled source at package/fesm2022/volo-abp.ng.account-admin.mjs in the published npm tarball:
Lines 343–481 define IdleSessionService. Line 409–421 (startIdleTimeout): the idle timer is a client-side RxJS timer(idleTimeoutMinutes * SECONDS). No server round-trip, no last-activity ping, no refresh-token gate. Line 399–402 (logout): on countdown completion, calls this.authService.logout().subscribe(). This triggers the standard OpenIddict logout (clears the client's tokens and cookies). It does not revoke the access token on the server — and even if it did, the revocation only fires when the client-side timer reaches zero. If the client never gets there (browser closed, JS error, captured-token attacker), the server never knows the session went idle. No LastActivityTime tracking is performed on the server. No interceptor, no filter, no refresh-endpoint check. The Account Pro server-side IdleSessionSettings config exposes the threshold but does not enforce it. For comparison, the cross-tab fix at the same lines (BroadcastChannel 'idle-session-activity', lines 437–458) is well-implemented — this report is specifically about the server-side enforcement gap, not the client behavior.
Why this matters — standards references This is not a theoretical concern. Multiple authoritative standards that enterprise/regulated ABP customers are held to explicitly require server-side enforcement:
OWASP Session Management Cheat Sheet (link):
"Session timeouts must be set on the server side and must invalidate the session ID. It is mandatory for the web application to take active actions when the session expires, or the user actively closes it. It is not enough for the web application to expect the web browser to do it."
OWASP ASVS v4.0.3 Section 3 (link):
V3.3.1 (L1+): logout and expiration must invalidate the session token server-side. V3.3.2 (L2+): re-authentication required after idle period. V3.3.4 (L2+): server-side session termination. NIST SP 800-63B §7.2 (link):
AAL2: re-authentication required after 30 minutes of inactivity. AAL3: re-authentication required after 15 minutes of inactivity. "The session shall be terminated when any of these time limits is reached." PCI DSS v4.0 Requirement 8.2.8: 15-minute server-enforced idle timeout for cardholder-data environments.
An L2+ ASVS audit, a NIST AAL2 control review, or a PCI assessment of a current ABP-based application would flag the Idle Session Timeout feature as non-compliant. Customers in regulated industries who reasonably believed the feature was a security control will discover this during their own security reviews — typically late, and typically as a surprise.
Threat model The client-side-only design protects against a single threat: a casual passerby walking up to an unattended browser. It provides no protection against:
An attacker who has captured the access token (XSS, browser memory inspection, malware on the user's machine, unsigned-out shared workstation, intercepted proxy logs) and uses it directly via HTTP client. A token leak in logs, browser history, or accidentally-shared session. Any scenario where the legitimate client's JavaScript does not fire — browser closed, computer suspended, JS error, tab crashed. A stolen device where the attacker reads the token from disk-resident storage. The "idle timeout" name implies protection against these scenarios. The implementation does not provide it.
Suggested approach (not prescriptive) Any of the following would address the gap; the choice is ABP's:
Short access tokens + sliding refresh with idle gate (recommended).
Default AccessTokenLifetime to the idle threshold (or shorter). On the refresh-token endpoint, check LastActivityTime (new field, tracked server-side, updated on every authenticated request via a MiddlewareBase or IAsyncActionFilter). Refuse re-issue if exceeded. This is the lowest-friction option for existing customers; the client-side IdleSessionService continues to work as the UX layer, and the server becomes the source of truth. OpenIddict reference tokens + introspection.
Switch the default for new Account Pro deployments to reference tokens. Idle revocation becomes a single Status field update on the OpenIddictToken aggregate. Higher friction (introspection on every request), but cleanest semantics. Per-request LastActivityTime check.
A LastActivityTime field on user or a new UserSession aggregate. Updated on every authenticated request. A custom IAuthorizationFilter or OpenIddictServerEvents.ProcessAuthenticationContext handler checks the value and short-circuits with 401 if exceeded. Simpler than option 1 for customers not running a refresh-token flow; doesn't require JWT lifetime changes. In all three options, the existing IdleSessionService should also call a server-side Logout or Revoke endpoint when its countdown fires, so the server-side state is updated on the happy-path client-side timeout too.
The IdleSessionTimeoutMinutes setting should remain authoritative — server enforcement reads the same setting value as the client.
Related Multi-tab cross-tab coordination was fixed in v9.3.x (BroadcastChannel) — confirmed at v10.0.1. That fix is good. This bug is the next layer down and is distinct. ABP support thread on the multi-tab fix for context: #10079. ABP docs for the feature: https://abp.io/docs/latest/modules/account/idle-session-timeout. The doc should be updated to clearly state the enforcement boundary (whatever it ends up being) so customers don't misinterpret the feature. Severity rationale This is not a remote-code-execution-class vulnerability; an attacker still needs to capture a valid token or be at the user's workstation. But it is a security control that does not do what its name and documentation imply it does, in a framework that is explicitly marketed to enterprise and regulated customers. We consider it a high-severity correctness issue in a security feature, not a defect in functional behavior. We are filing because we have several enterprise customers (including regulated industries) who rely on this control and need it to be defensible in their own security reviews.
Happy to provide any additional reproduction detail or discuss implementation approaches.
Subject: Error: “The Libs folder is missing!” When Launching HttpApi.Host from ABP Studio / Visual Studio
Description:
We are encountering an issue when attempting to launch the HttpApi.Host project from within ABP Studio and Visual Studio. The error message indicates that the Libs folder is missing, despite having run the abp install-libs command as instructed.
Error Message:
The Libs folder is missing!
The Libs folder contains mandatory NPM Packages for running the project.
Make sure you run the abp install-libs CLI tool command.
If your application does not use any client-side libraries, you can disable this check by setting AbpMvcLibsOptions.CheckLibs to false, as shown below:
Configure<AbpMvcLibsOptions>(options =>
{
options.CheckLibs = false;
});
For more information, check out the ABP CLI documentation.
Steps Taken:
abp install-libs in the project root directory.HttpApi.Host project again in both ABP Studio and Visual Studio.Libs folder does not get created.Can you help us identify what might be going wrong or suggest additional troubleshooting steps?
Thanks in advance for your support!
Error: ./src/polyfills.ts Module build failed (from ./node_modules/@ngtools/webpack/src/ivy/index.js): Error: Debug error: DtsModuleScopeResolver.read(AccountSettingsModule from C:/Repos/BerganKDV.Fraud/angular/projects/account/admin/src/account-settings.module.ts), but not a .d.ts file at MetadataDtsModuleScopeResolver.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\dependency.js:52:23) at MetadataDtsModuleScopeResolver.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\dependency.js:100:46) at LocalModuleScopeRegistry.getExportedScope (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:529:51) at LocalModuleScopeRegistry.getScopeOfModuleReference (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:271:44) at LocalModuleScopeRegistry.getScopeOfModule (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:148:22) at LocalModuleScopeRegistry.getScopeForComponent (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:122:22) at ComponentDecoratorHandler.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\annotations\src\component.js:365:42) at TraitCompiler.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\transform\src\compilation.js:392:50) at NgCompiler.resolveCompilation (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\core\src\compiler.js:542:27) at NgCompiler.<anonymous> (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\core\src\compiler.js:423:34) at step (C:\Repos\BerganKDV.Fraud\angular\node_modules\tslib\tslib.js:143:27) at Object.next (C:\Repos\BerganKDV.Fraud\angular\node_modules\tslib\tslib.js:124:57) at fulfilled (C:\Repos\BerganKDV.Fraud\angular\node_modules\tslib\tslib.js:114:62) at processTicksAndRejections (node:internal/process/task_queues:94:5)
Error: (webpack)-dev-server/client?http://0.0.0.0:0&sockPath=/sockjs-node Module build failed (from ./node_modules/@ngtools/webpack/src/ivy/index.js): Error: Debug error: DtsModuleScopeResolver.read(AccountSettingsModule from C:/Repos/BerganKDV.Fraud/angular/projects/account/admin/src/account-settings.module.ts), but not a .d.ts file at MetadataDtsModuleScopeResolver.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\dependency.js:52:23) at MetadataDtsModuleScopeResolver.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\dependency.js:100:46) at LocalModuleScopeRegistry.getExportedScope (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:529:51) at LocalModuleScopeRegistry.getScopeOfModuleReference (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:271:44) at LocalModuleScopeRegistry.getScopeOfModule (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:148:22) at LocalModuleScopeRegistry.getScopeForComponent (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\scope\src\local.js:122:22) at ComponentDecoratorHandler.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\annotations\src\component.js:365:42) at TraitCompiler.resolve (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\transform\src\compilation.js:392:50) at NgCompiler.resolveCompilation (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\core\src\compiler.js:542:27) at NgCompiler.<anonymous> (C:\Repos\BerganKDV.Fraud\angular\node_modules@angular\compiler-cli\src\ngtsc\core\src\compiler.js:423:34) at step (C:\Repos\BerganKDV.Fraud\angular\node_modules\tslib\tslib.js:143:27) at Object.next (C:\Repos\BerganKDV.Fraud\angular\node_modules\tslib\tslib.js:124:57) at fulfilled (C:\Repos\BerganKDV.Fraud\angular\node_modules\tslib\tslib.js:114:62) at processTicksAndRejections (node:internal/process/task_queues:94:5)
Thanks!
I recently upgraded our account to a Business license, however our account still shows we are on the Team license. Can you set our account to the Business license?