mirror of
https://github.com/DerTyp7/f1r3wave-website.git
synced 2025-10-28 12:32:08 +01:00
Removed next-auth and refactored authentication
This commit is contained in:
143
package-lock.json
generated
143
package-lock.json
generated
@@ -13,8 +13,8 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.1",
|
||||
"@fortawesome/react-fontawesome": "^3.0.2",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"iron-session": "^8.0.4",
|
||||
"next": "^15.5.4",
|
||||
"next-auth": "^5.0.0-beta.29",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
@@ -40,35 +40,6 @@
|
||||
"typescript": "^5"
|
||||
}
|
||||
},
|
||||
"node_modules/@auth/core": {
|
||||
"version": "0.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@auth/core/-/core-0.40.0.tgz",
|
||||
"integrity": "sha512-n53uJE0RH5SqZ7N1xZoMKekbHfQgjd0sAEyUbE+IYJnmuQkbvuZnXItCU7d+i7Fj8VGOgqvNO7Mw4YfBTlZeQw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@panva/hkdf": "^1.2.1",
|
||||
"jose": "^6.0.6",
|
||||
"oauth4webapi": "^3.3.0",
|
||||
"preact": "10.24.3",
|
||||
"preact-render-to-string": "6.5.11"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@simplewebauthn/browser": "^9.0.1",
|
||||
"@simplewebauthn/server": "^9.0.2",
|
||||
"nodemailer": "^6.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@simplewebauthn/browser": {
|
||||
"optional": true
|
||||
},
|
||||
"@simplewebauthn/server": {
|
||||
"optional": true
|
||||
},
|
||||
"nodemailer": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
|
||||
@@ -1043,15 +1014,6 @@
|
||||
"node": ">=12.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@panva/hkdf": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz",
|
||||
"integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
|
||||
@@ -2560,6 +2522,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -3963,6 +3934,30 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/iron-session": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/iron-session/-/iron-session-8.0.4.tgz",
|
||||
"integrity": "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/vvo",
|
||||
"https://github.com/sponsors/brc-dd"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^0.7.2",
|
||||
"iron-webcrypto": "^1.2.1",
|
||||
"uncrypto": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/iron-webcrypto": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
|
||||
"integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/brc-dd"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||
@@ -4413,15 +4408,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz",
|
||||
"integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -4806,33 +4792,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-auth": {
|
||||
"version": "5.0.0-beta.29",
|
||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.29.tgz",
|
||||
"integrity": "sha512-Ukpnuk3NMc/LiOl32njZPySk7pABEzbjhMUFd5/n10I0ZNC7NCuVv8IY2JgbDek2t/PUOifQEoUiOOTLy4os5A==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@auth/core": "0.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@simplewebauthn/browser": "^9.0.1",
|
||||
"@simplewebauthn/server": "^9.0.2",
|
||||
"next": "^14.0.0-0 || ^15.0.0-0",
|
||||
"nodemailer": "^6.6.5",
|
||||
"react": "^18.2.0 || ^19.0.0-0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@simplewebauthn/browser": {
|
||||
"optional": true
|
||||
},
|
||||
"@simplewebauthn/server": {
|
||||
"optional": true
|
||||
},
|
||||
"nodemailer": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
@@ -4840,15 +4799,6 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/oauth4webapi": {
|
||||
"version": "3.8.1",
|
||||
"resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.1.tgz",
|
||||
"integrity": "sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -5166,25 +5116,6 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.24.3",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz",
|
||||
"integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/preact-render-to-string": {
|
||||
"version": "6.5.11",
|
||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz",
|
||||
"integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"preact": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -6673,6 +6604,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/uncrypto": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
|
||||
"integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.1",
|
||||
"@fortawesome/react-fontawesome": "^3.0.2",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"iron-session": "^8.0.4",
|
||||
"next": "^15.5.4",
|
||||
"next-auth": "^5.0.0-beta.29",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import ImageManager from '@/components/ImageManager';
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
import { ImagesResponse, TagsResponse } from '@/interfaces/api';
|
||||
import { getAuthStatus } from '@/lib/auth-utils';
|
||||
import { getSession } from '@/lib/session';
|
||||
import styles from '@/styles/AdminPage.module.scss';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default async function AdminPage() {
|
||||
const { isAuthenticated } = await getAuthStatus();
|
||||
const session = await getSession();
|
||||
|
||||
if (!isAuthenticated) {
|
||||
if (!session.isAuthenticated) {
|
||||
redirect('/login');
|
||||
}
|
||||
|
||||
|
||||
15
src/app/api/auth/login/route.ts
Normal file
15
src/app/api/auth/login/route.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { getSession } from '@/lib/session';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const session = await getSession();
|
||||
const { password } = await request.json();
|
||||
|
||||
if (password === process.env.ADMIN_TOKEN) {
|
||||
session.isAuthenticated = true;
|
||||
await session.save();
|
||||
return NextResponse.json({ success: true });
|
||||
} else {
|
||||
return NextResponse.json({ error: 'Invalid password' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
10
src/app/api/auth/logout/route.ts
Normal file
10
src/app/api/auth/logout/route.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { getSession } from '@/lib/session';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function POST() {
|
||||
const session = await getSession();
|
||||
session.isAuthenticated = false;
|
||||
await session.save();
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
7
src/app/api/auth/status/route.ts
Normal file
7
src/app/api/auth/status/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { getSession } from '@/lib/session';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function GET() {
|
||||
const session = await getSession();
|
||||
return NextResponse.json({ isAuthenticated: session.isAuthenticated });
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { isAuthenticated } from '@/lib/auth-utils';
|
||||
import { deleteImageById } from '@/lib/data';
|
||||
import { getSession } from '@/lib/session';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function DELETE(request: NextRequest, context: { params: Promise<{ id: string }> }) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
const session = await getSession();
|
||||
|
||||
if (!session.isAuthenticated) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { imagesDir } from '@/const/api';
|
||||
import { ImagesResponse } from '@/interfaces/api';
|
||||
import { ImageMeta } from '@/interfaces/image';
|
||||
import { isAuthenticated } from '@/lib/auth-utils';
|
||||
import { addImage, getImageData, stringToTags } from '@/lib/data';
|
||||
import { getSession } from '@/lib/session';
|
||||
import { promises as fs } from 'fs';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@@ -52,8 +53,9 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
console.log('upload file', request);
|
||||
if (!(await isAuthenticated(request))) {
|
||||
const session = await getSession();
|
||||
|
||||
if (!session.isAuthenticated) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
@@ -83,6 +85,8 @@ export async function POST(request: NextRequest) {
|
||||
await fs.mkdir(imagesDir, { recursive: true });
|
||||
}
|
||||
|
||||
await fs.writeFile(path.join(imagesDir, uuidFilename), buffer);
|
||||
|
||||
const imageInfo = await sharp(buffer).metadata();
|
||||
|
||||
const newImage: ImageMeta = {
|
||||
@@ -95,8 +99,9 @@ export async function POST(request: NextRequest) {
|
||||
};
|
||||
|
||||
await addImage(newImage);
|
||||
return NextResponse.json({ message: 'File deleted successfully' }, { status: 201 });
|
||||
return NextResponse.json({ message: 'File uploaded successfully' }, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json({ error: error }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { isAuthenticated } from '@/lib/auth-utils';
|
||||
import { stringToTags, updateTagsOfImageId } from '@/lib/data';
|
||||
import { getSession } from '@/lib/session';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function PUT(request: NextRequest, context: { params: Promise<{ imageId: string }> }) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
const session = await getSession();
|
||||
|
||||
if (!session.isAuthenticated) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import LoginForm from '@/components/LoginForm';
|
||||
import { getAuthStatus } from '@/lib/auth-utils';
|
||||
import { getSession } from '@/lib/session';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default async function LoginPage() {
|
||||
const { isAuthenticated } = await getAuthStatus();
|
||||
const session = await getSession();
|
||||
|
||||
if (isAuthenticated) {
|
||||
if (session.isAuthenticated) {
|
||||
redirect('/admin');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { NextAuthConfig } from 'next-auth';
|
||||
|
||||
export const authConfig = {
|
||||
pages: {
|
||||
signIn: '/login',
|
||||
},
|
||||
callbacks: {
|
||||
authorized({ auth, request: { nextUrl } }) {
|
||||
const isLoggedIn = !!auth?.user;
|
||||
|
||||
const isOnAdminRoute = nextUrl.pathname.startsWith('/admin');
|
||||
|
||||
if (isOnAdminRoute) {
|
||||
if (isLoggedIn) return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
} satisfies NextAuthConfig;
|
||||
30
src/auth.ts
30
src/auth.ts
@@ -1,30 +0,0 @@
|
||||
import { validateAdminToken } from '@/lib/auth-utils';
|
||||
import NextAuth from 'next-auth';
|
||||
import Credentials from 'next-auth/providers/credentials';
|
||||
import { z } from 'zod';
|
||||
import { authConfig } from './auth.config';
|
||||
|
||||
export const { auth, signIn, signOut } = NextAuth({
|
||||
...authConfig,
|
||||
providers: [
|
||||
Credentials({
|
||||
async authorize(credentials) {
|
||||
const parsedCredentials = z.object({ token: z.string().min(1) }).safeParse(credentials);
|
||||
if (parsedCredentials.success) {
|
||||
const { token } = parsedCredentials.data;
|
||||
const isValidToken = await validateAdminToken(token);
|
||||
|
||||
if (isValidToken) {
|
||||
return {
|
||||
id: 'admin-id',
|
||||
name: 'Administrator',
|
||||
role: 'admin',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -1,30 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import logo from '@/assets/logo_text.png';
|
||||
import { performLogout } from '@/lib/actions';
|
||||
import { getAuthStatus } from '@/lib/auth-utils';
|
||||
import { deleteSessionCookie } from '@/lib/actions';
|
||||
import styles from '@/styles/Header.module.scss';
|
||||
import { faInstagram } from '@fortawesome/free-brands-svg-icons';
|
||||
import { faEnvelope } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function Header() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const [isAuth, setIsAuth] = useState<boolean>(false);
|
||||
const isActive = (path: string) => pathname.split('/')[1] === path.split('/')[1];
|
||||
|
||||
const performLogout = async (): Promise<void> => {
|
||||
await fetch('/api/auth/logout', { method: 'POST' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const auth = async () => {
|
||||
const { isAuthenticated } = await getAuthStatus();
|
||||
const result = await fetch('/api/auth/status', { method: 'GET' });
|
||||
const isAuthenticated = (await result.json()).isAuthenticated;
|
||||
|
||||
if (!isAuthenticated) {
|
||||
await deleteSessionCookie();
|
||||
}
|
||||
|
||||
setIsAuth(isAuthenticated);
|
||||
};
|
||||
auth();
|
||||
}, [pathname]);
|
||||
|
||||
const handleLogout = async () => {
|
||||
await performLogout();
|
||||
setIsAuth(false);
|
||||
router.push('/');
|
||||
router.refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<header className={`${styles.header}`}>
|
||||
<Link href="/" className={styles.logo}>
|
||||
@@ -41,7 +58,7 @@ export default function Header() {
|
||||
{isAuth ? (
|
||||
<p
|
||||
onClick={async () => {
|
||||
performLogout();
|
||||
handleLogout();
|
||||
}}
|
||||
className={`${styles.navLink}`}>
|
||||
Logout
|
||||
|
||||
@@ -2,21 +2,45 @@
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import InputField from '@/components/InputField';
|
||||
import { authenticate } from '@/lib/actions';
|
||||
import styles from '@/styles/LoginForm.module.scss';
|
||||
import { useActionState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function LoginForm() {
|
||||
const [errorMessage, dispatch, isPending] = useActionState(authenticate, undefined);
|
||||
const [isPending, setIsPending] = useState<boolean>(false);
|
||||
const [password, setPassword] = useState<string>('');
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const router = useRouter();
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsPending(true);
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ password }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
router.push('/admin');
|
||||
router.refresh();
|
||||
} else {
|
||||
setErrorMessage((await response.json()).error ?? '');
|
||||
setIsPending(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<form action={dispatch} className={styles.form}>
|
||||
<form onSubmit={handleSubmit} className={styles.form}>
|
||||
<span className={styles.errorMessage}>{errorMessage}</span>
|
||||
<InputField
|
||||
label="Token"
|
||||
name="token"
|
||||
type="password"
|
||||
onValueChange={(v) => {
|
||||
setPassword(v.currentTarget.value);
|
||||
}}
|
||||
required={true}
|
||||
placeholder="Enter your token"
|
||||
disabled={isPending}
|
||||
|
||||
@@ -1,35 +1,8 @@
|
||||
'use server';
|
||||
|
||||
import { signIn, signOut } from '@/auth';
|
||||
import { AuthError } from 'next-auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
export async function authenticate(_prevState: string | undefined, formData: FormData): Promise<string> {
|
||||
try {
|
||||
const result = await signIn('credentials', {
|
||||
redirect: false,
|
||||
token: formData.get('token'),
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
return 'Invalid token';
|
||||
}
|
||||
|
||||
redirect('/admin');
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
switch (error.type) {
|
||||
case 'CredentialsSignin':
|
||||
return 'Invalid token';
|
||||
default:
|
||||
return 'Something went wrong';
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function performLogout() {
|
||||
await signOut({ redirectTo: '/', redirect: true });
|
||||
redirect('/');
|
||||
export async function deleteSessionCookie() {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.delete('session');
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
'use server';
|
||||
|
||||
import { getToken } from 'next-auth/jwt';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
export async function validateAdminToken(token: string): Promise<boolean> {
|
||||
return token === process.env.ADMIN_TOKEN;
|
||||
}
|
||||
|
||||
export async function getAuthStatus() {
|
||||
const { auth } = await import('@/auth');
|
||||
const session = await auth();
|
||||
|
||||
return {
|
||||
isAuthenticated: !!session?.user,
|
||||
user: session?.user,
|
||||
};
|
||||
}
|
||||
|
||||
export async function isAuthenticated(request: NextRequest): Promise<boolean> {
|
||||
console.log('isAuthenticated');
|
||||
const token = await getToken({ req: request, secret: process.env.AUTH_SECRET });
|
||||
console.log('token', token);
|
||||
if (!token || (token.exp && Date.now() / 1000 > token.exp)) {
|
||||
console.log('false');
|
||||
return false;
|
||||
}
|
||||
console.log('true');
|
||||
return true;
|
||||
}
|
||||
24
src/lib/session.ts
Normal file
24
src/lib/session.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { getIronSession, SessionOptions } from 'iron-session';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
export interface SessionData {
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
|
||||
export const sessionOptions: SessionOptions = {
|
||||
password: process.env.AUTH_SECRET!,
|
||||
cookieName: 'session',
|
||||
cookieOptions: {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
},
|
||||
};
|
||||
|
||||
export async function getSession() {
|
||||
const session = await getIronSession<SessionData>(await cookies(), sessionOptions);
|
||||
if (!session.isAuthenticated) {
|
||||
session.isAuthenticated = false;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import NextAuth from 'next-auth';
|
||||
import { authConfig } from './auth.config';
|
||||
|
||||
export default NextAuth(authConfig).auth;
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
|
||||
};
|
||||
Reference in New Issue
Block a user