diff --git a/apps/api/app/webhooks/clerk/route.ts b/apps/api/app/webhooks/clerk/route.ts index 2432f28c..c52fa07c 100644 --- a/apps/api/app/webhooks/clerk/route.ts +++ b/apps/api/app/webhooks/clerk/route.ts @@ -1,11 +1,11 @@ +import { analytics } from '@repo/analytics/posthog/server'; import type { DeletedObjectJSON, OrganizationJSON, OrganizationMembershipJSON, UserJSON, WebhookEvent, -} from '@clerk/nextjs/server'; -import { analytics } from '@repo/analytics/posthog/server'; +} from '@repo/auth/server'; import { env } from '@repo/env'; import { log } from '@repo/observability/log'; import { headers } from 'next/headers'; diff --git a/apps/api/app/webhooks/stripe/route.ts b/apps/api/app/webhooks/stripe/route.ts index 7c2053b7..f2ec7ee7 100644 --- a/apps/api/app/webhooks/stripe/route.ts +++ b/apps/api/app/webhooks/stripe/route.ts @@ -1,5 +1,5 @@ -import { clerkClient } from '@clerk/nextjs/server'; import { analytics } from '@repo/analytics/posthog/server'; +import { clerkClient } from '@repo/auth/server'; import { env } from '@repo/env'; import { parseError } from '@repo/observability/error'; import { log } from '@repo/observability/log'; diff --git a/apps/api/package.json b/apps/api/package.json index 59a5ab37..402be2b0 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -10,8 +10,8 @@ "stripe": "stripe listen --forward-to localhost:3002/webhooks/stripe" }, "dependencies": { - "@clerk/nextjs": "^6.3.4", "@repo/analytics": "workspace:*", + "@repo/auth": "workspace:*", "@repo/database": "workspace:*", "@repo/design-system": "workspace:*", "@repo/env": "workspace:*", diff --git a/apps/app/app/(authenticated)/components/posthog-identifier.tsx b/apps/app/app/(authenticated)/components/posthog-identifier.tsx index 05222da8..1d0e1bee 100644 --- a/apps/app/app/(authenticated)/components/posthog-identifier.tsx +++ b/apps/app/app/(authenticated)/components/posthog-identifier.tsx @@ -1,7 +1,7 @@ 'use client'; -import { useUser } from '@clerk/nextjs'; import { analytics } from '@repo/analytics/posthog/client'; +import { useUser } from '@repo/auth/client'; import { usePathname, useSearchParams } from 'next/navigation'; import { useEffect, useRef } from 'react'; diff --git a/apps/app/app/(authenticated)/components/sidebar.tsx b/apps/app/app/(authenticated)/components/sidebar.tsx index 5515accf..2297448c 100644 --- a/apps/app/app/(authenticated)/components/sidebar.tsx +++ b/apps/app/app/(authenticated)/components/sidebar.tsx @@ -1,6 +1,6 @@ 'use client'; -import { OrganizationSwitcher, UserButton } from '@clerk/nextjs'; +import { OrganizationSwitcher, UserButton } from '@repo/auth/client'; import { ModeToggle } from '@repo/design-system/components/mode-toggle'; import { Collapsible, diff --git a/apps/app/app/(authenticated)/layout.tsx b/apps/app/app/(authenticated)/layout.tsx index e4124088..d2b6264f 100644 --- a/apps/app/app/(authenticated)/layout.tsx +++ b/apps/app/app/(authenticated)/layout.tsx @@ -1,4 +1,4 @@ -import { auth, currentUser } from '@clerk/nextjs/server'; +import { auth, currentUser } from '@repo/auth/server'; import { SidebarProvider } from '@repo/design-system/components/ui/sidebar'; import { showBetaFeature } from '@repo/feature-flags'; import arcjet, { detectBot, request } from '@repo/security'; diff --git a/apps/app/app/(authenticated)/page.tsx b/apps/app/app/(authenticated)/page.tsx index 12e35907..55137ccf 100644 --- a/apps/app/app/(authenticated)/page.tsx +++ b/apps/app/app/(authenticated)/page.tsx @@ -1,4 +1,4 @@ -import { auth } from '@clerk/nextjs/server'; +import { auth } from '@repo/auth/server'; import { database } from '@repo/database'; import { Breadcrumb, diff --git a/apps/app/app/actions/users/get.ts b/apps/app/app/actions/users/get.ts index 22529ca7..ca8bae65 100644 --- a/apps/app/app/actions/users/get.ts +++ b/apps/app/app/actions/users/get.ts @@ -4,7 +4,7 @@ import { type OrganizationMembership, auth, clerkClient, -} from '@clerk/nextjs/server'; +} from '@repo/auth/server'; import { tailwind } from '@repo/tailwind-config'; const getName = (user: OrganizationMembership): string | undefined => { diff --git a/apps/app/app/actions/users/search.ts b/apps/app/app/actions/users/search.ts index 8cda661d..c25d8abc 100644 --- a/apps/app/app/actions/users/search.ts +++ b/apps/app/app/actions/users/search.ts @@ -4,7 +4,7 @@ import { type OrganizationMembership, auth, clerkClient, -} from '@clerk/nextjs/server'; +} from '@repo/auth/server'; import Fuse from 'fuse.js'; const getName = (user: OrganizationMembership): string | undefined => { diff --git a/apps/app/app/api/collaboration/auth/route.ts b/apps/app/app/api/collaboration/auth/route.ts index 817618ec..b4d726f4 100644 --- a/apps/app/app/api/collaboration/auth/route.ts +++ b/apps/app/app/api/collaboration/auth/route.ts @@ -1,4 +1,4 @@ -import { auth, currentUser } from '@clerk/nextjs/server'; +import { auth, currentUser } from '@repo/auth/server'; import { authenticate } from '@repo/collaboration/auth'; import { tailwind } from '@repo/tailwind-config'; diff --git a/apps/app/middleware.ts b/apps/app/middleware.ts index d2840231..e37a24e2 100644 --- a/apps/app/middleware.ts +++ b/apps/app/middleware.ts @@ -1,6 +1,6 @@ -import { clerkMiddleware } from '@clerk/nextjs/server'; +import { authMiddleware } from '@repo/auth/middleware'; -export default clerkMiddleware(); +export default authMiddleware(); export const config = { matcher: [ diff --git a/apps/app/package.json b/apps/app/package.json index bebd73f4..b65bff5a 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -9,7 +9,6 @@ "test": "vitest run" }, "dependencies": { - "@clerk/nextjs": "^6.3.4", "@prisma/client": "5.22.0", "@repo/auth": "workspace:*", "@repo/analytics": "workspace:*", diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index c37858f9..133242e8 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -1,4 +1,4 @@ -import { clerkMiddleware } from '@clerk/nextjs/server'; +import { authMiddleware } from '@repo/auth/middleware'; import arcjet, { detectBot } from '@repo/security'; import { NextResponse } from 'next/server'; @@ -21,7 +21,7 @@ const aj = arcjet.withRule( }) ); -export default clerkMiddleware(async (_auth, request) => { +export default authMiddleware(async (_auth, request) => { const decision = await aj.protect(request); if ( diff --git a/apps/web/package.json b/apps/web/package.json index 6f554c12..4a900b92 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@arcjet/next": "1.0.0-alpha.28", - "@clerk/nextjs": "^6.3.4", "@content-collections/mdx": "^0.2.0", "@radix-ui/react-icons": "^1.3.2", "@repo/design-system": "workspace:*", diff --git a/docs/authentication/authjs.mdx b/docs/authentication/authjs.mdx new file mode 100644 index 00000000..0d80188f --- /dev/null +++ b/docs/authentication/authjs.mdx @@ -0,0 +1,132 @@ +--- +title: Switch to Auth.js +description: How to switch from Clerk to Auth.js. +--- + + +next-forge support for Auth.js is currently blocked by [this issue](https://github.com/nextauthjs/next-auth/issues/11076). + + +Here's how to switch from Clerk to [Auth.js](https://authjs.dev/). + +## 1. Replace the dependencies + +Uninstall the existing Clerk dependencies from the `auth` package... + +```sh Terminal +pnpm remove @clerk/nextjs @clerk/themes @clerk/types --filter auth +``` + +... and install the Auth.js dependencies. + +```sh Terminal +pnpm add next-auth@beta --filter auth +``` + +## 2. Generate an Auth.js secret + +Auth.js requires a random value secret, used by the library to encrypt tokens and email verification hashes. In each of the relevant app directories, generate a secret with the following command: + +```sh Terminal +cd apps/app && npx auth secret +cd apps/web && npx auth secret +cd apps/api && npx auth secret +``` + +This will automatically add an `AUTH_SECRET` environment variable to the `.env.local` file in each directory. + +## 3. Replace the relevant files + +Delete the existing `client.ts` and `server.ts` files in the `auth` package. Then, create the following file: + +```tsx packages/auth/index.ts +import NextAuth from "next-auth"; + +export const { handlers, signIn, signOut, auth } = NextAuth({ + providers: [], +}); +``` + +## 4. Update the middleware + +Update the `middleware.ts` file in the `auth` package with the following content: + +```tsx packages/auth/middleware.ts +import 'server-only'; + +export { auth as authMiddleware } from './'; +``` + +## 5. Update the auth components + +Auth.js has no concept of "sign up", so we'll use the `signIn` function to sign up users. Update both the `sign-in.tsx` and `sign-up.tsx` components in the `auth` package with the same content: + + + +```tsx packages/auth/components/sign-in.tsx +import { signIn } from '../'; + +export const SignIn = () => ( +
{ + "use server"; + await signIn(); + }} + > + +
+); +``` + +```tsx packages/auth/components/sign-up.tsx +import { signIn } from '../'; + +export const SignUp = () => ( +
{ + "use server"; + await signIn(); + }} + > + +
+); +``` + +
+ +## 6. Update the Provider file + +Auth.js has no concept of a Provider as a higher-order component, so you can either remove it entirely or just replace it with a stub, like so: + +```tsx packages/auth/provider.tsx +import type { ReactNode } from 'react'; + +type AuthProviderProps = { + children: ReactNode; +}; + +export const AuthProvider = ({ children }: AuthProviderProps) => children; +``` + +## 7. Create an auth route handler + +In your `app` application, create an auth route handler file with the following content: + +```tsx apps/app/api/auth/[...nextauth]/route.ts +import { handlers } from "@repo/auth" + +export const { GET, POST } = handlers; +``` + +## 8. Update your apps + +From here, you'll need to replace any remaining Clerk implementations in your apps with Auth.js references. This means swapping out references like: + +```tsx page.tsx +const { orgId } = await auth(); +const { redirectToSignIn } = await auth(); +const user = await currentUser(); +``` + +Etcetera. Keep in mind that you'll need to build your own "organization" logic as Auth.js doesn't have a concept of organizations. diff --git a/docs/authentication.mdx b/docs/authentication/overview.mdx similarity index 80% rename from docs/authentication.mdx rename to docs/authentication/overview.mdx index 80762648..dee2e2c1 100644 --- a/docs/authentication.mdx +++ b/docs/authentication/overview.mdx @@ -1,9 +1,9 @@ --- -title: Authentication +title: Overview description: We use Clerk to handle authentication, user and organization management. --- -[Clerk](https://clerk.com/) is used for authentication in next-forge. Clerk provides a complete authentication and user management solution that integrates seamlessly with Next.js applications. +next-forge manages authentication through the use of a `auth` package. By default, this package is a wrapper around [Clerk](https://clerk.com/) which provides a complete authentication and user management solution that integrates seamlessly with Next.js applications. ## In-App diff --git a/docs/mint.json b/docs/mint.json index 2e4feb4d..d4bcd9e0 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -53,7 +53,10 @@ "group": "Analytics", "pages": ["analytics/web", "analytics/product"] }, - "authentication", + { + "group": "Authentication", + "pages": ["authentication/overview", "authentication/authjs"] + }, "blog", "bundle-analysis", "collaboration", diff --git a/packages/auth/client.ts b/packages/auth/client.ts new file mode 100644 index 00000000..a832599f --- /dev/null +++ b/packages/auth/client.ts @@ -0,0 +1 @@ +export * from '@clerk/nextjs'; diff --git a/packages/auth/middleware.ts b/packages/auth/middleware.ts new file mode 100644 index 00000000..9d816d0e --- /dev/null +++ b/packages/auth/middleware.ts @@ -0,0 +1 @@ +export { clerkMiddleware as authMiddleware } from '@clerk/nextjs/server'; diff --git a/packages/auth/package.json b/packages/auth/package.json index 0e09b814..becd85fa 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -7,7 +7,8 @@ "@clerk/themes": "^2.1.45", "@repo/tailwind-config": "workspace:*", "next-themes": "^0.4.3", - "react": "^18.3.1" + "react": "^18.3.1", + "server-only": "^0.0.1" }, "devDependencies": { "@clerk/types": "^4.34.0", diff --git a/packages/auth/server.ts b/packages/auth/server.ts new file mode 100644 index 00000000..73ec4ca8 --- /dev/null +++ b/packages/auth/server.ts @@ -0,0 +1,3 @@ +import 'server-only'; + +export * from '@clerk/nextjs/server'; diff --git a/packages/feature-flags/lib/create-flag.ts b/packages/feature-flags/lib/create-flag.ts index 89ef9f77..0015b651 100644 --- a/packages/feature-flags/lib/create-flag.ts +++ b/packages/feature-flags/lib/create-flag.ts @@ -1,5 +1,5 @@ -import { auth } from '@clerk/nextjs/server'; import { analytics } from '@repo/analytics/posthog/server'; +import { auth } from '@repo/auth/server'; import { unstable_flag as flag } from '@vercel/flags/next'; export const createFlag = (key: string) => diff --git a/packages/feature-flags/package.json b/packages/feature-flags/package.json index 86d5a502..31748302 100644 --- a/packages/feature-flags/package.json +++ b/packages/feature-flags/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { - "@clerk/nextjs": "^6.3.4", + "@repo/auth": "workspace:*", "@repo/analytics": "workspace:*", "@repo/design-system": "workspace:*", "@vercel/flags": "^2.6.3" diff --git a/packages/webhooks/lib/svix.ts b/packages/webhooks/lib/svix.ts index c4aa7856..ddc77f8e 100644 --- a/packages/webhooks/lib/svix.ts +++ b/packages/webhooks/lib/svix.ts @@ -1,5 +1,5 @@ import 'server-only'; -import { auth } from '@clerk/nextjs/server'; +import { auth } from '@repo/auth/server'; import { env } from '@repo/env'; import { Svix } from 'svix'; diff --git a/packages/webhooks/package.json b/packages/webhooks/package.json index 60becaba..8184f46c 100644 --- a/packages/webhooks/package.json +++ b/packages/webhooks/package.json @@ -3,8 +3,8 @@ "version": "0.0.0", "private": true, "dependencies": { + "@repo/auth": "workspace:*", "@repo/env": "workspace:*", - "@clerk/nextjs": "^6.3.4", "svix": "^1.40.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27aa266f..f1408ef0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,12 +32,12 @@ importers: apps/api: dependencies: - '@clerk/nextjs': - specifier: ^6.3.4 - version: 6.3.4(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@repo/analytics': specifier: workspace:* version: link:../../packages/analytics + '@repo/auth': + specifier: workspace:* + version: link:../../packages/auth '@repo/database': specifier: workspace:* version: link:../../packages/database @@ -93,9 +93,6 @@ importers: apps/app: dependencies: - '@clerk/nextjs': - specifier: ^6.3.4 - version: 6.3.4(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@prisma/client': specifier: 5.22.0 version: 5.22.0(prisma@5.22.0) @@ -239,9 +236,6 @@ importers: '@arcjet/next': specifier: 1.0.0-alpha.28 version: 1.0.0-alpha.28(@bufbuild/protobuf@1.10.0)(@connectrpc/connect@1.6.1(@bufbuild/protobuf@1.10.0))(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@clerk/nextjs': - specifier: ^6.3.4 - version: 6.3.4(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@content-collections/mdx': specifier: ^0.2.0 version: 0.2.0(@content-collections/core@0.7.3(typescript@5.6.3))(acorn@8.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -406,6 +400,9 @@ importers: react: specifier: ^18.3.1 version: 18.3.1 + server-only: + specifier: ^0.0.1 + version: 0.0.1 devDependencies: '@clerk/types': specifier: ^4.34.0 @@ -742,12 +739,12 @@ importers: packages/feature-flags: dependencies: - '@clerk/nextjs': - specifier: ^6.3.4 - version: 6.3.4(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@repo/analytics': specifier: workspace:* version: link:../analytics + '@repo/auth': + specifier: workspace:* + version: link:../auth '@repo/design-system': specifier: workspace:* version: link:../design-system @@ -922,9 +919,9 @@ importers: packages/webhooks: dependencies: - '@clerk/nextjs': - specifier: ^6.3.4 - version: 6.3.4(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@repo/auth': + specifier: workspace:* + version: link:../auth '@repo/env': specifier: workspace:* version: link:../env @@ -8507,20 +8504,6 @@ snapshots: react-dom: 18.3.1(react@18.3.1) tslib: 2.4.1 - '@clerk/nextjs@6.3.4(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@clerk/backend': 1.16.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/clerk-react': 5.15.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/shared': 2.14.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@clerk/types': 4.34.0 - crypto-js: 4.2.0 - ezheaders: 0.1.0(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - next: 15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - server-only: 0.0.1 - tslib: 2.4.1 - '@clerk/nextjs@6.3.4(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@clerk/backend': 1.16.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12401,10 +12384,6 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 - ezheaders@0.1.0(next@15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): - dependencies: - next: 15.0.1(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - ezheaders@0.1.0(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: next: 15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)