mirror of
https://github.com/documenso/documenso.git
synced 2025-11-20 19:51:32 +10:00
fix: wip
This commit is contained in:
@ -53,8 +53,16 @@ export class AuthClient {
|
||||
}
|
||||
|
||||
public emailPassword = {
|
||||
signIn: async (data: TEmailPasswordSignin) => {
|
||||
const response = await this.client['email-password'].authorize.$post({ json: data });
|
||||
signIn: async (data: Omit<TEmailPasswordSignin, 'csrfToken'>) => {
|
||||
const { csrfToken } = await this.client.csrf.$get().then(async (res) => res.json());
|
||||
|
||||
const response = await this.client['email-password'].authorize.$post({
|
||||
json: {
|
||||
...data,
|
||||
csrfToken,
|
||||
},
|
||||
});
|
||||
|
||||
await this.handleError(response);
|
||||
|
||||
handleSignInRedirect(data.redirectPath);
|
||||
|
||||
@ -2,9 +2,11 @@ import { Hono } from 'hono';
|
||||
import { HTTPException } from 'hono/http-exception';
|
||||
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
|
||||
import { setCsrfCookie } from './lib/session/session-cookies';
|
||||
import { emailPasswordRoute } from './routes/email-password';
|
||||
import { googleRoute } from './routes/google';
|
||||
import { passkeyRoute } from './routes/passkey';
|
||||
@ -16,8 +18,28 @@ import type { HonoAuthContext } from './types/context';
|
||||
export const auth = new Hono<HonoAuthContext>()
|
||||
.use(async (c, next) => {
|
||||
c.set('requestMetadata', extractRequestMetadata(c.req.raw));
|
||||
|
||||
// Todo: Maybe use auth URL.
|
||||
const validOrigin = new URL(NEXT_PUBLIC_WEBAPP_URL()).origin;
|
||||
const headerOrigin = c.req.header('Origin');
|
||||
|
||||
if (headerOrigin && headerOrigin !== validOrigin) {
|
||||
return c.json(
|
||||
{
|
||||
message: 'Forbidden',
|
||||
statusCode: 403,
|
||||
},
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
await next();
|
||||
})
|
||||
.get('/csrf', async (c) => {
|
||||
const csrfToken = await setCsrfCookie(c);
|
||||
|
||||
return c.json({ csrfToken });
|
||||
})
|
||||
.route('/', sessionRoute)
|
||||
.route('/', signOutRoute)
|
||||
.route('/email-password', emailPasswordRoute)
|
||||
|
||||
@ -6,6 +6,8 @@ import { useSecureCookies } from '@documenso/lib/constants/auth';
|
||||
import { appLog } from '@documenso/lib/utils/debugger';
|
||||
import { env } from '@documenso/lib/utils/env';
|
||||
|
||||
import { generateSessionToken } from './session';
|
||||
|
||||
export const sessionCookieName = 'sessionId';
|
||||
|
||||
const getAuthSecret = () => {
|
||||
@ -30,7 +32,7 @@ const getAuthDomain = () => {
|
||||
export const sessionCookieOptions = {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: useSecureCookies ? 'none' : 'lax',
|
||||
sameSite: useSecureCookies ? 'none' : 'lax', // Todo: This feels wrong?
|
||||
secure: useSecureCookies,
|
||||
domain: getAuthDomain(),
|
||||
// Todo: Max age for specific auth cookies.
|
||||
@ -89,3 +91,23 @@ export const setSessionCookie = async (c: Context, sessionToken: string) => {
|
||||
export const deleteSessionCookie = (c: Context) => {
|
||||
deleteCookie(c, sessionCookieName, sessionCookieOptions);
|
||||
};
|
||||
|
||||
export const getCsrfCookie = async (c: Context) => {
|
||||
const csrfToken = await getSignedCookie(c, getAuthSecret(), 'csrfToken');
|
||||
|
||||
return csrfToken || null;
|
||||
};
|
||||
|
||||
export const setCsrfCookie = async (c: Context) => {
|
||||
const csrfToken = generateSessionToken();
|
||||
|
||||
await setSignedCookie(c, 'csrfToken', csrfToken, getAuthSecret(), {
|
||||
...sessionCookieOptions,
|
||||
|
||||
// Explicity set to undefined for session lived cookie.
|
||||
expires: undefined,
|
||||
maxAge: undefined,
|
||||
});
|
||||
|
||||
return csrfToken;
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@ import { prisma } from '@documenso/prisma';
|
||||
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
|
||||
|
||||
import { AuthenticationErrorCode } from '../lib/errors/error-codes';
|
||||
import { getCsrfCookie } from '../lib/session/session-cookies';
|
||||
import { onAuthorize } from '../lib/utils/authorizer';
|
||||
import { getRequiredSession, getSession } from '../lib/utils/get-session';
|
||||
import type { HonoAuthContext } from '../types/context';
|
||||
@ -45,7 +46,16 @@ export const emailPasswordRoute = new Hono<HonoAuthContext>()
|
||||
.post('/authorize', sValidator('json', ZSignInSchema), async (c) => {
|
||||
const requestMetadata = c.get('requestMetadata');
|
||||
|
||||
const { email, password, totpCode, backupCode } = c.req.valid('json');
|
||||
const { email, password, totpCode, backupCode, csrfToken } = c.req.valid('json');
|
||||
|
||||
const csrfCookieToken = await getCsrfCookie(c);
|
||||
|
||||
// Todo: Add logging here.
|
||||
if (csrfToken !== csrfCookieToken || !csrfCookieToken) {
|
||||
throw new AppError(AuthenticationErrorCode.InvalidRequest, {
|
||||
message: 'Invalid CSRF token',
|
||||
});
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
|
||||
@ -10,6 +10,7 @@ export const ZSignInSchema = z.object({
|
||||
password: ZCurrentPasswordSchema,
|
||||
totpCode: z.string().trim().optional(),
|
||||
backupCode: z.string().trim().optional(),
|
||||
csrfToken: z.string().trim(),
|
||||
});
|
||||
|
||||
export type TSignInSchema = z.infer<typeof ZSignInSchema>;
|
||||
|
||||
Reference in New Issue
Block a user