cofoundry icon indicating copy to clipboard operation
cofoundry copied to clipboard

Document user migration from ASP.NET Identity

Open HeyJoel opened this issue 6 years ago • 2 comments

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.

HeyJoel avatar Oct 04 '18 15:10 HeyJoel

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?

bbqchickenrobot avatar Jan 04 '19 19:01 bbqchickenrobot

@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.

HeyJoel avatar Jan 08 '19 13:01 HeyJoel