Skip to content

Instantly share code, notes, and snippets.

@hgirish
Created June 4, 2022 07:05
Show Gist options
  • Save hgirish/f234d65bfc23a7ff5f7f62efe2f8b817 to your computer and use it in GitHub Desktop.
Save hgirish/f234d65bfc23a7ff5f7f62efe2f8b817 to your computer and use it in GitHub Desktop.
Update Asp.Net Identity to Latest .net Core Identity, tested with Net6
// https://stackoverflow.com/questions/53878000/how-to-migrate-identity-users-from-a-mvc5-app-to-a-asp-net-core-2-2-app
/* We need a way to differentiate what users are using the new hash version or not.
One way is to add a new property to IdentityUser:
*/
using Microsoft.AspNetCore.Identity;
public class ApplicationUser: IdentityUser
{
public PasswordHashVersion HashVersion { get; set; }
public ApplicationUser()
{
HashVersion = PasswordHashVersion.Core;
}
}
public enum PasswordHashVersion
{
OldMvc,
Core
}
/*
This class inherits the new Identity Core PasswordHasher. If the user's password hash version is already using the new algorithm (e.g HashVersion = Core), then we just call the base method from PasswordHasher which uses the new algorithm. Otherwise, use the old identity algorithm to verify the password.
If the password matches, we update the user password hash version to Core, and return PasswordVerificationResult.SuccessRehashNeeded to force updating the existing hash with the new algorithm.
*/
public class OldMvcPasswordHasher : PasswordHasher<ApplicationUser>
{
public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
{
// if it's the new algorithm version, delegate the call to parent class
if (user.HashVersion == PasswordHashVersion.Core)
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
byte[] buffer4;
if (hashedPassword == null)
{
return PasswordVerificationResult.Failed;
}
if (providedPassword == null)
{
throw new ArgumentNullException("providedPassword");
}
byte[] src = Convert.FromBase64String(hashedPassword);
if ((src.Length != 0x31) || (src[0] != 0))
{
return PasswordVerificationResult.Failed;
}
byte[] dst = new byte[0x10];
Buffer.BlockCopy(src, 1, dst, 0, 0x10);
byte[] buffer3 = new byte[0x20];
Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(providedPassword, dst, 0x3e8))
{
buffer4 = bytes.GetBytes(0x20);
}
if (AreHashesEqual(buffer3, buffer4))
{
user.HashVersion = PasswordHashVersion.Core;
return PasswordVerificationResult.SuccessRehashNeeded;
}
return PasswordVerificationResult.Failed;
}
private bool AreHashesEqual(byte[] firstHash, byte[] secondHash)
{
int _minHashLength = firstHash.Length <= secondHash.Length ? firstHash.Length : secondHash.Length;
var xor = firstHash.Length ^ secondHash.Length;
for (int i = 0; i < _minHashLength; i++)
xor |= firstHash[i] ^ secondHash[i];
return 0 == xor;
}
}
//This must be added after any calls to AddIdentity, AddDefaultIdentity or AddIdentityCore.
// Replace the existing scoped IPasswordHasher<> implementation
services.Replace(new ServiceDescriptor(
serviceType: typeof(IPasswordHasher<ApplicationUser>),
implementationType: typeof(OldMvcPasswordHasher),
ServiceLifetime.Scoped));
-- Script to update Asp.Net Identity tables to Asp.Net Core or Net 6 Identity tables
IF (not EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'AspNetUserTokens'))
Begin
CREATE TABLE dbo.[AspNetUserTokens](
[UserId] [nvarchar](128) NOT NULL,
[LoginProvider] [nvarchar](128) NOT NULL,
[Name] [nvarchar](128) NOT NULL,
[Value] [nvarchar](max) NULL,
CONSTRAINT [PK_AspNetUserTokens] PRIMARY KEY CLUSTERED
(
[UserId] ASC,
[LoginProvider] ASC,
[Name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
end
GO
IF (not EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'AspNetRoleClaims'))
Begin
CREATE TABLE [dbo].[AspNetRoleClaims](
[Id] [int] IDENTITY(1,1) NOT NULL,
[RoleId] [nvarchar](128) NOT NULL,
[ClaimType] [nvarchar](max) NULL,
[ClaimValue] [nvarchar](max) NULL,
CONSTRAINT [PK_AspNetRoleClaims] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
end
GO
IF (OBJECT_ID('FK_AspNetRoleClaims_AspNetRoles_RoleId', 'F') IS NULL)
Begin
ALTER TABLE [dbo].[AspNetRoleClaims] WITH CHECK ADD CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId] FOREIGN KEY([RoleId])
REFERENCES [dbo].[AspNetRoles] ([Id])
ON DELETE CASCADE
end
GO
ALTER TABLE [dbo].[AspNetRoleClaims] CHECK CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId]
GO
IF (OBJECT_ID('FK_AspNetUserTokens_AspNetUsers_UserId', 'F') IS NULL)
Begin
ALTER TABLE [dbo].[AspNetUserTokens] WITH CHECK ADD CONSTRAINT [FK_AspNetUserTokens_AspNetUsers_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[AspNetUsers] ([Id])
ON DELETE CASCADE
end
GO
ALTER TABLE [dbo].[AspNetUserTokens] CHECK CONSTRAINT [FK_AspNetUserTokens_AspNetUsers_UserId]
GO
IF COL_LENGTH('dbo.AspNetUsers', 'NormalizedUserName') IS NULL
begin
alter table dbo.AspNetUsers
add NormalizedUserName nvarchar(256),
NormalizedEmail nvarchar(256),
ConcurrencyStamp nvarchar(max),
LockoutEnd datetimeOffset
end
go
IF COL_LENGTH('dbo.AspNetUserLogins', 'ProviderDisplayName') IS NULL
begin
alter table dbo.AspNetUserLogins add ProviderDisplayName nvarchar(max)
end
go
IF COL_LENGTH('dbo.AspNetRoles', 'NormalizedName') IS NULL
begin
alter table dbo.AspNetRoles
add NormalizedName nvarchar(256),
ConcurrencyStamp nvarchar(max)
end
go
UPDATE dbo.AspNetUsers SET NormalizedEmail = UPPER(Email), NormalizedUserName = UPPER(UserName)
WHERE NormalizedEmail IS NULL
go
update dbo.AspNetRoles set NormalizedName = upper(Name) where NormalizedName is null
go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment