JudahGabriel / RavenDB.Identity

RavenDB Identity provider for ASP.NET Core. Let RavenDB manage your users and logins.
https://www.nuget.org/packages/RavenDB.Identity/1.0.0
MIT License
61 stars 29 forks source link

Fix casting exception when adding user to role #39

Closed wagich closed 3 years ago

wagich commented 3 years ago

When adding a user to roles with UserManager.AddToRolesAsync, I get an exception (see stacktrace below). I tracked it down to the IdentityUser implementation which exposes IReadOnlyList<string> Roles but assumes the underlying implementation to be assignable to List<string>. When deserializing the type from RavenDB, the serializer seems to create a ReadOnlyCollection<string> (to best match the interface contract) which violates this assumption.

This PR makes sure that IdentityUser.Roles is always backed by a List<string>.

System.InvalidCastException: Unable to cast object of type 'System.Collections.ObjectModel.ReadOnlyCollection`1[System.String]' to type 'System.Collections.Generic.List`1[System.String]'.
   at Raven.Identity.IdentityUser.GetRolesList()
   at Raven.Identity.UserStore`2.AddToRoleAsync(TUser user, String roleName, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Identity.UserManager`1.AddToRolesAsync(TUser user, IEnumerable`1 roles)
JudahGabriel commented 3 years ago

@wagich Thanks for this! What is the backwards compatibility story here - if I apply this change, will it break existing databases?

wagich commented 3 years ago

@JudahGabriel This should be fully backwards compatible IMHO. It's just enforcing the (previously implicit) constraint that Roles always is a List<string>.

I think this worked in previous releases of Newtonsoft.Json because it always chose to use a List<T> when deserializing into IReadOnlyList<T> and somewhere along the way Newtonsoft.Json began using a more applicable type for this interface which broke the assumption.

JudahGabriel commented 3 years ago

I've deployed your fix to RavenDB.Identity 8.0.7. 👍