This commit is contained in:
David Nguyen
2025-02-12 16:41:35 +11:00
parent 548d92c2fc
commit 15922d447b
70 changed files with 889 additions and 551 deletions

View File

@ -12,11 +12,11 @@
"dependencies": {
"@documenso/lib": "*",
"@documenso/prisma": "*",
"@hono/zod-validator": "^0.4.2",
"@hono/standard-validator": "^0.1.2",
"@oslojs/crypto": "^1.0.1",
"@oslojs/encoding": "^1.1.0",
"arctic": "^3.1.0",
"hono": "4.6.15",
"hono": "4.7.0",
"luxon": "^3.5.0",
"nanoid": "^4.0.2",
"ts-pattern": "^5.0.5",

View File

@ -5,14 +5,28 @@ import { type Session, type User, UserSecurityAuditLogType } from '@prisma/clien
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { prisma } from '@documenso/prisma';
/**
* The user object to pass around the app.
*
* Do not put anything sensitive in here since it will be public.
*/
export type SessionUser = Pick<
User,
| 'id'
| 'name'
| 'email'
| 'emailVerified'
| 'avatarImageId'
| 'twoFactorEnabled'
| 'roles'
| 'signature'
| 'url'
>;
export type SessionValidationResult =
| {
session: Session;
user: User;
// user: Pick<
// User,
// 'id' | 'name' | 'email' | 'emailVerified' | 'avatarImageId' | 'twoFactorEnabled' | 'roles' // Todo
// >;
user: SessionUser;
isAuthenticated: true;
}
| { session: null; user: null; isAuthenticated: false };
@ -36,7 +50,7 @@ export const createSession = async (
const session: Session = {
id: hashedSessionId,
sessionToken: hashedSessionId, // todo
sessionToken: hashedSessionId,
userId,
updatedAt: new Date(),
createdAt: new Date(),
@ -69,23 +83,26 @@ export const validateSessionToken = async (token: string): Promise<SessionValida
id: sessionId,
},
include: {
user: true,
user: {
/**
* Do not expose anything sensitive here.
*/
select: {
id: true,
name: true,
email: true,
emailVerified: true,
avatarImageId: true,
twoFactorEnabled: true,
roles: true,
signature: true,
url: true,
},
},
},
});
// user: {
// select: {
// id: true,
// name: true,
// email: true,
// emailVerified: true,
// avatarImageId: true,
// twoFactorEnabled: true,
// },
// },
// todo; how can result.user be null?
if (result === null || !result.user) {
if (!result?.user) {
return { session: null, user: null, isAuthenticated: false };
}

View File

@ -1,4 +1,4 @@
import { zValidator } from '@hono/zod-validator';
import { sValidator } from '@hono/standard-validator';
import { compare } from '@node-rs/bcrypt';
import { Hono } from 'hono';
import { DateTime } from 'luxon';
@ -42,7 +42,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Authorize endpoint.
*/
.post('/authorize', zValidator('json', ZSignInSchema), async (c) => {
.post('/authorize', sValidator('json', ZSignInSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
const { email, password, totpCode, backupCode } = c.req.valid('json');
@ -131,7 +131,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Signup endpoint.
*/
.post('/signup', zValidator('json', ZSignUpSchema), async (c) => {
.post('/signup', sValidator('json', ZSignUpSchema), async (c) => {
if (env('NEXT_PUBLIC_DISABLE_SIGNUP') === 'true') {
throw new AppError('SIGNUP_DISABLED', {
message: 'Signups are disabled.',
@ -160,7 +160,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Update password endpoint.
*/
.post('/update-password', zValidator('json', ZUpdatePasswordSchema), async (c) => {
.post('/update-password', sValidator('json', ZUpdatePasswordSchema), async (c) => {
const { password, currentPassword } = c.req.valid('json');
const requestMetadata = c.get('requestMetadata');
@ -182,7 +182,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Verify email endpoint.
*/
.post('/verify-email', zValidator('json', ZVerifyEmailSchema), async (c) => {
.post('/verify-email', sValidator('json', ZVerifyEmailSchema), async (c) => {
const { state, userId } = await verifyEmail({ token: c.req.valid('json').token });
// If email is verified, automatically authenticate user.
@ -197,7 +197,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Resend verification email endpoint.
*/
.post('/resend-verify-email', zValidator('json', ZResendVerifyEmailSchema), async (c) => {
.post('/resend-verify-email', sValidator('json', ZResendVerifyEmailSchema), async (c) => {
const { email } = c.req.valid('json');
await jobsClient.triggerJob({
@ -212,7 +212,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Forgot password endpoint.
*/
.post('/forgot-password', zValidator('json', ZForgotPasswordSchema), async (c) => {
.post('/forgot-password', sValidator('json', ZForgotPasswordSchema), async (c) => {
const { email } = c.req.valid('json');
await forgotPassword({
@ -224,7 +224,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
/**
* Reset password endpoint.
*/
.post('/reset-password', zValidator('json', ZResetPasswordSchema), async (c) => {
.post('/reset-password', sValidator('json', ZResetPasswordSchema), async (c) => {
const { token, password } = c.req.valid('json');
const requestMetadata = c.get('requestMetadata');
@ -258,7 +258,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
*/
.post(
'/2fa/enable',
zValidator(
sValidator(
'json',
z.object({
code: z.string(),
@ -304,7 +304,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
*/
.post(
'/2fa/disable',
zValidator(
sValidator(
'json',
z.object({
totpCode: z.string().trim().optional(),
@ -325,6 +325,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
email: true,
twoFactorEnabled: true,
twoFactorSecret: true,
twoFactorBackupCodes: true,
},
});
@ -349,7 +350,7 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
*/
.post(
'/2fa/view-recovery-codes',
zValidator(
sValidator(
'json',
z.object({
token: z.string(),

View File

@ -1,4 +1,4 @@
import { zValidator } from '@hono/zod-validator';
import { sValidator } from '@hono/standard-validator';
import { Google, decodeIdToken, generateCodeVerifier, generateState } from 'arctic';
import { Hono } from 'hono';
import { deleteCookie, setCookie } from 'hono/cookie';
@ -35,7 +35,7 @@ export const googleRoute = new Hono<HonoAuthContext>()
/**
* Authorize endpoint.
*/
.post('/authorize', zValidator('json', ZGoogleAuthorizeSchema), (c) => {
.post('/authorize', sValidator('json', ZGoogleAuthorizeSchema), (c) => {
const scopes = options.scope;
const state = generateState();

View File

@ -1,4 +1,4 @@
import { zValidator } from '@hono/zod-validator';
import { sValidator } from '@hono/standard-validator';
import { UserSecurityAuditLogType } from '@prisma/client';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';
import { Hono } from 'hono';
@ -17,7 +17,7 @@ export const passkeyRoute = new Hono<HonoAuthContext>()
/**
* Authorize endpoint.
*/
.post('/authorize', zValidator('json', ZPasskeyAuthorizeSchema), async (c) => {
.post('/authorize', sValidator('json', ZPasskeyAuthorizeSchema), async (c) => {
const requestMetadata = c.get('requestMetadata');
const { csrfToken, credential } = c.req.valid('json');
@ -129,7 +129,7 @@ export const passkeyRoute = new Hono<HonoAuthContext>()
// .post(
// '/pre-authenticate',
// zValidator(
// sValidator(
// 'json',
// z.object({
// code: z.string(),