add: GitHub authentication

This commit is contained in:
Dominik Natter
2025-03-26 12:51:29 +01:00
parent b4b24410c2
commit 18ab55600b
12 changed files with 96 additions and 10275 deletions

View File

@@ -20,6 +20,7 @@
}, },
"dependencies": { "dependencies": {
"@payloadcms/admin-bar": "latest", "@payloadcms/admin-bar": "latest",
"@payloadcms/db-sqlite": "latest",
"@payloadcms/live-preview-react": "latest", "@payloadcms/live-preview-react": "latest",
"@payloadcms/next": "latest", "@payloadcms/next": "latest",
"@payloadcms/payload-cloud": "latest", "@payloadcms/payload-cloud": "latest",
@@ -34,6 +35,7 @@
"@radix-ui/react-label": "^2.0.2", "@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0", "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-slot": "^1.0.2",
"auth": "^1.2.3",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
@@ -41,16 +43,17 @@
"graphql": "^16.8.2", "graphql": "^16.8.2",
"lucide-react": "^0.378.0", "lucide-react": "^0.378.0",
"next": "15.2.3", "next": "15.2.3",
"next-auth": "5.0.0-beta.25",
"next-sitemap": "^4.2.3", "next-sitemap": "^4.2.3",
"payload": "latest", "payload": "latest",
"payload-authjs": "^0.7.1",
"prism-react-renderer": "^2.3.1", "prism-react-renderer": "^2.3.1",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
"react-hook-form": "7.45.4", "react-hook-form": "7.45.4",
"sharp": "0.32.6", "sharp": "0.32.6",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7"
"@payloadcms/db-sqlite": "latest"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.2.0", "@eslint/eslintrc": "^3.2.0",

10222
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

9
src/_middleware.ts Normal file
View File

@@ -0,0 +1,9 @@
import NextAuth from "next-auth";
import { authConfig } from "./auth.config";
const { auth: middleware } = NextAuth(authConfig);
export default middleware;
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

View File

@@ -0,0 +1,14 @@
import { signIn } from "@/auth";
export default function SignInButton() {
return (
<form
action={async () => {
"use server";
await signIn("github");
}}
>
<button type="submit">Sign In</button>
</form>
);
}

View File

@@ -20,6 +20,7 @@ import { LinkToDoc as LinkToDoc_aead06e4cbf6b2620c5c51c9ab283634 } from '@payloa
import { ReindexButton as ReindexButton_aead06e4cbf6b2620c5c51c9ab283634 } from '@payloadcms/plugin-search/client' import { ReindexButton as ReindexButton_aead06e4cbf6b2620c5c51c9ab283634 } from '@payloadcms/plugin-search/client'
import { RowLabel as RowLabel_ec255a65fa6fa8d1faeb09cf35284224 } from '@/Header/RowLabel' import { RowLabel as RowLabel_ec255a65fa6fa8d1faeb09cf35284224 } from '@/Header/RowLabel'
import { RowLabel as RowLabel_1f6ff6ff633e3695d348f4f3c58f1466 } from '@/Footer/RowLabel' import { RowLabel as RowLabel_1f6ff6ff633e3695d348f4f3c58f1466 } from '@/Footer/RowLabel'
import { SignInWithAuthjsButton as SignInWithAuthjsButton_06d0cb594d8f6ba2ac35015f930c882e } from 'payload-authjs/components'
import { default as default_1a7510af427896d367a49dbf838d2de6 } from '@/components/BeforeDashboard' import { default as default_1a7510af427896d367a49dbf838d2de6 } from '@/components/BeforeDashboard'
import { default as default_8a7ab0eb7ab5c511aba12e68480bfe5e } from '@/components/BeforeLogin' import { default as default_8a7ab0eb7ab5c511aba12e68480bfe5e } from '@/components/BeforeLogin'
@@ -46,6 +47,7 @@ export const importMap = {
"@payloadcms/plugin-search/client#ReindexButton": ReindexButton_aead06e4cbf6b2620c5c51c9ab283634, "@payloadcms/plugin-search/client#ReindexButton": ReindexButton_aead06e4cbf6b2620c5c51c9ab283634,
"@/Header/RowLabel#RowLabel": RowLabel_ec255a65fa6fa8d1faeb09cf35284224, "@/Header/RowLabel#RowLabel": RowLabel_ec255a65fa6fa8d1faeb09cf35284224,
"@/Footer/RowLabel#RowLabel": RowLabel_1f6ff6ff633e3695d348f4f3c58f1466, "@/Footer/RowLabel#RowLabel": RowLabel_1f6ff6ff633e3695d348f4f3c58f1466,
"payload-authjs/components#SignInWithAuthjsButton": SignInWithAuthjsButton_06d0cb594d8f6ba2ac35015f930c882e,
"@/components/BeforeDashboard#default": default_1a7510af427896d367a49dbf838d2de6, "@/components/BeforeDashboard#default": default_1a7510af427896d367a49dbf838d2de6,
"@/components/BeforeLogin#default": default_8a7ab0eb7ab5c511aba12e68480bfe5e "@/components/BeforeLogin#default": default_8a7ab0eb7ab5c511aba12e68480bfe5e
} }

View File

@@ -0,0 +1,2 @@
import { handlers } from "@/auth" // Referring to the auth.ts we just created
export const { GET, POST } = handlers

9
src/auth.config.ts Normal file
View File

@@ -0,0 +1,9 @@
import { NextAuthConfig } from "next-auth";
import github from "next-auth/providers/github";
export const authConfig: NextAuthConfig = {
providers: [github],
callbacks: {
authorized: ({ auth }) => !!auth,
},
};

10
src/auth.ts Normal file
View File

@@ -0,0 +1,10 @@
import payloadConfig from "@payload-config";
import NextAuth from "next-auth";
import { withPayload } from "payload-authjs";
import { authConfig } from "./auth.config";
export const { handlers, signIn, signOut, auth } = NextAuth(
withPayload(authConfig, {
payloadConfig,
}),
);

View File

@@ -1,26 +0,0 @@
import type { CollectionConfig } from 'payload'
import { authenticated } from '../../access/authenticated'
export const Users: CollectionConfig = {
slug: 'users',
access: {
admin: authenticated,
create: authenticated,
delete: authenticated,
read: authenticated,
update: authenticated,
},
admin: {
defaultColumns: ['name', 'email'],
useAsTitle: 'name',
},
auth: true,
fields: [
{
name: 'name',
type: 'text',
},
],
timestamps: true,
}

7
src/collections/users.ts Normal file
View File

@@ -0,0 +1,7 @@
// users.ts
import type { CollectionConfig } from 'payload'
const Users: CollectionConfig = {
slug: "users",
fields: [],
};

View File

@@ -167,7 +167,7 @@ export interface Paper {
}; };
authors: { authors: {
profilePicture: number | Media; profilePicture: number | Media;
user: number | User; user: string | User;
position: 'leader' | 'member'; position: 'leader' | 'member';
description: string; description: string;
id?: string | null; id?: string | null;
@@ -286,18 +286,21 @@ export interface Media {
* via the `definition` "users". * via the `definition` "users".
*/ */
export interface User { export interface User {
id: number; id: string;
email: string;
emailVerified?: string | null;
name?: string | null; name?: string | null;
image?: string | null;
accounts?:
| {
id?: string | null;
provider: string;
providerAccountId: string;
type: string;
}[]
| null;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
password?: string | null;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
@@ -399,7 +402,7 @@ export interface Post {
description?: string | null; description?: string | null;
}; };
publishedAt?: string | null; publishedAt?: string | null;
authors?: (number | User)[] | null; authors?: (string | User)[] | null;
populatedAuthors?: populatedAuthors?:
| { | {
id?: string | null; id?: string | null;
@@ -973,7 +976,7 @@ export interface PayloadLockedDocument {
} | null) } | null)
| ({ | ({
relationTo: 'users'; relationTo: 'users';
value: number | User; value: string | User;
} | null) } | null)
| ({ | ({
relationTo: 'redirects'; relationTo: 'redirects';
@@ -998,7 +1001,7 @@ export interface PayloadLockedDocument {
globalSlug?: string | null; globalSlug?: string | null;
user: { user: {
relationTo: 'users'; relationTo: 'users';
value: number | User; value: string | User;
}; };
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
@@ -1011,7 +1014,7 @@ export interface PayloadPreference {
id: number; id: number;
user: { user: {
relationTo: 'users'; relationTo: 'users';
value: number | User; value: string | User;
}; };
key?: string | null; key?: string | null;
value?: value?:
@@ -1367,16 +1370,21 @@ export interface CategoriesSelect<T extends boolean = true> {
* via the `definition` "users_select". * via the `definition` "users_select".
*/ */
export interface UsersSelect<T extends boolean = true> { export interface UsersSelect<T extends boolean = true> {
id?: T;
email?: T;
emailVerified?: T;
name?: T; name?: T;
image?: T;
accounts?:
| T
| {
id?: T;
provider?: T;
providerAccountId?: T;
type?: T;
};
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
@@ -1754,7 +1762,7 @@ export interface TaskSchedulePublish {
value: number | Post; value: number | Post;
} | null); } | null);
global?: string | null; global?: string | null;
user?: (number | null) | User; user?: (string | null) | User;
}; };
output?: unknown; output?: unknown;
} }

View File

@@ -10,7 +10,6 @@ import { Categories } from './collections/Categories'
import { Media } from './collections/Media' import { Media } from './collections/Media'
import { Pages } from './collections/Pages' import { Pages } from './collections/Pages'
import { Posts } from './collections/Posts' import { Posts } from './collections/Posts'
import { Users } from './collections/Users'
import { Footer } from './Footer/config' import { Footer } from './Footer/config'
import { Header } from './Header/config' import { Header } from './Header/config'
import { plugins } from './plugins' import { plugins } from './plugins'
@@ -18,12 +17,15 @@ import { defaultLexical } from '@/fields/defaultLexical'
import { getServerSideURL } from './utilities/getURL' import { getServerSideURL } from './utilities/getURL'
import { Papers } from '@/collections/Papers' import { Papers } from '@/collections/Papers'
import { Technologies } from '@/collections/Technologies' import { Technologies } from '@/collections/Technologies'
import { authjsPlugin } from 'payload-authjs'
import { authConfig } from '@/auth.config'
const filename = fileURLToPath(import.meta.url) const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
export default buildConfig({ export default buildConfig({
admin: { admin: {
user: "users",
components: { components: {
// The `BeforeLogin` component renders a message that you see while logging into your admin panel. // The `BeforeLogin` component renders a message that you see while logging into your admin panel.
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeLogin` statement on line 15. // Feel free to delete this at any time. Simply remove the line below and the import `BeforeLogin` statement on line 15.
@@ -35,7 +37,7 @@ export default buildConfig({
importMap: { importMap: {
baseDir: path.resolve(dirname), baseDir: path.resolve(dirname),
}, },
user: Users.slug,
livePreview: { livePreview: {
breakpoints: [ breakpoints: [
{ {
@@ -66,10 +68,13 @@ export default buildConfig({
url: process.env.DATABASE_URI || '', url: process.env.DATABASE_URI || '',
}, },
}), }),
collections: [Papers, Technologies, Pages, Posts, Media, Categories, Users], collections: [Papers, Technologies, Pages, Posts, Media, Categories],
cors: [getServerSideURL()].filter(Boolean), cors: [getServerSideURL()].filter(Boolean),
globals: [Header, Footer], globals: [Header, Footer],
plugins: [ plugins: [
authjsPlugin({
authjsConfig: authConfig,
}),
...plugins, ...plugins,
// storage-adapter-placeholder // storage-adapter-placeholder
], ],