License status corruption
Steps To Reproduce
Noticed last week while traveling, that the Bitwarden Android app was reporting no premium to allow viewing a TOTP. I now got back, and checked the admin page
So somehow the premium expiration is in the future, yet there is no premium status on the user. Trying to re-upload the license downloaded from vault.bitwarden.com says the license is not supported for the user, with the server returning a 400 on /api/accounts/license.
On further debugging and looking at the license files, my guess is that changing the user email invalidated the license (the license file still has the old email). Similarly, the newly downloaded license file on bitwarden.com has a different email address, which I guess results in the 400 and invalid user.
Hopefully you are not using the email address as an identifier for users, and also don't require the self-host and bitwarden.com accounts to have the same email addresses to function properly. Emails are not immutable, and that would mean any time you change your email on the self-hosted version, you would need to re-do a license upload dance.
So hopefully just a bug.
Expected Result
User has access to premium features, or is able to re-upload a license file.
Actual Result
User has no premium features, and is unable to re-upload a license file.
Screenshots or Videos
No response
Additional Context
Tried to upgrade to 2024.6.2 to see if the issue would resolve, but nope. Android app has been auto-updating, server had not been updated from 2024.4.2. Single home / personal user, not an organization account either on bitwarden.com nor at the self-hosted server. Changed user email some time ago and quite sure after logout / login, the TOTP was still working on the mobile devices. Similarly, TOTP was still working on the browser extension, though that died today too.
Githash Version
f59c17db-dirty
Environment Details
Docker image from docker hub.
Database Image
postgres:14
Issue-Link
https://github.com/bitwarden/server/issues/2480
Issue Tracking Info
- [X] I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.
Hi there,
I am unable to reproduce this issue, it has been escalated for further investigation. If you have more information that can help us, please add it below.
Thanks!
I can see if I can dig up anything from logs later today if there'd be more information somewhere. Let me know if there is anything specific that would be helpful.
I might also test creating another user and seeing how that one behaves, since not having access to the 2fA codes locks me out of a fair number of services (luckily I have kept a slightly older backup in iCloud, but trickier to use with Android).
Here are a couple of errors from the runtime log that I think are related:
System.ArgumentException: An item with the same key has already been added.
Key: 482f930e-e9e6-XXXX-XXXX-XXXXXXXXXXXX
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at Bit.Core.Services.LicensingService.ValidateUserPremiumAsync(User user) in /source/src/Core/Services/Implementations/LicensingService.cs:line 189
at Bit.Identity.IdentityServer.ProfileService.GetProfileDataAsync(ProfileDataRequestContext context) in /source/src/Identity/IdentityServer/ProfileService.cs:line 46
at Duende.IdentityServer.Services.DefaultClaimsService.GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) in /_/src/IdentityServer/Services/Default/DefaultClaimsService.cs:line 211
at Duende.IdentityServer.Services.DefaultTokenService.CreateAccessTokenAsync(TokenCreationRequest request) in /_/src/IdentityServer/Services/Default/DefaultTokenService.cs:line 191
at Duende.IdentityServer.ResponseHandling.TokenResponseGenerator.ProcessRefreshTokenRequestAsync(TokenRequestValidationResult request) in /_/src/IdentityServer/ResponseHandling/Default/TokenResponseGenerator.cs:line 201
at Duende.IdentityServer.ResponseHandling.TokenResponseGenerator.ProcessAsync(TokenRequestValidationResult request) in /_/src/IdentityServer/ResponseHandling/Default/TokenResponseGenerator.cs:line 102
at Duende.IdentityServer.Endpoints.TokenEndpoint.ProcessTokenRequestAsync(HttpContext context) in /_/src/IdentityServer/Endpoints/TokenEndpoint.cs:line 128
at Duende.IdentityServer.Endpoints.TokenEndpoint.ProcessAsync(HttpContext context) in /_/src/IdentityServer/Endpoints/TokenEndpoint.cs:line 81
at Duende.IdentityServer.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IdentityServerOptions options, IEndpointRouter router, IUserSession userSession, IEventService events, IIssuerNameService issuerNameService, ISessionCoordinationService sessionCoordinationService) in /_/src/IdentityServer/Hosting/IdentityServerMiddleware.cs:line 101
And
System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at Bit.Core.Services.LicensingService.ValidateUserPremiumAsync(User user) in /source/src/Core/Services/Implementations/LicensingService.cs:line 189
at Bit.Identity.IdentityServer.ProfileService.GetProfileDataAsync(ProfileDataRequestContext context) in /source/src/Identity/IdentityServer/ProfileService.cs:line 46
at Duende.IdentityServer.Services.DefaultClaimsService.GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) in /_/src/IdentityServer/Services/Default/DefaultClaimsService.cs:line 211
at Duende.IdentityServer.Services.DefaultTokenService.CreateAccessTokenAsync(TokenCreationRequest request) in /_/src/IdentityServer/Services/Default/DefaultTokenService.cs:line 191
at Duende.IdentityServer.ResponseHandling.TokenResponseGenerator.ProcessRefreshTokenRequestAsync(TokenRequestValidationResult request) in /_/src/IdentityServer/ResponseHandling/Default/TokenResponseGenerator.cs:line 201
at Duende.IdentityServer.ResponseHandling.TokenResponseGenerator.ProcessAsync(TokenRequestValidationResult request) in /_/src/IdentityServer/ResponseHandling/Default/TokenResponseGenerator.cs:line 102
at Duende.IdentityServer.Endpoints.TokenEndpoint.ProcessTokenRequestAsync(HttpContext context) in /_/src/IdentityServer/Endpoints/TokenEndpoint.cs:line 128
at Duende.IdentityServer.Endpoints.TokenEndpoint.ProcessAsync(HttpContext context) in /_/src/IdentityServer/Endpoints/TokenEndpoint.cs:line 81
at Duende.IdentityServer.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IdentityServerOptions options, IEndpointRouter router, IUserSession userSession, IEventService events, IIssuerNameService issuerNameService, ISessionCoordinationService sessionCoordinationService) in /_/src/IdentityServer/Hosting/IdentityServerMiddleware.cs:line 101
Hopefully you are not using the email address as an identifier for users, and also don't require the self-host and bitwarden.com accounts to have the same email addresses to function properly. Emails are not immutable, and that would mean any time you change your email on the self-hosted version, you would need to re-do a license upload dance.
As per https://bitwarden.com/help/licensing-on-premise/ the emails on cloud and self-hosted must match. The error seem to suggest that server is trying to upload license for different email of ID that already exists and is assigned to other user. You'll need to change your email on self-hosted instance to match cloud one and then upload new license file.