mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(core,schemas): add support for argon2d and argon2id (#6404)
This commit is contained in:
parent
43d5bd248c
commit
d56bc2f731
5 changed files with 88 additions and 3 deletions
10
.changeset/thirty-cups-joke.md
Normal file
10
.changeset/thirty-cups-joke.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
"@logto/schemas": minor
|
||||||
|
"@logto/core": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add support for new password digest algorithm argon2d and argon2id
|
||||||
|
|
||||||
|
In `POST /users`, the `passwordAlgorithm` field now accepts `Argon2d` and `Argon2id`.
|
||||||
|
|
||||||
|
Users with those algorithms will be migrated to `Argon2i` upon succussful sign in.
|
|
@ -99,7 +99,7 @@ describe('encryptUserPassword()', () => {
|
||||||
describe('verifyUserPassword()', () => {
|
describe('verifyUserPassword()', () => {
|
||||||
const { verifyUserPassword } = createUserLibrary(queries);
|
const { verifyUserPassword } = createUserLibrary(queries);
|
||||||
|
|
||||||
describe('Argon2', () => {
|
describe('Argon2i', () => {
|
||||||
it('resolves when password is correct', async () => {
|
it('resolves when password is correct', async () => {
|
||||||
await expect(verifyUserPassword(mockUser, 'password')).resolves.not.toThrowError();
|
await expect(verifyUserPassword(mockUser, 'password')).resolves.not.toThrowError();
|
||||||
});
|
});
|
||||||
|
@ -111,6 +111,43 @@ describe('verifyUserPassword()', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Argon2d', () => {
|
||||||
|
const user = {
|
||||||
|
...mockUser,
|
||||||
|
passwordEncrypted: '$argon2d$v=19$m=16,t=2,p=1$VW1JcEJrMjN1Vnp3Tm5JUA$Ddl/I6Zem7vbZ4r5jPCb/g',
|
||||||
|
passwordEncryptionMethod: UsersPasswordEncryptionMethod.Argon2d,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('resolves when password is correct', async () => {
|
||||||
|
await expect(verifyUserPassword(user, 'password')).resolves.not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects when password is incorrect', async () => {
|
||||||
|
await expect(verifyUserPassword(user, 'wrong')).rejects.toThrowError(
|
||||||
|
new RequestError({ code: 'session.invalid_credentials', status: 422 })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Argon2id', () => {
|
||||||
|
const user = {
|
||||||
|
...mockUser,
|
||||||
|
passwordEncrypted:
|
||||||
|
'$argon2id$v=19$m=16,t=2,p=1$VW1JcEJrMjN1Vnp3Tm5JUA$0uzNwxbjs/f/1e5r4uX7JQ',
|
||||||
|
passwordEncryptionMethod: UsersPasswordEncryptionMethod.Argon2id,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('resolves when password is correct', async () => {
|
||||||
|
await expect(verifyUserPassword(user, 'password')).resolves.not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects when password is incorrect', async () => {
|
||||||
|
await expect(verifyUserPassword(user, 'wrong')).rejects.toThrowError(
|
||||||
|
new RequestError({ code: 'session.invalid_credentials', status: 422 })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('MD5', () => {
|
describe('MD5', () => {
|
||||||
const user = {
|
const user = {
|
||||||
...mockUser,
|
...mockUser,
|
||||||
|
|
|
@ -178,7 +178,10 @@ export const createUserLibrary = (queries: Queries) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (passwordEncryptionMethod) {
|
switch (passwordEncryptionMethod) {
|
||||||
case UsersPasswordEncryptionMethod.Argon2i: {
|
// Argon2i, Argon2id, Argon2d shares the same verify function
|
||||||
|
case UsersPasswordEncryptionMethod.Argon2i:
|
||||||
|
case UsersPasswordEncryptionMethod.Argon2id:
|
||||||
|
case UsersPasswordEncryptionMethod.Argon2d: {
|
||||||
const result = await argon2Verify({ password, hash: passwordEncrypted });
|
const result = await argon2Verify({ password, hash: passwordEncrypted });
|
||||||
assertThat(result, new RequestError({ code: 'session.invalid_credentials', status: 422 }));
|
assertThat(result, new RequestError({ code: 'session.invalid_credentials', status: 422 }));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { sql } from '@silverhand/slonik';
|
||||||
|
|
||||||
|
import type { AlterationScript } from '../lib/types/alteration.js';
|
||||||
|
|
||||||
|
const alteration: AlterationScript = {
|
||||||
|
up: async (pool) => {
|
||||||
|
await pool.query(sql`
|
||||||
|
alter type users_password_encryption_method add value 'Argon2id';
|
||||||
|
alter type users_password_encryption_method add value 'Argon2d';
|
||||||
|
`);
|
||||||
|
},
|
||||||
|
down: async (pool) => {
|
||||||
|
const { rows } = await pool.query(sql`
|
||||||
|
select id from users
|
||||||
|
where password_encryption_method = ${'Argon2id'}
|
||||||
|
or password_encryption_method = ${'Argon2d'}
|
||||||
|
`);
|
||||||
|
if (rows.length > 0) {
|
||||||
|
throw new Error('There are users with password encryption methods Argon2id or Argon2d.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await pool.query(sql`
|
||||||
|
create type users_password_encryption_method_revised as enum ('Argon2i', 'SHA1', 'SHA256', 'MD5', 'Bcrypt');
|
||||||
|
|
||||||
|
alter table users
|
||||||
|
alter column password_encryption_method type users_password_encryption_method_revised
|
||||||
|
using password_encryption_method::text::users_password_encryption_method_revised;
|
||||||
|
|
||||||
|
drop type users_password_encryption_method;
|
||||||
|
alter type users_password_encryption_method_revised rename to users_password_encryption_method;
|
||||||
|
`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default alteration;
|
|
@ -1,6 +1,6 @@
|
||||||
/* init_order = 1 */
|
/* init_order = 1 */
|
||||||
|
|
||||||
create type users_password_encryption_method as enum ('Argon2i', 'SHA1', 'SHA256', 'MD5', 'Bcrypt');
|
create type users_password_encryption_method as enum ('Argon2i', 'Argon2id', 'Argon2d', 'SHA1', 'SHA256', 'MD5', 'Bcrypt');
|
||||||
|
|
||||||
create table users (
|
create table users (
|
||||||
tenant_id varchar(21) not null
|
tenant_id varchar(21) not null
|
||||||
|
|
Loading…
Reference in a new issue