cofoundry-cms / cofoundry

Cofoundry is an extensible and flexible .NET Core CMS & application framework focusing on code first development
https://www.cofoundry.org
MIT License
821 stars 144 forks source link

Document user migration from ASP.NET Identity #255

Open HeyJoel opened 6 years ago

HeyJoel commented 6 years ago

Cofoundry doesn't use the ASP.NET identity system because Cofoundry requires some additional features not available in ASP.NET identity (e.g. the ability to partition users across user areas).

It is possible to migrate an existing identity site to Cofoundry depending on the features in Identity your using. We should document this.

For copying CMS admin users, generally speaking these are the steps required:

1) Copy your Identity users to the Cofoundry.User table using "COF" UserAreaCode and an appropriate RoleId. For the Cofoundry Super Admin role you can query by looking up the role code select RoleId from Cofoundry.Role where RoleCode = 'SUP' and UserAreaCode = 'COF' 2) For password to work you'll need to replace the IPasswordCryptographyService with one that uses the ASP.NET Identity PasswordHasher. Cofoundry will be moving to use the ASP.NET Identity PasswordHasher in an upcoming release (currently slated for v6) but if you can't wait for that you can use the following code to override the existing implementation:

using Cofoundry.Domain;
using Cofoundry.Core.DependencyInjection;
using Microsoft.AspNetCore.Identity;
using Cofoundry.Domain.Data;

namespace CustomCryptography
{
    /// <summary>
    /// This adds our service as an override in the DI container.
    /// </summary>
    public class CustomCryptographyDependencyRegistration : IDependencyRegistration
    {
        public void Register(IContainerRegister container)
        {
            container.Register<IPasswordCryptographyService, AspNetIdentityPasswordCryptographyService>(RegistrationOptions.Override());
        }
    }

    /// <summary>
    /// We overide the Cofoundry hasher to take account of migrated accounts
    /// from asp.net identity.
    /// </summary>
    public class AspNetIdentityPasswordCryptographyService : PasswordCryptographyService
    {
        /// <summary>
        /// Verifies that an unecrypted password matches the specified hash.
        /// </summary>
        /// <param name="password">Plain text version of the password to check</param>
        /// <param name="hash">The encrypted hash to check the password against</param>
        /// <param name="version">The encryption version of the password hash.</param>
        public override bool Verify(string password, string hash, int version)
        {
            // use a unique version number for the hash
            if (version == 3333)
            {
                // Not sure why TUser is now required in PasswordHasher, it doesn't seem to be used.
                var user = new User();

                // use the asp.net identity hasher
                var hasher = new PasswordHasher<User>();
                var result = hasher.VerifyHashedPassword(user, hash, password);
                return result == PasswordVerificationResult.Success || result == PasswordVerificationResult.SuccessRehashNeeded;
            }

            // For non-migrated accounts just call into the base class
            return base.Verify(password, hash, version);
        }
    }
}

Note that the hash version here is "3333", you'll need to insert this value into your users table in the PasswordHashVersion column.

bbqchickenrobot commented 5 years ago

Doesn't this break compatibility with several asp.net mvc sites? There are other CMS (netcore) systems that are doing this across multi-tenant implementations. Just curious if this was the only possible method of achieving this?

HeyJoel commented 5 years ago

@bbqchickenrobot thanks for the feedback. When you say break compatibility do you mean:

  1. You would like to run your existing ASP.NET Identity side-by-side with Cofoundry User accounts as two separate systems
  2. You would like to use your existing ASP.NET Identity solution as auth for Cofoundry

For 1, that should be possible and I've done similar things with a custom auth schema running alongside Cofoundry as part of a migration where my custom accounts didn't need to be integrated into Cofoundry. The same should work for Identity (registering it ahead of Cofoundry in your middleware pipeline), but I haven't tested that.

For 2, our user system is tightly integrated into Cofoundry as it's a fundamental part of the CMS, I think abstraction here would cause more issues than it would solve and would restrict what we can do.

In terms of other CMS's, I've seen Orchard doing multi-tenancy where the user system is completely separated accross tenants - I don't think this is the same as what we do, the equivalent would be to have multiple instances of Identity in each tenant e.g. a Member area, a client area and the Cofoundry area in one tenant. It's hard to keep up with what all the CMS's are doing, so I could be wrong there. If I am can you point me to docs or an example of what you're after?

We do not support multi-tenancy, but I think that would need exploring in a separate issue.