mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
## Description Add support to login with passkeys. Passkeys can be added via the user security settings page. Note: Currently left out adding the type of authentication method for the 'user security audit logs' because we're using the `signIn` next-auth event which doesn't appear to provide the context. Will look into it at another time. ## Changes Made - Add passkeys to login - Add passkeys feature flag - Add page to manage passkeys - Add audit logs relating to passkeys - Updated prisma schema to support passkeys & anonymous verification tokens ## Testing Performed To be done. MacOS: - Safari ✅ - Chrome ✅ - Firefox ✅ Windows: - Chrome [Untested] - Firefox [Untested] Linux: - Chrome [Untested] - Firefox [Untested] iOS: - Safari ✅ ## Checklist <!--- Please check the boxes that apply to this pull request. --> <!--- You can add or remove items as needed. --> - [X] I have tested these changes locally and they work as expected. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced Passkey authentication, including creation, sign-in, and management of passkeys. - Added a Passkeys section in Security Settings for managing user passkeys. - Implemented UI updates for Passkey authentication, including a new dialog for creating passkeys and a data table for managing them. - Enhanced security settings with server-side feature flags to conditionally display new security features. - **Bug Fixes** - Improved UI consistency in the Settings Security Activity Page. - Updated button styling in the 2FA Recovery Codes component for better visibility. - **Refactor** - Streamlined authentication options to include WebAuthn credentials provider. - **Chores** - Updated database schema to support passkeys and related functionality. - Added new audit log types for passkey-related activities. - Enhanced server-only authentication utilities for passkey registration and management. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
59 lines
1.5 KiB
TypeScript
59 lines
1.5 KiB
TypeScript
import { generateRegistrationOptions } from '@simplewebauthn/server';
|
|
import type { AuthenticatorTransportFuture } from '@simplewebauthn/types';
|
|
import { DateTime } from 'luxon';
|
|
|
|
import { prisma } from '@documenso/prisma';
|
|
|
|
import { PASSKEY_TIMEOUT } from '../../constants/auth';
|
|
import { getAuthenticatorRegistrationOptions } from '../../utils/authenticator';
|
|
|
|
type CreatePasskeyRegistrationOptions = {
|
|
userId: number;
|
|
};
|
|
|
|
export const createPasskeyRegistrationOptions = async ({
|
|
userId,
|
|
}: CreatePasskeyRegistrationOptions) => {
|
|
const user = await prisma.user.findFirstOrThrow({
|
|
where: {
|
|
id: userId,
|
|
},
|
|
select: {
|
|
name: true,
|
|
email: true,
|
|
passkeys: true,
|
|
},
|
|
});
|
|
|
|
const { passkeys } = user;
|
|
|
|
const { rpName, rpId: rpID } = getAuthenticatorRegistrationOptions();
|
|
|
|
const options = await generateRegistrationOptions({
|
|
rpName,
|
|
rpID,
|
|
userID: userId.toString(),
|
|
userName: user.email,
|
|
userDisplayName: user.name ?? undefined,
|
|
timeout: PASSKEY_TIMEOUT,
|
|
attestationType: 'none',
|
|
excludeCredentials: passkeys.map((passkey) => ({
|
|
id: passkey.credentialId,
|
|
type: 'public-key',
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
transports: passkey.transports as AuthenticatorTransportFuture[],
|
|
})),
|
|
});
|
|
|
|
await prisma.verificationToken.create({
|
|
data: {
|
|
userId,
|
|
token: options.challenge,
|
|
expires: DateTime.now().plus({ minutes: 2 }).toJSDate(),
|
|
identifier: 'PASSKEY_CHALLENGE',
|
|
},
|
|
});
|
|
|
|
return options;
|
|
};
|