feat: password reauthentication for documents and recipients (#1827)

Adds password reauthentication to our existing reauth providers,
additionally swaps from an exclusive provider to an inclusive type where
multiple methods can be selected to offer a this or that experience.
This commit is contained in:
Lucas Smith
2025-06-07 00:27:19 +10:00
committed by GitHub
parent ce66da0055
commit 55c8632620
62 changed files with 985 additions and 466 deletions

View File

@ -123,8 +123,8 @@ export const ZDocumentAuditLogFieldDiffSchema = z.union([
]);
export const ZGenericFromToSchema = z.object({
from: z.string().nullable(),
to: z.string().nullable(),
from: z.union([z.string(), z.array(z.string())]).nullable(),
to: z.union([z.string(), z.array(z.string())]).nullable(),
});
export const ZRecipientDiffActionAuthSchema = ZGenericFromToSchema.extend({
@ -296,7 +296,7 @@ export const ZDocumentAuditLogEventDocumentFieldInsertedSchema = z.object({
},
z
.object({
type: ZRecipientActionAuthTypesSchema,
type: ZRecipientActionAuthTypesSchema.optional(),
})
.optional(),
),
@ -384,7 +384,7 @@ export const ZDocumentAuditLogEventDocumentFieldPrefilledSchema = z.object({
},
z
.object({
type: ZRecipientActionAuthTypesSchema,
type: ZRecipientActionAuthTypesSchema.optional(),
})
.optional(),
),
@ -428,7 +428,13 @@ export const ZDocumentAuditLogEventDocumentMetaUpdatedSchema = z.object({
export const ZDocumentAuditLogEventDocumentOpenedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED),
data: ZBaseRecipientDataSchema.extend({
accessAuth: z.string().optional(),
accessAuth: z.preprocess((unknownValue) => {
if (!unknownValue) {
return [];
}
return Array.isArray(unknownValue) ? unknownValue : [unknownValue];
}, z.array(ZRecipientAccessAuthTypesSchema)),
}),
});
@ -438,7 +444,13 @@ export const ZDocumentAuditLogEventDocumentOpenedSchema = z.object({
export const ZDocumentAuditLogEventDocumentRecipientCompleteSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED),
data: ZBaseRecipientDataSchema.extend({
actionAuth: z.string().optional(),
actionAuth: z.preprocess((unknownValue) => {
if (!unknownValue) {
return [];
}
return Array.isArray(unknownValue) ? unknownValue : [unknownValue];
}, z.array(ZRecipientActionAuthTypesSchema)),
}),
});
@ -516,8 +528,20 @@ export const ZDocumentAuditLogEventFieldUpdatedSchema = z.object({
export const ZDocumentAuditLogEventRecipientAddedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED),
data: ZBaseRecipientDataSchema.extend({
accessAuth: ZRecipientAccessAuthTypesSchema.optional(),
actionAuth: ZRecipientActionAuthTypesSchema.optional(),
accessAuth: z.preprocess((unknownValue) => {
if (!unknownValue) {
return [];
}
return Array.isArray(unknownValue) ? unknownValue : [unknownValue];
}, z.array(ZRecipientAccessAuthTypesSchema)),
actionAuth: z.preprocess((unknownValue) => {
if (!unknownValue) {
return [];
}
return Array.isArray(unknownValue) ? unknownValue : [unknownValue];
}, z.array(ZRecipientActionAuthTypesSchema)),
}),
});

View File

@ -9,8 +9,10 @@ export const ZDocumentAuthTypesSchema = z.enum([
'ACCOUNT',
'PASSKEY',
'TWO_FACTOR_AUTH',
'PASSWORD',
'EXPLICIT_NONE',
]);
export const DocumentAuth = ZDocumentAuthTypesSchema.Enum;
const ZDocumentAuthAccountSchema = z.object({
@ -27,6 +29,11 @@ const ZDocumentAuthPasskeySchema = z.object({
tokenReference: z.string().min(1),
});
const ZDocumentAuthPasswordSchema = z.object({
type: z.literal(DocumentAuth.PASSWORD),
password: z.string().min(1),
});
const ZDocumentAuth2FASchema = z.object({
type: z.literal(DocumentAuth.TWO_FACTOR_AUTH),
token: z.string().min(4).max(10),
@ -40,6 +47,7 @@ export const ZDocumentAuthMethodsSchema = z.discriminatedUnion('type', [
ZDocumentAuthExplicitNoneSchema,
ZDocumentAuthPasskeySchema,
ZDocumentAuth2FASchema,
ZDocumentAuthPasswordSchema,
]);
/**
@ -61,9 +69,15 @@ export const ZDocumentActionAuthSchema = z.discriminatedUnion('type', [
ZDocumentAuthAccountSchema,
ZDocumentAuthPasskeySchema,
ZDocumentAuth2FASchema,
ZDocumentAuthPasswordSchema,
]);
export const ZDocumentActionAuthTypesSchema = z
.enum([DocumentAuth.ACCOUNT, DocumentAuth.PASSKEY, DocumentAuth.TWO_FACTOR_AUTH])
.enum([
DocumentAuth.ACCOUNT,
DocumentAuth.PASSKEY,
DocumentAuth.TWO_FACTOR_AUTH,
DocumentAuth.PASSWORD,
])
.describe(
'The type of authentication required for the recipient to sign the document. This field is restricted to Enterprise plan users only.',
);
@ -89,6 +103,7 @@ export const ZRecipientActionAuthSchema = z.discriminatedUnion('type', [
ZDocumentAuthAccountSchema,
ZDocumentAuthPasskeySchema,
ZDocumentAuth2FASchema,
ZDocumentAuthPasswordSchema,
ZDocumentAuthExplicitNoneSchema,
]);
export const ZRecipientActionAuthTypesSchema = z
@ -96,6 +111,7 @@ export const ZRecipientActionAuthTypesSchema = z
DocumentAuth.ACCOUNT,
DocumentAuth.PASSKEY,
DocumentAuth.TWO_FACTOR_AUTH,
DocumentAuth.PASSWORD,
DocumentAuth.EXPLICIT_NONE,
])
.describe('The type of authentication required for the recipient to sign the document.');
@ -110,18 +126,26 @@ export const RecipientActionAuth = ZRecipientActionAuthTypesSchema.Enum;
*/
export const ZDocumentAuthOptionsSchema = z.preprocess(
(unknownValue) => {
if (unknownValue) {
return unknownValue;
if (!unknownValue || typeof unknownValue !== 'object') {
return {
globalAccessAuth: [],
globalActionAuth: [],
};
}
const globalAccessAuth =
'globalAccessAuth' in unknownValue ? processAuthValue(unknownValue.globalAccessAuth) : [];
const globalActionAuth =
'globalActionAuth' in unknownValue ? processAuthValue(unknownValue.globalActionAuth) : [];
return {
globalAccessAuth: null,
globalActionAuth: null,
globalAccessAuth,
globalActionAuth,
};
},
z.object({
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullable(),
globalActionAuth: ZDocumentActionAuthTypesSchema.nullable(),
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema),
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema),
}),
);
@ -130,21 +154,46 @@ export const ZDocumentAuthOptionsSchema = z.preprocess(
*/
export const ZRecipientAuthOptionsSchema = z.preprocess(
(unknownValue) => {
if (unknownValue) {
return unknownValue;
if (!unknownValue || typeof unknownValue !== 'object') {
return {
accessAuth: [],
actionAuth: [],
};
}
const accessAuth =
'accessAuth' in unknownValue ? processAuthValue(unknownValue.accessAuth) : [];
const actionAuth =
'actionAuth' in unknownValue ? processAuthValue(unknownValue.actionAuth) : [];
return {
accessAuth: null,
actionAuth: null,
accessAuth,
actionAuth,
};
},
z.object({
accessAuth: ZRecipientAccessAuthTypesSchema.nullable(),
actionAuth: ZRecipientActionAuthTypesSchema.nullable(),
accessAuth: z.array(ZRecipientAccessAuthTypesSchema),
actionAuth: z.array(ZRecipientActionAuthTypesSchema),
}),
);
/**
* Utility function to process the auth value.
*
* Converts the old singular auth value to an array of auth values.
*/
const processAuthValue = (value: unknown) => {
if (value === null || value === undefined) {
return [];
}
if (Array.isArray(value)) {
return value;
}
return [value];
};
export type TDocumentAuth = z.infer<typeof ZDocumentAuthTypesSchema>;
export type TDocumentAuthMethods = z.infer<typeof ZDocumentAuthMethodsSchema>;
export type TDocumentAuthOptions = z.infer<typeof ZDocumentAuthOptionsSchema>;