# How to Migrate Passwords from Bcrypt to Argon2ID Hashes in AdonisJS

**Author:** Ndianabasi Udonkang  
**Published:** 2023-08-21

Learn how to migrate your users' passwords which were hashed with bcrypt algorithm to argon2id algorithm while ensuring that they can still login seamlessly

---

## Tags

- [Adonisjs](/llms/technical-blog/tag/adonisjs.md)
- [Passwords](/llms/technical-blog/tag/passwords.md)
- [Bcrypt Algorithm](/llms/technical-blog/tag/bcrypt-algorithm.md)
- [Password Hashing](/llms/technical-blog/tag/password-hashing.md)
- [Argon2ID Algorithm](/llms/technical-blog/tag/argon2-id-algorithm.md)
- [Hashing](/llms/technical-blog/tag/hashing.md)

---

## Article Content

You aren't alone on this. I've had to migrate several production applications which were running with Adonisjs V4 (using the `bcrypt` hashing algorithm) to Adonisjs V5 (using the `argon2id` hashing algorithm). The challenge here is ensuring existing users (before the migration) can still log in seamlessly.

Here is how to ensure that your users whose passwords were hashed with `bcrypt` can still login while progressively migrating their password hashes to the `argon2id` algorithm.

1. In your `User` model, add a method to check if the user's password is still hashed with the `bcrypt` algorithm:

```ts
public usingOldPassword(oldPassword?: string) {
    const self = this
    const password = oldPassword ?? self.password
    return !!password && ['$2a$', '$2b$', '$bcrypt'].some((pattern) => password.startsWith(pattern))
  }
```

In the above `usingOldPassword` method, `self` points at `this` - which is an instance of the `User` model. The `oldPassword` parameter is the user's password hash and is optional. If not supplied from the outside, it uses the user's password hash stored on the database.

`bcrypt` password hashes start with either `$2a$` or `$2b$`, or `$bcrypt`. See: <https://en.wikipedia.org/wiki/Bcrypt>

1. In your password verification logic, verify the user's password with either `bcrypt` or `argon2id`:

```ts
/**
 * Verify password
 */
const passwordMatched = user.usingOldPassword()
      ? await bcrypt.compare(this.payload.password,                      user.password)
      : await Hash.verify(user.password, this.payload.password)

if (!passwordMatched) {
      // Handle as your wish
  }
```

In the above snippet, we check if the user is using the old password. If `true`, we compare the password hash using `bcrypt`. If `false`, we compare using the default Adonisjs hashing driver which is `argon.`

1. Before you generate the token, overwrite the `bcrypt` hash with the `argon` hash:

```ts
  if (user.usingOldPassword()) {
    await user.merge({ password }).save()            
  }

  token = await auth!.use('api').attempt(email, password, {           expiresIn: loginExpiresAt })
  return token
```

The above snippet will resave the user's password using the `argon` algorithm if the user is still using the `bcrypt` hash. Ensure that you have the following `beforeSave` hook installed:

```typescript
@beforeSave()
  public static async hashPassword(user: User) {
    if (user.$dirty.password) {
      user.password = await Hash.make(user.$dirty.password)
    }
}
```

> This strategy could be applied for migrating password hashes from any hash algorithm to another with little adaptation - especially for hashing algorithms which require secrets.

If this worked for you or you have improvements, please drop a comment. All the best with your hash migrations 🚀


*This document was generated from the live article page on https://ndianabasi.com/technical-blog/article/nqgrwqfbusjv57iwwnexolvw/how-to-migrate-passwords-from-bcrypt-to-argon2-id-hashes-in-adonis-js • 2026-06-07*
