migrateLegacyPasswordHashes static method
Migrates legacy password hashes to the latest hash algorithm.
batchSize
is the number of entries to migrate in each batch.
maxMigratedEntries
is the maximum number of entries that will be
migrated. If null, all entries in the database will be migrated.
Returns the number of migrated entries.
Warning: This migration method is designed for password hashes generated by the framework's default algorithm. Hashes stored with a custom generator or different algorithm may produce unexpected results.
Implementation
static Future<int> migrateLegacyPasswordHashes(
Session session, {
int batchSize = 100,
int? maxMigratedEntries,
}) async {
if (AuthConfig.current.passwordHashGenerator.hashCode !=
defaultGeneratePasswordHash.hashCode ||
AuthConfig.current.passwordHashValidator.hashCode !=
defaultValidatePasswordHash.hashCode) {
throw Exception(
'Legacy password hash migration not supported when using custom password hash algorithm.');
}
var updatedEntries = 0;
int lastEntryId = 0;
while (true) {
var entries = await EmailAuth.db.find(
session,
where: (t) => t.hash.notLike(r'%$%') & (t.id > lastEntryId),
orderBy: (t) => t.id,
limit: batchSize,
);
if (entries.isEmpty) {
return updatedEntries;
}
if (maxMigratedEntries != null) {
if (maxMigratedEntries == updatedEntries) {
return updatedEntries;
}
var entrySurplus =
(updatedEntries + entries.length) - maxMigratedEntries;
if (entrySurplus > 0) {
entries = entries.sublist(0, entries.length - entrySurplus);
}
}
lastEntryId = entries.last.id!;
var migratedEntries = await Future.wait(entries.where((entry) {
try {
return PasswordHash(
entry.hash,
legacySalt: EmailSecrets.legacySalt,
).isLegacyHash();
} catch (e) {
session.log(
'Error when checking if hash is legacy: $e',
level: LogLevel.error,
);
return false;
}
}).map((entry) async {
return entry.copyWith(
hash: await PasswordHash.migratedLegacyToArgon2idHash(
entry.hash,
legacySalt: EmailSecrets.legacySalt,
pepper: EmailSecrets.pepper,
allowUnsecureRandom: AuthConfig.current.allowUnsecureRandom,
),
);
}).toList());
try {
await EmailAuth.db.update(session, migratedEntries);
updatedEntries += migratedEntries.length;
} catch (e) {
session.log(
'Failed to update migrated entries: $e',
level: LogLevel.error,
);
}
}
}