diff --git a/apps/web/src/app/(dashboard)/settings/token/page.tsx b/apps/web/src/app/(dashboard)/settings/token/page.tsx
index ab4992f14..889e7a2a8 100644
--- a/apps/web/src/app/(dashboard)/settings/token/page.tsx
+++ b/apps/web/src/app/(dashboard)/settings/token/page.tsx
@@ -3,7 +3,7 @@ import { ApiTokenForm } from '~/components/forms/token';
export default function ApiToken() {
return (
-
API Token
+
API Tokens
On this page, you can create new API tokens and manage the existing ones.
diff --git a/apps/web/src/components/forms/token.tsx b/apps/web/src/components/forms/token.tsx
index 954d4dd2e..f3f05028e 100644
--- a/apps/web/src/components/forms/token.tsx
+++ b/apps/web/src/components/forms/token.tsx
@@ -6,6 +6,7 @@ import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Loader } from 'lucide-react';
+import { DateTime } from 'luxon';
import { useForm } from 'react-hook-form';
import type { z } from 'zod';
@@ -139,23 +140,13 @@ export const ApiTokenForm = ({ className }: ApiTokenFormProps) => {
Created:{' '}
{token.createdAt
- ? new Date(token.createdAt).toLocaleDateString(undefined, {
- weekday: 'long',
- year: 'numeric',
- month: 'long',
- day: 'numeric',
- })
+ ? DateTime.fromJSDate(token.createdAt).toLocaleString(DateTime.DATETIME_FULL)
: 'N/A'}
Expires:{' '}
{token.expires
- ? new Date(token.expires).toLocaleDateString(undefined, {
- weekday: 'long',
- year: 'numeric',
- month: 'long',
- day: 'numeric',
- })
+ ? DateTime.fromJSDate(token.createdAt).toLocaleString(DateTime.DATETIME_FULL)
: 'N/A'}
{
- try {
- return await checkUserFromToken({ token });
- } catch (e) {
- return null;
- }
-};
-
const router = createNextRoute(contract, {
getDocuments: async (args) => {
const page = Number(args.query.page) || 1;
const perPage = Number(args.query.perPage) || 10;
const { authorization } = args.headers;
+ let user;
- const user = await validateUserToken(authorization);
-
- if (!user) {
+ try {
+ user = await checkUserFromToken({ token: authorization });
+ } catch (e) {
return {
status: 401,
body: {
- message: 'Unauthorized',
+ message: e.message,
},
};
}
@@ -50,14 +43,15 @@ const router = createNextRoute(contract, {
getDocument: async (args) => {
const { id: documentId } = args.params;
const { authorization } = args.headers;
+ let user;
- const user = await validateUserToken(authorization);
-
- if (!user) {
+ try {
+ user = await checkUserFromToken({ token: authorization });
+ } catch (e) {
return {
status: 401,
body: {
- message: 'Unauthorized',
+ message: e.message,
},
};
}
@@ -82,13 +76,15 @@ const router = createNextRoute(contract, {
const { id: documentId } = args.params;
const { authorization } = args.headers;
- const user = await validateUserToken(authorization);
+ let user;
- if (!user) {
+ try {
+ user = await checkUserFromToken({ token: authorization });
+ } catch (e) {
return {
status: 401,
body: {
- message: 'Unauthorized',
+ message: e.message,
},
};
}
@@ -141,14 +137,15 @@ const router = createNextRoute(contract, {
const { authorization } = args.headers;
const { id } = args.params;
const { body } = args;
+ let user;
- const user = await validateUserToken(authorization);
-
- if (!user) {
+ try {
+ user = await checkUserFromToken({ token: authorization });
+ } catch (e) {
return {
status: 401,
body: {
- message: 'Unauthorized',
+ message: e.message,
},
};
}
diff --git a/packages/lib/constants/time.ts b/packages/lib/constants/time.ts
index e2581e14c..c1a262048 100644
--- a/packages/lib/constants/time.ts
+++ b/packages/lib/constants/time.ts
@@ -3,3 +3,5 @@ export const ONE_MINUTE = ONE_SECOND * 60;
export const ONE_HOUR = ONE_MINUTE * 60;
export const ONE_DAY = ONE_HOUR * 24;
export const ONE_WEEK = ONE_DAY * 7;
+export const ONE_MONTH = ONE_DAY * 30;
+export const ONE_YEAR = ONE_DAY * 365;
diff --git a/packages/lib/server-only/public-api/create-api-token.ts b/packages/lib/server-only/public-api/create-api-token.ts
index 582053359..645e9fb1b 100644
--- a/packages/lib/server-only/public-api/create-api-token.ts
+++ b/packages/lib/server-only/public-api/create-api-token.ts
@@ -3,7 +3,7 @@ import crypto from 'crypto';
import { prisma } from '@documenso/prisma';
// temporary choice for testing only
-import { ONE_WEEK } from '../../constants/time';
+import { ONE_YEAR } from '../../constants/time';
type CreateApiTokenInput = {
userId: number;
@@ -22,7 +22,7 @@ export const createApiToken = async ({ userId, tokenName }: CreateApiTokenInput)
token: tokenHash,
name: tokenName,
userId,
- expires: new Date(Date.now() + ONE_WEEK),
+ expires: new Date(Date.now() + ONE_YEAR),
},
});
diff --git a/packages/lib/server-only/public-api/get-user-by-token.ts b/packages/lib/server-only/public-api/get-user-by-token.ts
index 3092deaa7..277fc13b2 100644
--- a/packages/lib/server-only/public-api/get-user-by-token.ts
+++ b/packages/lib/server-only/public-api/get-user-by-token.ts
@@ -1,7 +1,7 @@
import { prisma } from '@documenso/prisma';
export const checkUserFromToken = async ({ token }: { token: string }) => {
- const user = await prisma.user.findFirstOrThrow({
+ const user = await prisma.user.findFirst({
where: {
ApiToken: {
some: {
@@ -9,7 +9,20 @@ export const checkUserFromToken = async ({ token }: { token: string }) => {
},
},
},
+ include: {
+ ApiToken: true,
+ },
});
+ if (!user) {
+ throw new Error('Token not found');
+ }
+
+ const tokenObject = user.ApiToken.find((apiToken) => apiToken.token === token);
+
+ if (!tokenObject || new Date(tokenObject.expires) < new Date()) {
+ throw new Error('The API token has expired');
+ }
+
return user;
};