mirror of
https://github.com/michivonah/nextjs.git
synced 2025-12-22 22:16:28 +01:00
add authentication with next-auth
This commit is contained in:
parent
5487bccd8d
commit
759acde045
11 changed files with 318 additions and 98 deletions
125
README.md
125
README.md
|
|
@ -50,6 +50,14 @@ npm run dev
|
|||
Afterwards the site can be accessed in the browser at `http://localhost:3000`.
|
||||

|
||||
|
||||
## pnpm
|
||||
`pnpm` is a faster and more efficient packet manager for `Node.js` than `npm`. It is recommended by `Next.js`.
|
||||
|
||||
Install `pnpm`
|
||||
```zsh
|
||||
npm install -g pnpm
|
||||
```
|
||||
|
||||
|
||||
## General structure
|
||||
- The whole app is organzied in multiple folders. THe typescript files for the app are stored in `src/app`.
|
||||
|
|
@ -662,6 +670,123 @@ if(!invoice){
|
|||
Therefor the file `not-found.tsx` should be created with the UI you want to show, when something is not found.
|
||||
Thoose specific error pages will be shown before the general error page.
|
||||
|
||||
## Authentication
|
||||
`NextAuth.js` or `Auth.js` is a popular library for implementing authentication into a `Next.js` application. This library takes away the most complexitiy and effort in building a robust authentication system from scratch.
|
||||
|
||||
Install `Auth.js`:
|
||||
```tsx
|
||||
pnpm i next-auth@beta
|
||||
```
|
||||
|
||||
Before using `Auth.js` it's required to generate a secret. This can be done with following command:
|
||||
```zsh
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
Than this values has to be added to the `.env`-File as `AUTH_SECRET=<SECRET>`.
|
||||
|
||||
The secret will be used to encrypt cookies for the user sessions.
|
||||
|
||||
To configure the authentication a file `auth.config.ts` has to be created in the root directory of the app.
|
||||
This file has to export a object with the config.
|
||||
|
||||
Example:
|
||||
```ts
|
||||
import type { NextAuthConfig } from 'next-auth';
|
||||
|
||||
export const authConfig = {
|
||||
pages: {
|
||||
signIn: '/login',
|
||||
},
|
||||
} satisfies NextAuthConfig;
|
||||
```
|
||||
|
||||
But now the app routes aren't protect and everyone can access them. This can be changed by adding callbacks to `auth.config.ts` which control the pages that need authorization.
|
||||
```ts
|
||||
import type { NextAuthConfig } from 'next-auth';
|
||||
|
||||
export const authConfig = {
|
||||
pages: {
|
||||
signIn: '/login',
|
||||
},
|
||||
callbacks: {
|
||||
authorized({ auth, request: { nextUrl } }) {
|
||||
const isLoggedIn = !!auth?.user;
|
||||
const isOnDashboard = nextUrl.pathname.startsWith('/dashboard');
|
||||
if (isOnDashboard) {
|
||||
if (isLoggedIn) return true;
|
||||
return false;
|
||||
} else if (isLoggedIn) {
|
||||
return Response.redirect(new URL('/dashboard', nextUrl));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
} satisfies NextAuthConfig;
|
||||
```
|
||||
|
||||
Next a middleware has to be created. This is defined in a file `middleware.ts` in the project's root.
|
||||
```ts
|
||||
import NextAuth from 'next-auth';
|
||||
import { authConfig } from './auth.config';
|
||||
|
||||
export default NextAuth(authConfig).auth;
|
||||
|
||||
export const config = {
|
||||
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
|
||||
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
|
||||
};
|
||||
```
|
||||
|
||||
Additonal a `auth.ts` file is needed. This could look like this:
|
||||
```ts
|
||||
import NextAuth from 'next-auth';
|
||||
import { authConfig } from './auth.config';
|
||||
import Credentials from 'next-auth/providers/credentials';
|
||||
import { z } from 'zod';
|
||||
import type { User } from '@/app/lib/definitions';
|
||||
import bcrypt from 'bcrypt';
|
||||
import postgres from 'postgres';
|
||||
|
||||
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
|
||||
|
||||
async function getUser(email: string): Promise<User | undefined> {
|
||||
try{
|
||||
const user = await sql<User[]>`SELECT * FROM users WHERE email=${email}`;
|
||||
return user[0];
|
||||
} catch(error){
|
||||
console.error('Failed to fetch user:', error);
|
||||
throw new Error('Failed to fetch user.');
|
||||
}
|
||||
}
|
||||
|
||||
export const { auth, signIn, signOut } = NextAuth({
|
||||
...authConfig,
|
||||
providers: [Credentials({
|
||||
async authorize(credentials){
|
||||
const parsedCredentials = z
|
||||
.object({ email: z.string().email(), password: z.string().min(6)})
|
||||
.safeParse(credentials);
|
||||
|
||||
if(parsedCredentials.success){
|
||||
const { email, password } = parsedCredentials.data;
|
||||
const user = await getUser(email);
|
||||
if(!user) return null;
|
||||
const passwordsMatch = await bcrypt.compare(password, user.password);
|
||||
|
||||
if(passwordsMatch) return user;
|
||||
}
|
||||
|
||||
console.log('Invalid credentials');
|
||||
return null;
|
||||
},
|
||||
})],
|
||||
});
|
||||
````
|
||||
|
||||
More details about the authentication can be found in the [Next.js course](https://nextjs.org/learn/dashboard-app/adding-authentication).
|
||||
|
||||
|
||||
## Ressources
|
||||
- [Next.js Installation](https://nextjs.org/docs/app/getting-started/installation)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,30 @@ import { z } from 'zod';
|
|||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
import postgres from 'postgres';
|
||||
import { signIn } from '@/auth';
|
||||
import { AuthError } from 'next-auth';
|
||||
|
||||
const sql = postgres(process.env.POSTGRES_URL!, {ssl: 'require'});
|
||||
|
||||
export async function authenticate(
|
||||
prevState: string | undefined,
|
||||
formData: FormData,
|
||||
){
|
||||
try{
|
||||
await signIn('credentials', formData)
|
||||
} catch(error){
|
||||
if(error instanceof AuthError){
|
||||
switch(error.type){
|
||||
case 'CredentialsSignIn':
|
||||
return 'Invalid credentials.';
|
||||
default:
|
||||
return 'Something went wrong.';
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const FormSchema = z.object({
|
||||
id: z.string(),
|
||||
customerId: z.string({
|
||||
|
|
|
|||
20
dashboard-app-course/app/login/page.tsx
Normal file
20
dashboard-app-course/app/login/page.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import AcmeLogo from "../ui/acme-logo";
|
||||
import LoginForm from "../ui/login-form";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default function LoginPage(){
|
||||
return (
|
||||
<main className="flex items-center justify-center md:h-screen">
|
||||
<div className="relative mx-auto flex w-full max-w-[400px] flex-col space-y-2.5 p-4 md:-mt-32">
|
||||
<div className="flex h-20 w-full items-end rounded-lg bg-blue-500 p-3 md:h-36">
|
||||
<div className="w-32 text-white md:w-36">
|
||||
<AcmeLogo />
|
||||
</div>
|
||||
</div>
|
||||
<Suspense>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import Link from 'next/link';
|
|||
import NavLinks from '@/app/ui/dashboard/nav-links';
|
||||
import AcmeLogo from '@/app/ui/acme-logo';
|
||||
import { PowerIcon } from '@heroicons/react/24/outline';
|
||||
import { signOut } from '@/auth';
|
||||
|
||||
export default function SideNav() {
|
||||
return (
|
||||
|
|
@ -17,7 +18,12 @@ export default function SideNav() {
|
|||
<div className="flex grow flex-row justify-between space-x-2 md:flex-col md:space-x-0 md:space-y-2">
|
||||
<NavLinks />
|
||||
<div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
|
||||
<form>
|
||||
<form
|
||||
action={async () => {
|
||||
'use server';
|
||||
await signOut({redirectTo: '/'});
|
||||
}}
|
||||
>
|
||||
<button className="flex h-[48px] w-full grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
|
||||
<PowerIcon className="w-6" />
|
||||
<div className="hidden md:block">Sign Out</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
'use client';
|
||||
|
||||
import { lusitana } from '@/app/ui/fonts';
|
||||
import {
|
||||
AtSymbolIcon,
|
||||
|
|
@ -6,10 +8,19 @@ import {
|
|||
} from '@heroicons/react/24/outline';
|
||||
import { ArrowRightIcon } from '@heroicons/react/20/solid';
|
||||
import { Button } from './button';
|
||||
import { useActionState } from 'react';
|
||||
import { authenticate } from '../lib/actions';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
|
||||
export default function LoginForm() {
|
||||
const searchParams = useSearchParams();
|
||||
const callbackUrl = searchParams.get('callbackUrl') || '/dashboard';
|
||||
const [errorMessage, formAction, isPending] = useActionState(
|
||||
authenticate,
|
||||
undefined,
|
||||
);
|
||||
return (
|
||||
<form className="space-y-3">
|
||||
<form action={formAction} className="space-y-3">
|
||||
<div className="flex-1 rounded-lg bg-gray-50 px-6 pb-4 pt-8">
|
||||
<h1 className={`${lusitana.className} mb-3 text-2xl`}>
|
||||
Please log in to continue.
|
||||
|
|
@ -55,11 +66,21 @@ export default function LoginForm() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button className="mt-4 w-full">
|
||||
<input type="hidden" name="redirectTo" value={callbackUrl} />
|
||||
<Button className="mt-4 w-full" aria-disabled={isPending}>
|
||||
Log in <ArrowRightIcon className="ml-auto h-5 w-5 text-gray-50" />
|
||||
</Button>
|
||||
<div className="flex h-8 items-end space-x-1">
|
||||
{/* Add form errors here */}
|
||||
<div
|
||||
className="flex h-8 items-end space-x-1"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
>
|
||||
{errorMessage && (
|
||||
<>
|
||||
<ExclamationCircleIcon className="h-5 w-5 text-red-500" />
|
||||
<p className="text-sm text-red-500">{errorMessage}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
21
dashboard-app-course/auth.config.ts
Normal file
21
dashboard-app-course/auth.config.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { NextAuthConfig } from 'next-auth';
|
||||
|
||||
export const authConfig = {
|
||||
pages: {
|
||||
signIn: '/login',
|
||||
},
|
||||
callbacks: {
|
||||
authorized({ auth, request: { nextUrl } }) {
|
||||
const isLoggedIn = !!auth?.user;
|
||||
const isOnDashboard = nextUrl.pathname.startsWith('/dashboard');
|
||||
if (isOnDashboard) {
|
||||
if (isLoggedIn) return true;
|
||||
return false;
|
||||
} else if (isLoggedIn) {
|
||||
return Response.redirect(new URL('/dashboard', nextUrl));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
} satisfies NextAuthConfig;
|
||||
42
dashboard-app-course/auth.ts
Normal file
42
dashboard-app-course/auth.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import NextAuth from 'next-auth';
|
||||
import { authConfig } from './auth.config';
|
||||
import Credentials from 'next-auth/providers/credentials';
|
||||
import { z } from 'zod';
|
||||
import type { User } from '@/app/lib/definitions';
|
||||
import bcrypt from 'bcrypt';
|
||||
import postgres from 'postgres';
|
||||
|
||||
const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });
|
||||
|
||||
async function getUser(email: string): Promise<User | undefined> {
|
||||
try{
|
||||
const user = await sql<User[]>`SELECT * FROM users WHERE email=${email}`;
|
||||
return user[0];
|
||||
} catch(error){
|
||||
console.error('Failed to fetch user:', error);
|
||||
throw new Error('Failed to fetch user.');
|
||||
}
|
||||
}
|
||||
|
||||
export const { auth, signIn, signOut } = NextAuth({
|
||||
...authConfig,
|
||||
providers: [Credentials({
|
||||
async authorize(credentials){
|
||||
const parsedCredentials = z
|
||||
.object({ email: z.string().email(), password: z.string().min(6)})
|
||||
.safeParse(credentials);
|
||||
|
||||
if(parsedCredentials.success){
|
||||
const { email, password } = parsedCredentials.data;
|
||||
const user = await getUser(email);
|
||||
if(!user) return null;
|
||||
const passwordsMatch = await bcrypt.compare(password, user.password);
|
||||
|
||||
if(passwordsMatch) return user;
|
||||
}
|
||||
|
||||
console.log('Invalid credentials');
|
||||
return null;
|
||||
},
|
||||
})],
|
||||
});
|
||||
9
dashboard-app-course/middleware.ts
Normal file
9
dashboard-app-course/middleware.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import NextAuth from 'next-auth';
|
||||
import { authConfig } from './auth.config';
|
||||
|
||||
export default NextAuth(authConfig).auth;
|
||||
|
||||
export const config = {
|
||||
// https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
|
||||
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
|
||||
};
|
||||
68
dashboard-app-course/package-lock.json
generated
68
dashboard-app-course/package-lock.json
generated
|
|
@ -12,7 +12,7 @@
|
|||
"bcrypt": "^5.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
"next": "latest",
|
||||
"next-auth": "5.0.0-beta.25",
|
||||
"next-auth": "5.0.0-beta.26",
|
||||
"postcss": "8.5.1",
|
||||
"postgres": "^3.4.5",
|
||||
"react": "latest",
|
||||
|
|
@ -42,18 +42,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@auth/core": {
|
||||
"version": "0.37.2",
|
||||
"resolved": "https://registry.npmjs.org/@auth/core/-/core-0.37.2.tgz",
|
||||
"integrity": "sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==",
|
||||
"version": "0.39.0",
|
||||
"resolved": "https://registry.npmjs.org/@auth/core/-/core-0.39.0.tgz",
|
||||
"integrity": "sha512-jusviw/sUSfAh6S/wjY5tRmJOq0Itd3ImF+c/b4HB9DfmfChtcfVJTNJeqCeExeCG8oh4PBKRsMQJsn2W6NhFQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@panva/hkdf": "^1.2.1",
|
||||
"@types/cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"jose": "^5.9.3",
|
||||
"oauth4webapi": "^3.0.0",
|
||||
"preact": "10.11.3",
|
||||
"preact-render-to-string": "5.2.3"
|
||||
"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",
|
||||
|
|
@ -841,12 +839,6 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz",
|
||||
|
|
@ -1267,15 +1259,6 @@
|
|||
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"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",
|
||||
|
|
@ -1729,9 +1712,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz",
|
||||
"integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==",
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz",
|
||||
"integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
|
|
@ -1964,12 +1947,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/next-auth": {
|
||||
"version": "5.0.0-beta.25",
|
||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.25.tgz",
|
||||
"integrity": "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==",
|
||||
"version": "5.0.0-beta.26",
|
||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.26.tgz",
|
||||
"integrity": "sha512-yAQLIP2x6FAM+GX6FTlQjoPph6msO/9HI3pjI1z1yws3VnvS77atetcxQOmCpxSLTO4jzvpQqPaBZMgRxDgsYg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@auth/core": "0.37.2"
|
||||
"@auth/core": "0.39.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@simplewebauthn/browser": "^9.0.1",
|
||||
|
|
@ -2429,9 +2412,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.11.3",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz",
|
||||
"integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==",
|
||||
"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",
|
||||
|
|
@ -2439,23 +2422,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/preact-render-to-string": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz",
|
||||
"integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==",
|
||||
"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",
|
||||
"dependencies": {
|
||||
"pretty-format": "^3.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"preact": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-format": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"bcrypt": "^5.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
"next": "latest",
|
||||
"next-auth": "5.0.0-beta.25",
|
||||
"next-auth": "5.0.0-beta.26",
|
||||
"postcss": "8.5.1",
|
||||
"postgres": "^3.4.5",
|
||||
"react": "latest",
|
||||
|
|
|
|||
71
dashboard-app-course/pnpm-lock.yaml
generated
71
dashboard-app-course/pnpm-lock.yaml
generated
|
|
@ -30,8 +30,8 @@ importers:
|
|||
specifier: latest
|
||||
version: 15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next-auth:
|
||||
specifier: 5.0.0-beta.25
|
||||
version: 5.0.0-beta.25(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
|
||||
specifier: 5.0.0-beta.26
|
||||
version: 5.0.0-beta.26(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
|
||||
postcss:
|
||||
specifier: 8.5.1
|
||||
version: 8.5.1
|
||||
|
|
@ -76,8 +76,8 @@ packages:
|
|||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@auth/core@0.37.2':
|
||||
resolution: {integrity: sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==}
|
||||
'@auth/core@0.39.0':
|
||||
resolution: {integrity: sha512-jusviw/sUSfAh6S/wjY5tRmJOq0Itd3ImF+c/b4HB9DfmfChtcfVJTNJeqCeExeCG8oh4PBKRsMQJsn2W6NhFQ==}
|
||||
peerDependencies:
|
||||
'@simplewebauthn/browser': ^9.0.1
|
||||
'@simplewebauthn/server': ^9.0.2
|
||||
|
|
@ -317,9 +317,6 @@ packages:
|
|||
'@types/bcrypt@5.0.2':
|
||||
resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==}
|
||||
|
||||
'@types/cookie@0.6.0':
|
||||
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
|
||||
|
||||
'@types/node@22.10.7':
|
||||
resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==}
|
||||
|
||||
|
|
@ -462,10 +459,6 @@ packages:
|
|||
console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
|
||||
cookie@0.7.1:
|
||||
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
|
@ -625,8 +618,8 @@ packages:
|
|||
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
|
||||
hasBin: true
|
||||
|
||||
jose@5.9.6:
|
||||
resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==}
|
||||
jose@6.0.10:
|
||||
resolution: {integrity: sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==}
|
||||
|
||||
lilconfig@3.1.3:
|
||||
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
|
||||
|
|
@ -693,8 +686,8 @@ packages:
|
|||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
next-auth@5.0.0-beta.25:
|
||||
resolution: {integrity: sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==}
|
||||
next-auth@5.0.0-beta.26:
|
||||
resolution: {integrity: sha512-yAQLIP2x6FAM+GX6FTlQjoPph6msO/9HI3pjI1z1yws3VnvS77atetcxQOmCpxSLTO4jzvpQqPaBZMgRxDgsYg==}
|
||||
peerDependencies:
|
||||
'@simplewebauthn/browser': ^9.0.1
|
||||
'@simplewebauthn/server': ^9.0.2
|
||||
|
|
@ -762,8 +755,8 @@ packages:
|
|||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
oauth4webapi@3.1.4:
|
||||
resolution: {integrity: sha512-eVfN3nZNbok2s/ROifO0UAc5G8nRoLSbrcKJ09OqmucgnhXEfdIQOR4gq1eJH1rN3gV7rNw62bDEgftsgFtBEg==}
|
||||
oauth4webapi@3.5.0:
|
||||
resolution: {integrity: sha512-DF3mLWNuxPkxJkHmWxbSFz4aE5CjWOsm465VBfBdWzmzX4Mg3vF8icxK+iKqfdWrIumBJ2TaoNQWx+SQc2bsPQ==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
|
|
@ -895,16 +888,13 @@ packages:
|
|||
resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
preact-render-to-string@5.2.3:
|
||||
resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
|
||||
preact-render-to-string@6.5.11:
|
||||
resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==}
|
||||
peerDependencies:
|
||||
preact: '>=10'
|
||||
|
||||
preact@10.11.3:
|
||||
resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
|
||||
|
||||
pretty-format@3.8.0:
|
||||
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
|
||||
preact@10.24.3:
|
||||
resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==}
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
|
@ -1127,15 +1117,13 @@ snapshots:
|
|||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
|
||||
'@auth/core@0.37.2':
|
||||
'@auth/core@0.39.0':
|
||||
dependencies:
|
||||
'@panva/hkdf': 1.2.1
|
||||
'@types/cookie': 0.6.0
|
||||
cookie: 0.7.1
|
||||
jose: 5.9.6
|
||||
oauth4webapi: 3.1.4
|
||||
preact: 10.11.3
|
||||
preact-render-to-string: 5.2.3(preact@10.11.3)
|
||||
jose: 6.0.10
|
||||
oauth4webapi: 3.5.0
|
||||
preact: 10.24.3
|
||||
preact-render-to-string: 6.5.11(preact@10.24.3)
|
||||
|
||||
'@emnapi/runtime@1.3.1':
|
||||
dependencies:
|
||||
|
|
@ -1325,8 +1313,6 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 22.10.7
|
||||
|
||||
'@types/cookie@0.6.0': {}
|
||||
|
||||
'@types/node@22.10.7':
|
||||
dependencies:
|
||||
undici-types: 6.20.0
|
||||
|
|
@ -1473,8 +1459,6 @@ snapshots:
|
|||
|
||||
console-control-strings@1.1.0: {}
|
||||
|
||||
cookie@0.7.1: {}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
|
|
@ -1630,7 +1614,7 @@ snapshots:
|
|||
|
||||
jiti@1.21.7: {}
|
||||
|
||||
jose@5.9.6: {}
|
||||
jose@6.0.10: {}
|
||||
|
||||
lilconfig@3.1.3: {}
|
||||
|
||||
|
|
@ -1684,9 +1668,9 @@ snapshots:
|
|||
|
||||
nanoid@3.3.8: {}
|
||||
|
||||
next-auth@5.0.0-beta.25(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0):
|
||||
next-auth@5.0.0-beta.26(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@auth/core': 0.37.2
|
||||
'@auth/core': 0.39.0
|
||||
next: 15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
|
||||
|
|
@ -1738,7 +1722,7 @@ snapshots:
|
|||
gauge: 3.0.2
|
||||
set-blocking: 2.0.0
|
||||
|
||||
oauth4webapi@3.1.4: {}
|
||||
oauth4webapi@3.5.0: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
|
|
@ -1844,14 +1828,11 @@ snapshots:
|
|||
|
||||
postgres@3.4.5: {}
|
||||
|
||||
preact-render-to-string@5.2.3(preact@10.11.3):
|
||||
preact-render-to-string@6.5.11(preact@10.24.3):
|
||||
dependencies:
|
||||
preact: 10.11.3
|
||||
pretty-format: 3.8.0
|
||||
preact: 10.24.3
|
||||
|
||||
preact@10.11.3: {}
|
||||
|
||||
pretty-format@3.8.0: {}
|
||||
preact@10.24.3: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue