Add PrismaORM

preparation for access_token refresh, since JWT cookies cannot be refreshed from the server side
This commit is contained in:
GHOSCHT 2024-05-19 18:08:47 +02:00
parent c25bf01d53
commit b4503db688
Signed by: ghoscht
GPG key ID: 2C2C1C62A5388E82
9 changed files with 267 additions and 6 deletions

3
.gitignore vendored
View file

@ -41,3 +41,6 @@ next-env.d.ts
/result-lib /result-lib
.direnv .direnv
.env .env
# prisma
schnabu-web.db

25
auth.ts
View file

@ -1,13 +1,18 @@
import NextAuth,{ type DefaultSession } from "next-auth"; import NextAuth,{ type DefaultSession } from "next-auth";
import Authentik from "next-auth/providers/authentik"; import Authentik from "next-auth/providers/authentik";
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()
declare module "next-auth" { declare module "next-auth" {
interface Session { interface Session {
access_token: string; access_token: string | null;
} }
} }
export const { handlers, auth, signIn, signOut } = NextAuth({ export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [Authentik({ providers: [Authentik({
clientId: process.env.AUTH_OIDC_CLIENT_ID, clientId: process.env.AUTH_OIDC_CLIENT_ID,
clientSecret: process.env.AUTH_OIDC_CLIENT_SECRET, clientSecret: process.env.AUTH_OIDC_CLIENT_SECRET,
@ -21,12 +26,20 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
} }
return token return token
}, },
async session({session, token}) { async session({session, user}) {
if(session) { const getToken = await prisma.account.findFirst({
session = Object.assign({}, session, {access_token: token.access_token}) where: {
console.log(session); userId: user.id,
},
});
let accessToken: string | null = null;
if (getToken) {
accessToken = getToken.access_token!;
} }
return session
session.access_token = accessToken;
return session;
} }
} }
}); });

View file

@ -17,6 +17,9 @@
devShells = eachSystem (pkgs: { devShells = eachSystem (pkgs: {
default = pkgs.mkShell { default = pkgs.mkShell {
buildInputs = [ buildInputs = [
pkgs.openssl
pkgs.prisma-engines
pkgs.nodejs pkgs.nodejs
pkgs.nodePackages.pnpm pkgs.nodePackages.pnpm
@ -24,6 +27,13 @@
pkgs.nodePackages.typescript pkgs.nodePackages.typescript
pkgs.nodePackages.typescript-language-server pkgs.nodePackages.typescript-language-server
]; ];
shellHook = ''
export PRISMA_SCHEMA_ENGINE_BINARY="${pkgs.prisma-engines}/bin/schema-engine"
export PRISMA_QUERY_ENGINE_BINARY="${pkgs.prisma-engines}/bin/query-engine"
export PRISMA_QUERY_ENGINE_LIBRARY="${pkgs.prisma-engines}/lib/libquery_engine.node"
export PRISMA_FMT_BINARY="${pkgs.prisma-engines}/bin/prisma-fmt"
export PATH="$PWD/node_modules/.bin/:$PATH"
'';
}; };
}); });
}; };

View file

@ -9,6 +9,8 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@auth/prisma-adapter": "^2.1.0",
"@prisma/client": "^5.14.0",
"next": "14.2.3", "next": "14.2.3",
"next-auth": "5.0.0-beta.18", "next-auth": "5.0.0-beta.18",
"react": "^18", "react": "^18",
@ -21,6 +23,7 @@
"eslint": "^8", "eslint": "^8",
"eslint-config-next": "14.2.3", "eslint-config-next": "14.2.3",
"postcss": "^8", "postcss": "^8",
"prisma": "^5.14.0",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"typescript": "^5" "typescript": "^5"
} }

View file

@ -5,6 +5,12 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@auth/prisma-adapter':
specifier: ^2.1.0
version: 2.1.0(@prisma/client@5.14.0)
'@prisma/client':
specifier: ^5.14.0
version: 5.14.0(prisma@5.14.0)
next: next:
specifier: 14.2.3 specifier: 14.2.3
version: 14.2.3(react-dom@18.3.1)(react@18.3.1) version: 14.2.3(react-dom@18.3.1)(react@18.3.1)
@ -37,6 +43,9 @@ devDependencies:
postcss: postcss:
specifier: ^8 specifier: ^8
version: 8.4.38 version: 8.4.38
prisma:
specifier: ^5.14.0
version: 5.14.0
tailwindcss: tailwindcss:
specifier: ^3.4.1 specifier: ^3.4.1
version: 3.4.3 version: 3.4.3
@ -74,6 +83,19 @@ packages:
preact-render-to-string: 5.2.3(preact@10.11.3) preact-render-to-string: 5.2.3(preact@10.11.3)
dev: false dev: false
/@auth/prisma-adapter@2.1.0(@prisma/client@5.14.0):
resolution: {integrity: sha512-x1gYsi8xCFdxpEM6pxhh7OTV+VHB3PgID2L18y8F1kXu+PbcEWt4VZSQ8zk6dI/4YRStcuwQdHe7neCpczr0mg==}
peerDependencies:
'@prisma/client': '>=2.26.0 || >=3 || >=4 || >=5'
dependencies:
'@auth/core': 0.31.0
'@prisma/client': 5.14.0(prisma@5.14.0)
transitivePeerDependencies:
- '@simplewebauthn/browser'
- '@simplewebauthn/server'
- nodemailer
dev: false
/@babel/runtime@7.24.5: /@babel/runtime@7.24.5:
resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -303,6 +325,46 @@ packages:
dev: true dev: true
optional: true optional: true
/@prisma/client@5.14.0(prisma@5.14.0):
resolution: {integrity: sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg==}
engines: {node: '>=16.13'}
requiresBuild: true
peerDependencies:
prisma: '*'
peerDependenciesMeta:
prisma:
optional: true
dependencies:
prisma: 5.14.0
dev: false
/@prisma/debug@5.14.0:
resolution: {integrity: sha512-iq56qBZuFfX3fCxoxT8gBX33lQzomBU0qIUaEj1RebsKVz1ob/BVH1XSBwwwvRVtZEV1b7Fxx2eVu34Ge/mg3w==}
/@prisma/engines-version@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48:
resolution: {integrity: sha512-ip6pNkRo1UxWv+6toxNcYvItNYaqQjXdFNGJ+Nuk2eYtRoEdoF13wxo7/jsClJFFenMPVNVqXQDV0oveXnR1cA==}
/@prisma/engines@5.14.0:
resolution: {integrity: sha512-lgxkKZ6IEygVcw6IZZUlPIfLQ9hjSYAtHjZ5r64sCLDgVzsPFCi2XBBJgzPMkOQ5RHzUD4E/dVdpn9+ez8tk1A==}
requiresBuild: true
dependencies:
'@prisma/debug': 5.14.0
'@prisma/engines-version': 5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48
'@prisma/fetch-engine': 5.14.0
'@prisma/get-platform': 5.14.0
/@prisma/fetch-engine@5.14.0:
resolution: {integrity: sha512-VrheA9y9DMURK5vu8OJoOgQpxOhas3qF0IBHJ8G/0X44k82kc8E0w98HCn2nhnbOOMwbWsJWXfLC2/F8n5u0gQ==}
dependencies:
'@prisma/debug': 5.14.0
'@prisma/engines-version': 5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48
'@prisma/get-platform': 5.14.0
/@prisma/get-platform@5.14.0:
resolution: {integrity: sha512-/yAyBvcEjRv41ynZrhdrPtHgk47xLRRq/o5eWGcUpBJ1YrUZTYB8EoPiopnP7iQrMATK8stXQdPOoVlrzuTQZw==}
dependencies:
'@prisma/debug': 5.14.0
/@rushstack/eslint-patch@1.10.3: /@rushstack/eslint-patch@1.10.3:
resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==}
dev: true dev: true
@ -2294,6 +2356,14 @@ packages:
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
dev: false dev: false
/prisma@5.14.0:
resolution: {integrity: sha512-gCNZco7y5XtjrnQYeDJTiVZmT/ncqCr5RY1/Cf8X2wgLRmyh9ayPAGBNziI4qEE4S6SxCH5omQLVo9lmURaJ/Q==}
engines: {node: '>=16.13'}
hasBin: true
requiresBuild: true
dependencies:
'@prisma/engines': 5.14.0
/prop-types@15.8.1: /prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
dependencies: dependencies:

View file

@ -0,0 +1,77 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT,
"email" TEXT,
"emailVerified" DATETIME,
"image" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "Account" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"refresh_token" TEXT,
"access_token" TEXT,
"expires_at" INTEGER,
"token_type" TEXT,
"scope" TEXT,
"id_token" TEXT,
"session_state" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Session" (
"id" TEXT NOT NULL PRIMARY KEY,
"sessionToken" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expires" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "VerificationToken" (
"identifier" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expires" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "Authenticator" (
"credentialID" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"credentialPublicKey" TEXT NOT NULL,
"counter" INTEGER NOT NULL,
"credentialDeviceType" TEXT NOT NULL,
"credentialBackedUp" BOOLEAN NOT NULL,
"transports" TEXT,
PRIMARY KEY ("userId", "credentialID"),
CONSTRAINT "Authenticator_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
-- CreateIndex
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
-- CreateIndex
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
-- CreateIndex
CREATE UNIQUE INDEX "Authenticator_credentialID_key" ON "Authenticator"("credentialID");

View file

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "sqlite"

View file

@ -0,0 +1,80 @@
datasource db {
provider = "sqlite"
url = "file:./schnabu-web.db"
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
// Optional for WebAuthn support
Authenticator Authenticator[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model VerificationToken {
identifier String
token String
expires DateTime
@@unique([identifier, token])
}
// Optional for WebAuthn support
model Authenticator {
credentialID String @unique
userId String
providerAccountId String
credentialPublicKey String
counter Int
credentialDeviceType String
credentialBackedUp Boolean
transports String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@id([userId, credentialID])
}

View file

@ -2,6 +2,8 @@ import { signIn,signOut, auth } from "auth"
export default async function Home() { export default async function Home() {
let session = await auth(); let session = await auth();
console.log(session?.access_token)
return ( return (
<> <>
<h1>Hello {session?.user?.name}!</h1> <h1>Hello {session?.user?.name}!</h1>