finished access token creation feature
This commit is contained in:
parent
d91ebb3fa2
commit
05563134b4
|
@ -12,6 +12,8 @@ type Props = {
|
||||||
export default function NewKeyModal({ onClose }: Props) {
|
export default function NewKeyModal({ onClose }: Props) {
|
||||||
const { data } = useSession();
|
const { data } = useSession();
|
||||||
|
|
||||||
|
const [newToken, setNewToken] = useState("");
|
||||||
|
|
||||||
const initial = {
|
const initial = {
|
||||||
name: "",
|
name: "",
|
||||||
expires: 0 as KeyExpiry,
|
expires: 0 as KeyExpiry,
|
||||||
|
@ -43,7 +45,7 @@ export default function NewKeyModal({ onClose }: Props) {
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
toast.success(`Created!`);
|
toast.success(`Created!`);
|
||||||
onClose();
|
setNewToken(data.response);
|
||||||
} else toast.error(data.response as string);
|
} else toast.error(data.response as string);
|
||||||
|
|
||||||
setSubmitLoader(false);
|
setSubmitLoader(false);
|
||||||
|
@ -54,146 +56,173 @@ export default function NewKeyModal({ onClose }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal toggleModal={onClose}>
|
<Modal toggleModal={onClose}>
|
||||||
<p className="text-xl font-thin">Create an Access Token</p>
|
{newToken ? (
|
||||||
|
<div className="flex flex-col justify-center space-y-4">
|
||||||
<div className="divider mb-3 mt-1"></div>
|
<p className="text-xl font-thin">Access Token Created</p>
|
||||||
|
<p>
|
||||||
<div className="flex gap-2 items-center">
|
Your new token has been created. Please copy it and store it
|
||||||
<div className="w-full">
|
somewhere safe. You will not be able to see it again.
|
||||||
<p className="mb-2">Name</p>
|
</p>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
value={key.name}
|
spellCheck={false}
|
||||||
onChange={(e) => setKey({ ...key, name: e.target.value })}
|
value={newToken}
|
||||||
placeholder="e.g. For the Mobile App"
|
onChange={() => {}}
|
||||||
className="bg-base-200"
|
className="w-full"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(newToken);
|
||||||
|
toast.success("Copied to clipboard!");
|
||||||
|
}}
|
||||||
|
className="btn btn-primary w-fit mx-auto"
|
||||||
|
>
|
||||||
|
Copy to Clipboard
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="text-xl font-thin">Create an Access Token</p>
|
||||||
|
|
||||||
<div>
|
<div className="divider mb-3 mt-1"></div>
|
||||||
<p className="mb-2">Date of Expiry</p>
|
|
||||||
|
|
||||||
<div className="dropdown dropdown-bottom dropdown-end">
|
<div className="flex gap-2 items-center">
|
||||||
<div
|
<div className="w-full">
|
||||||
tabIndex={0}
|
<p className="mb-2">Name</p>
|
||||||
role="button"
|
|
||||||
className="btn btn-outline w-36 flex items-center btn-sm h-10"
|
<TextInput
|
||||||
>
|
value={key.name}
|
||||||
{key.expires === KeyExpiry.sevenDays && "7 Days"}
|
onChange={(e) => setKey({ ...key, name: e.target.value })}
|
||||||
{key.expires === KeyExpiry.oneMonth && "30 Days"}
|
placeholder="e.g. For the iOS shortcut"
|
||||||
{key.expires === KeyExpiry.twoMonths && "60 Days"}
|
className="bg-base-200"
|
||||||
{key.expires === KeyExpiry.threeMonths && "90 Days"}
|
/>
|
||||||
{key.expires === KeyExpiry.never && "No Expiration"}
|
|
||||||
</div>
|
</div>
|
||||||
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl w-52 mt-1">
|
|
||||||
<li>
|
|
||||||
<label
|
|
||||||
className="label cursor-pointer flex justify-start"
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="sort-radio"
|
|
||||||
className="radio checked:bg-primary"
|
|
||||||
checked={key.expires === KeyExpiry.sevenDays}
|
|
||||||
onChange={() => {
|
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
|
||||||
setKey({ ...key, expires: KeyExpiry.sevenDays });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="label-text">7 Days</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label
|
|
||||||
className="label cursor-pointer flex justify-start"
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="sort-radio"
|
|
||||||
className="radio checked:bg-primary"
|
|
||||||
checked={key.expires === KeyExpiry.oneMonth}
|
|
||||||
onChange={() => {
|
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
|
||||||
setKey({ ...key, expires: KeyExpiry.oneMonth });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="label-text">30 Days</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label
|
|
||||||
className="label cursor-pointer flex justify-start"
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="sort-radio"
|
|
||||||
className="radio checked:bg-primary"
|
|
||||||
checked={key.expires === KeyExpiry.twoMonths}
|
|
||||||
onChange={() => {
|
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
|
||||||
setKey({ ...key, expires: KeyExpiry.twoMonths });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="label-text">60 Days</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label
|
|
||||||
className="label cursor-pointer flex justify-start"
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="sort-radio"
|
|
||||||
className="radio checked:bg-primary"
|
|
||||||
checked={key.expires === KeyExpiry.threeMonths}
|
|
||||||
onChange={() => {
|
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
|
||||||
setKey({ ...key, expires: KeyExpiry.threeMonths });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="label-text">90 Days</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label
|
|
||||||
className="label cursor-pointer flex justify-start"
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="sort-radio"
|
|
||||||
className="radio checked:bg-primary"
|
|
||||||
checked={key.expires === KeyExpiry.never}
|
|
||||||
onChange={() => {
|
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
|
||||||
setKey({ ...key, expires: KeyExpiry.never });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="label-text">No Expiration</span>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between items-center mt-5">
|
<div>
|
||||||
<button
|
<p className="mb-2">Expires in</p>
|
||||||
className="btn btn-accent dark:border-violet-400 text-white"
|
|
||||||
onClick={submit}
|
<div className="dropdown dropdown-bottom dropdown-end">
|
||||||
>
|
<div
|
||||||
Create Access Token
|
tabIndex={0}
|
||||||
</button>
|
role="button"
|
||||||
</div>
|
className="btn btn-outline w-36 flex items-center btn-sm h-10"
|
||||||
|
>
|
||||||
|
{key.expires === KeyExpiry.sevenDays && "7 Days"}
|
||||||
|
{key.expires === KeyExpiry.oneMonth && "30 Days"}
|
||||||
|
{key.expires === KeyExpiry.twoMonths && "60 Days"}
|
||||||
|
{key.expires === KeyExpiry.threeMonths && "90 Days"}
|
||||||
|
{key.expires === KeyExpiry.never && "No Expiration"}
|
||||||
|
</div>
|
||||||
|
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl w-52 mt-1">
|
||||||
|
<li>
|
||||||
|
<label
|
||||||
|
className="label cursor-pointer flex justify-start"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="sort-radio"
|
||||||
|
className="radio checked:bg-primary"
|
||||||
|
checked={key.expires === KeyExpiry.sevenDays}
|
||||||
|
onChange={() => {
|
||||||
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
|
setKey({ ...key, expires: KeyExpiry.sevenDays });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="label-text">7 Days</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label
|
||||||
|
className="label cursor-pointer flex justify-start"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="sort-radio"
|
||||||
|
className="radio checked:bg-primary"
|
||||||
|
checked={key.expires === KeyExpiry.oneMonth}
|
||||||
|
onChange={() => {
|
||||||
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
|
setKey({ ...key, expires: KeyExpiry.oneMonth });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="label-text">30 Days</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label
|
||||||
|
className="label cursor-pointer flex justify-start"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="sort-radio"
|
||||||
|
className="radio checked:bg-primary"
|
||||||
|
checked={key.expires === KeyExpiry.twoMonths}
|
||||||
|
onChange={() => {
|
||||||
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
|
setKey({ ...key, expires: KeyExpiry.twoMonths });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="label-text">60 Days</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label
|
||||||
|
className="label cursor-pointer flex justify-start"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="sort-radio"
|
||||||
|
className="radio checked:bg-primary"
|
||||||
|
checked={key.expires === KeyExpiry.threeMonths}
|
||||||
|
onChange={() => {
|
||||||
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
|
setKey({ ...key, expires: KeyExpiry.threeMonths });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="label-text">90 Days</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label
|
||||||
|
className="label cursor-pointer flex justify-start"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="sort-radio"
|
||||||
|
className="radio checked:bg-primary"
|
||||||
|
checked={key.expires === KeyExpiry.never}
|
||||||
|
onChange={() => {
|
||||||
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
|
setKey({ ...key, expires: KeyExpiry.never });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="label-text">No Expiration</span>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center mt-5">
|
||||||
|
<button
|
||||||
|
className="btn btn-accent dark:border-violet-400 text-white"
|
||||||
|
onClick={submit}
|
||||||
|
>
|
||||||
|
Create Access Token
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ type Props = {
|
||||||
onChange: ChangeEventHandler<HTMLInputElement>;
|
onChange: ChangeEventHandler<HTMLInputElement>;
|
||||||
onKeyDown?: KeyboardEventHandler<HTMLInputElement> | undefined;
|
onKeyDown?: KeyboardEventHandler<HTMLInputElement> | undefined;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
spellCheck?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TextInput({
|
export default function TextInput({
|
||||||
|
@ -18,9 +19,11 @@ export default function TextInput({
|
||||||
onChange,
|
onChange,
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
className,
|
className,
|
||||||
|
spellCheck,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
|
spellCheck={spellCheck}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
type={type ? type : "text"}
|
type={type ? type : "text"}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { prisma } from "@/lib/api/db";
|
import { prisma } from "@/lib/api/db";
|
||||||
import { KeyExpiry } from "@/types/global";
|
import { KeyExpiry } from "@/types/global";
|
||||||
import bcrypt from "bcrypt";
|
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
import { decode, encode, getToken } from "next-auth/jwt";
|
||||||
|
|
||||||
export default async function postToken(
|
export default async function postToken(
|
||||||
body: {
|
body: {
|
||||||
|
@ -34,44 +34,55 @@ export default async function postToken(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
let expiryDate = new Date();
|
let expiryDate = new Date();
|
||||||
|
const oneDayInSeconds = 86400;
|
||||||
|
let expiryDateSecond = 7 * oneDayInSeconds;
|
||||||
|
|
||||||
switch (body.expires) {
|
if (body.expires === KeyExpiry.oneMonth) {
|
||||||
case KeyExpiry.sevenDays:
|
expiryDate.setDate(expiryDate.getDate() + 30);
|
||||||
expiryDate.setDate(expiryDate.getDate() + 7);
|
expiryDateSecond = 30 * oneDayInSeconds;
|
||||||
break;
|
} else if (body.expires === KeyExpiry.twoMonths) {
|
||||||
case KeyExpiry.oneMonth:
|
expiryDate.setDate(expiryDate.getDate() + 60);
|
||||||
expiryDate.setDate(expiryDate.getDate() + 30);
|
expiryDateSecond = 60 * oneDayInSeconds;
|
||||||
break;
|
} else if (body.expires === KeyExpiry.threeMonths) {
|
||||||
case KeyExpiry.twoMonths:
|
expiryDate.setDate(expiryDate.getDate() + 90);
|
||||||
expiryDate.setDate(expiryDate.getDate() + 60);
|
expiryDateSecond = 90 * oneDayInSeconds;
|
||||||
break;
|
} else if (body.expires === KeyExpiry.never) {
|
||||||
case KeyExpiry.threeMonths:
|
expiryDate.setDate(expiryDate.getDate() + 73000); // 200 years (not really never)
|
||||||
expiryDate.setDate(expiryDate.getDate() + 90);
|
expiryDateSecond = 73050 * oneDayInSeconds;
|
||||||
break;
|
} else {
|
||||||
case KeyExpiry.never:
|
expiryDate.setDate(expiryDate.getDate() + 7);
|
||||||
expiryDate.setDate(expiryDate.getDate() + 73000); // 200 years (not really never)
|
expiryDateSecond = 7 * oneDayInSeconds;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
expiryDate.setDate(expiryDate.getDate() + 7);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const saltRounds = 10;
|
const token = await encode({
|
||||||
|
token: {
|
||||||
|
id: userId,
|
||||||
|
iat: now / 1000,
|
||||||
|
exp: (expiryDate as any) / 1000,
|
||||||
|
jti: crypto.randomUUID(),
|
||||||
|
},
|
||||||
|
maxAge: expiryDateSecond || 604800,
|
||||||
|
secret: process.env.NEXTAUTH_SECRET,
|
||||||
|
});
|
||||||
|
|
||||||
const hashedKey = bcrypt.hashSync(crypto.randomUUID(), saltRounds);
|
const tokenBody = await decode({
|
||||||
|
token,
|
||||||
|
secret: process.env.NEXTAUTH_SECRET,
|
||||||
|
});
|
||||||
|
|
||||||
const createToken = await prisma.apiKey.create({
|
const createToken = await prisma.apiKey.create({
|
||||||
data: {
|
data: {
|
||||||
name: body.name,
|
name: body.name,
|
||||||
userId,
|
userId,
|
||||||
token: hashedKey,
|
token: tokenBody?.jti as string,
|
||||||
expires: expiryDate,
|
expires: expiryDate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: createToken.token,
|
response: token,
|
||||||
status: 200,
|
status: 200,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,13 @@ export default async function verifyUser({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (token.exp < Date.now() / 1000) {
|
||||||
|
res
|
||||||
|
.status(401)
|
||||||
|
.json({ response: "Your session has expired, please log in again." });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: userId,
|
id: userId,
|
||||||
|
|
|
@ -65,6 +65,7 @@ import ZitadelProvider from "next-auth/providers/zitadel";
|
||||||
import ZohoProvider from "next-auth/providers/zoho";
|
import ZohoProvider from "next-auth/providers/zoho";
|
||||||
import ZoomProvider from "next-auth/providers/zoom";
|
import ZoomProvider from "next-auth/providers/zoom";
|
||||||
import * as process from "process";
|
import * as process from "process";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
const emailEnabled =
|
const emailEnabled =
|
||||||
process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false;
|
process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false;
|
||||||
|
@ -1059,60 +1060,60 @@ if (process.env.NEXT_PUBLIC_ZOOM_ENABLED_ENABLED === "true") {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const authOptions: AuthOptions = {
|
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
||||||
adapter: adapter as Adapter,
|
return await NextAuth(req, res, {
|
||||||
session: {
|
adapter: adapter as Adapter,
|
||||||
strategy: "jwt",
|
session: {
|
||||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
strategy: "jwt",
|
||||||
},
|
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||||
providers,
|
},
|
||||||
pages: {
|
providers,
|
||||||
signIn: "/login",
|
pages: {
|
||||||
verifyRequest: "/confirmation",
|
signIn: "/login",
|
||||||
},
|
verifyRequest: "/confirmation",
|
||||||
callbacks: {
|
},
|
||||||
async signIn({ user, account, profile, email, credentials }) {
|
callbacks: {
|
||||||
if (account?.provider !== "credentials") {
|
async signIn({ user, account, profile, email, credentials }) {
|
||||||
// registration via SSO can be separately disabled
|
if (account?.provider !== "credentials") {
|
||||||
const existingUser = await prisma.account.findFirst({
|
// registration via SSO can be separately disabled
|
||||||
where: {
|
const existingUser = await prisma.account.findFirst({
|
||||||
providerAccountId: account?.providerAccountId,
|
where: {
|
||||||
},
|
providerAccountId: account?.providerAccountId,
|
||||||
});
|
},
|
||||||
if (existingUser && newSsoUsersDisabled) {
|
});
|
||||||
return false;
|
if (existingUser && newSsoUsersDisabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
return true;
|
},
|
||||||
},
|
async jwt({ token, trigger, user }) {
|
||||||
async jwt({ token, trigger, user }) {
|
token.sub = token.sub ? Number(token.sub) : undefined;
|
||||||
token.sub = token.sub ? Number(token.sub) : undefined;
|
if (trigger === "signIn" || trigger === "signUp")
|
||||||
if (trigger === "signIn" || trigger === "signUp")
|
token.id = user?.id as number;
|
||||||
token.id = user?.id as number;
|
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
},
|
},
|
||||||
async session({ session, token }) {
|
async session({ session, token }) {
|
||||||
session.user.id = token.id;
|
session.user.id = token.id;
|
||||||
|
|
||||||
if (STRIPE_SECRET_KEY) {
|
if (STRIPE_SECRET_KEY) {
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: token.id,
|
id: token.id,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
subscriptions: true,
|
subscriptions: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
const subscribedUser = await verifySubscription(user);
|
const subscribedUser = await verifySubscription(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NextAuth(authOptions);
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ export default async function users(req: NextApiRequest, res: NextApiResponse) {
|
||||||
return res.status(401).json({ response: "You must be logged in." });
|
return res.status(401).json({ response: "You must be logged in." });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (token.exp < Date.now() / 1000) {
|
||||||
|
return res
|
||||||
|
.status(401)
|
||||||
|
.json({ response: "Your session has expired, please log in again." });
|
||||||
|
}
|
||||||
|
|
||||||
if (userId !== Number(req.query.id))
|
if (userId !== Number(req.query.id))
|
||||||
return res.status(401).json({ response: "Permission denied." });
|
return res.status(401).json({ response: "Permission denied." });
|
||||||
|
|
||||||
|
|
Ŝarĝante…
Reference in New Issue