From b4503db68802743ed9d491246d7c6155a573a316 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Sun, 19 May 2024 18:08:47 +0200 Subject: [PATCH] Add PrismaORM preparation for access_token refresh, since JWT cookies cannot be refreshed from the server side --- .gitignore | 3 + auth.ts | 25 ++++-- flake.nix | 10 +++ package.json | 3 + pnpm-lock.yaml | 70 ++++++++++++++++ .../20240519155316_init_authjs/migration.sql | 77 ++++++++++++++++++ prisma/migrations/migration_lock.toml | 3 + prisma/schema-sqlite.prisma | 80 +++++++++++++++++++ src/app/page.tsx | 2 + 9 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 prisma/migrations/20240519155316_init_authjs/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema-sqlite.prisma diff --git a/.gitignore b/.gitignore index 13a630a..1088fb5 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ next-env.d.ts /result-lib .direnv .env + +# prisma +schnabu-web.db diff --git a/auth.ts b/auth.ts index 84c0c1d..cef7b71 100644 --- a/auth.ts +++ b/auth.ts @@ -1,13 +1,18 @@ import NextAuth,{ type DefaultSession } from "next-auth"; 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" { interface Session { - access_token: string; + access_token: string | null; } } export const { handlers, auth, signIn, signOut } = NextAuth({ + adapter: PrismaAdapter(prisma), providers: [Authentik({ clientId: process.env.AUTH_OIDC_CLIENT_ID, clientSecret: process.env.AUTH_OIDC_CLIENT_SECRET, @@ -21,12 +26,20 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ } return token }, - async session({session, token}) { - if(session) { - session = Object.assign({}, session, {access_token: token.access_token}) - console.log(session); + async session({session, user}) { + const getToken = await prisma.account.findFirst({ + where: { + userId: user.id, + }, + }); + + let accessToken: string | null = null; + if (getToken) { + accessToken = getToken.access_token!; } - return session + + session.access_token = accessToken; + return session; } } }); diff --git a/flake.nix b/flake.nix index d3842eb..c3de856 100644 --- a/flake.nix +++ b/flake.nix @@ -17,6 +17,9 @@ devShells = eachSystem (pkgs: { default = pkgs.mkShell { buildInputs = [ + pkgs.openssl + pkgs.prisma-engines + pkgs.nodejs pkgs.nodePackages.pnpm @@ -24,6 +27,13 @@ pkgs.nodePackages.typescript 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" + ''; }; }); }; diff --git a/package.json b/package.json index da577b4..3f0fa3d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "lint": "next lint" }, "dependencies": { + "@auth/prisma-adapter": "^2.1.0", + "@prisma/client": "^5.14.0", "next": "14.2.3", "next-auth": "5.0.0-beta.18", "react": "^18", @@ -21,6 +23,7 @@ "eslint": "^8", "eslint-config-next": "14.2.3", "postcss": "^8", + "prisma": "^5.14.0", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 691a518..6d605c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,12 @@ settings: excludeLinksFromLockfile: false 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: specifier: 14.2.3 version: 14.2.3(react-dom@18.3.1)(react@18.3.1) @@ -37,6 +43,9 @@ devDependencies: postcss: specifier: ^8 version: 8.4.38 + prisma: + specifier: ^5.14.0 + version: 5.14.0 tailwindcss: specifier: ^3.4.1 version: 3.4.3 @@ -74,6 +83,19 @@ packages: preact-render-to-string: 5.2.3(preact@10.11.3) 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: resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} engines: {node: '>=6.9.0'} @@ -303,6 +325,46 @@ packages: dev: 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: resolution: {integrity: sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==} dev: true @@ -2294,6 +2356,14 @@ packages: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} 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: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: diff --git a/prisma/migrations/20240519155316_init_authjs/migration.sql b/prisma/migrations/20240519155316_init_authjs/migration.sql new file mode 100644 index 0000000..ce20143 --- /dev/null +++ b/prisma/migrations/20240519155316_init_authjs/migration.sql @@ -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"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5e5c47 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -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" \ No newline at end of file diff --git a/prisma/schema-sqlite.prisma b/prisma/schema-sqlite.prisma new file mode 100644 index 0000000..4e351ab --- /dev/null +++ b/prisma/schema-sqlite.prisma @@ -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]) +} diff --git a/src/app/page.tsx b/src/app/page.tsx index cfb8e59..9b68f12 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,6 +2,8 @@ import { signIn,signOut, auth } from "auth" export default async function Home() { let session = await auth(); + +console.log(session?.access_token) return ( <>

Hello {session?.user?.name}!