mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(schemas): add user verifications column (#4480)
This commit is contained in:
parent
5556a73b0a
commit
08a0a6748b
5 changed files with 75 additions and 12 deletions
|
@ -15,6 +15,7 @@ export const mockUser: User = {
|
|||
identities: {
|
||||
connector1: { userId: 'connector1', details: {} },
|
||||
},
|
||||
mfaVerifications: [],
|
||||
customData: {},
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_789,
|
||||
|
@ -39,6 +40,7 @@ export const mockUserWithPassword: User = {
|
|||
connector1: { userId: 'connector1', details: {} },
|
||||
},
|
||||
customData: {},
|
||||
mfaVerifications: [],
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_789,
|
||||
createdAt: 1_650_969_000_000,
|
||||
|
@ -58,6 +60,7 @@ export const mockUserList: User[] = [
|
|||
avatar: null,
|
||||
identities: {},
|
||||
customData: {},
|
||||
mfaVerifications: [],
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_000,
|
||||
createdAt: 1_650_969_000_000,
|
||||
|
@ -75,6 +78,7 @@ export const mockUserList: User[] = [
|
|||
avatar: null,
|
||||
identities: {},
|
||||
customData: {},
|
||||
mfaVerifications: [],
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_000,
|
||||
createdAt: 1_650_969_000_000,
|
||||
|
@ -92,6 +96,7 @@ export const mockUserList: User[] = [
|
|||
avatar: null,
|
||||
identities: {},
|
||||
customData: {},
|
||||
mfaVerifications: [],
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_000,
|
||||
createdAt: 1_650_969_000_000,
|
||||
|
@ -109,6 +114,7 @@ export const mockUserList: User[] = [
|
|||
avatar: null,
|
||||
identities: {},
|
||||
customData: {},
|
||||
mfaVerifications: [],
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_000,
|
||||
createdAt: 1_650_969_000_000,
|
||||
|
@ -126,6 +132,7 @@ export const mockUserList: User[] = [
|
|||
avatar: null,
|
||||
identities: {},
|
||||
customData: {},
|
||||
mfaVerifications: [],
|
||||
applicationId: 'bar',
|
||||
lastSignInAt: 1_650_969_465_000,
|
||||
createdAt: 1_650_969_000_000,
|
||||
|
|
|
@ -39,6 +39,7 @@ describe('user query', () => {
|
|||
...mockUser,
|
||||
identities: JSON.stringify(mockUser.identities),
|
||||
customData: JSON.stringify(mockUser.customData),
|
||||
mfaVerifications: JSON.stringify(mockUser.mfaVerifications),
|
||||
};
|
||||
|
||||
it('findUserByUsername', async () => {
|
||||
|
@ -271,6 +272,7 @@ describe('user query', () => {
|
|||
...mockUser,
|
||||
identities: JSON.stringify(restIdentities),
|
||||
customData: JSON.stringify(mockUser.customData),
|
||||
mfaVerifications: JSON.stringify(mockUser.mfaVerifications),
|
||||
};
|
||||
|
||||
const expectSql = sql`
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import type { AlterationScript } from '../lib/types/alteration.js';
|
||||
|
||||
const alteration: AlterationScript = {
|
||||
up: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table users
|
||||
add column if not exists mfa_verifications jsonb not null default '[]'::jsonb;
|
||||
`);
|
||||
},
|
||||
down: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table users
|
||||
drop column mfa_verifications;
|
||||
`);
|
||||
},
|
||||
};
|
||||
|
||||
export default alteration;
|
|
@ -92,18 +92,6 @@ export const customClientMetadataGuard = z.object({
|
|||
*/
|
||||
export type CustomClientMetadata = z.infer<typeof customClientMetadataGuard>;
|
||||
|
||||
/* === Users === */
|
||||
export const roleNamesGuard = z.string().array();
|
||||
|
||||
const identityGuard = z.object({
|
||||
userId: z.string(),
|
||||
details: z.object({}).optional(), // Connector's userinfo details, schemaless
|
||||
});
|
||||
export const identitiesGuard = z.record(identityGuard);
|
||||
|
||||
export type Identity = z.infer<typeof identityGuard>;
|
||||
export type Identities = z.infer<typeof identitiesGuard>;
|
||||
|
||||
/* === SignIn Experiences === */
|
||||
|
||||
export const colorGuard = z.object({
|
||||
|
@ -186,6 +174,51 @@ export const mfaGuard = z.object({
|
|||
|
||||
export type Mfa = z.infer<typeof mfaGuard>;
|
||||
|
||||
/* === Users === */
|
||||
export const roleNamesGuard = z.string().array();
|
||||
|
||||
const identityGuard = z.object({
|
||||
userId: z.string(),
|
||||
details: z.object({}).optional(), // Connector's userinfo details, schemaless
|
||||
});
|
||||
export const identitiesGuard = z.record(identityGuard);
|
||||
|
||||
export type Identity = z.infer<typeof identityGuard>;
|
||||
export type Identities = z.infer<typeof identitiesGuard>;
|
||||
|
||||
const baseMfaVerification = {
|
||||
id: z.string(),
|
||||
createdAt: z.date(),
|
||||
};
|
||||
|
||||
const mfaVerificationGuard = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
type: z.literal(MfaFactor.TOTP),
|
||||
...baseMfaVerification,
|
||||
key: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(MfaFactor.WebAuthn),
|
||||
...baseMfaVerification,
|
||||
credentialId: z.string(),
|
||||
publicKey: z.string(),
|
||||
counter: z.number(),
|
||||
agent: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(MfaFactor.BackupCode),
|
||||
...baseMfaVerification,
|
||||
code: z.string(),
|
||||
usedAt: z.date().optional(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type MfaVerification = z.infer<typeof mfaVerificationGuard>;
|
||||
|
||||
export const mfaVerificationsGuard = mfaVerificationGuard.array();
|
||||
|
||||
export type MfaVerifications = z.infer<typeof mfaVerificationsGuard>;
|
||||
|
||||
/* === Phrases === */
|
||||
|
||||
export type Translation = {
|
||||
|
|
|
@ -16,6 +16,7 @@ create table users (
|
|||
application_id varchar(21),
|
||||
identities jsonb /* @use Identities */ not null default '{}'::jsonb,
|
||||
custom_data jsonb /* @use JsonObject */ not null default '{}'::jsonb,
|
||||
mfa_verifications jsonb /* @use MfaVerifications */ not null default '[]'::jsonb,
|
||||
is_suspended boolean not null default false,
|
||||
last_sign_in_at timestamptz,
|
||||
created_at timestamptz not null default (now()),
|
||||
|
|
Loading…
Reference in a new issue