Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/g12i/next-pocketbase-auth
Wrapper for Next.js applications using PocketBase, with support for both client and server components
https://github.com/g12i/next-pocketbase-auth
Last synced: about 1 month ago
JSON representation
Wrapper for Next.js applications using PocketBase, with support for both client and server components
- Host: GitHub
- URL: https://github.com/g12i/next-pocketbase-auth
- Owner: g12i
- License: isc
- Created: 2024-11-25T18:38:42.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2024-11-25T19:26:38.000Z (about 2 months ago)
- Last Synced: 2024-11-25T19:36:59.469Z (about 2 months ago)
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/next-pocketbase-auth
- Size: 0 Bytes
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-pocketbase - next-pocketbase-auth - A lightweight authentication wrapper for Next.js applications, providing easy-to-use utilities for handling user session in both client and server components. ![GitHub Repo stars](https://img.shields.io/github/stars/g12i/next-pocketbase-auth) (React)
README
# next-pocketbase-auth
A lightweight authentication wrapper for Next.js applications using PocketBase, providing easy-to-use utilities for handling user session in both client and server components.
## Installation
```bash
npm install next-pocketbase-auth
```## Security Considerations
Don't rely solely on `pb.authStore.record` inside server code such as middleware. It isn't guaranteed to revalidate the Auth token.
Always use `await pb.collection("users").authRefresh();` to protect pages and user data.
Use following code to safely obtain information about the current user:
```ts
const pb = createServerClient(await cookies());const { record: user } = await pb.collection("users").authRefresh();
```## Setup Guide
### 1. Environment Variables
Add your PocketBase URL to your environment variables:
```
NEXT_PUBLIC_PB_URL=http://127.0.0.1:8090/api/
```### 2. Initialize PocketBase Clients
You'll need two different clients for client-side and server-side operations:
#### Client Component
For Client Components:
```ts
import { createBrowserClient } from "next-pocketbase-auth";const pb = createBrowserClient();
````createBrowserClient` uses a singleton pattern, so you only ever create one instance, no matter how many times you call your createClient function.
#### Server Components
For Server Components, Server Actions, and Route Handlers::
```ts
import { createServerClient } from "next-pocketbase-auth";
import { cookies } from "next/headers";const pb = createServerClient(await cookies());
```### 3. Configure Middleware
The middleware is essential for maintaining authentication state across your application.
It handles:
- Automatic token refresh
- Cookie management
- Authentication state persistenceCreate a `middleware.ts` file in your project root (or in the `src/` folder if that's where your code is).
```ts
// middleware.tsimport { createServerClient } from "next-pocketbase-auth";
import { NextResponse, type NextRequest } from "next/server";export async function middleware(request: NextRequest) {
const response = NextResponse.next({ request });const pb = createServerClient({
get: (name) => request.cookies.get(name),
set: (name, value, opts) => {
request.cookies.set(name, value);
response.cookies.set(name, value, opts);
},
delete: (name) => {
request.cookies.delete(name);
response.cookies.delete(name);
},
});// If we have a valid token, refresh the token
try {
if (pb.authStore.isValid) await pb.collection("users").authRefresh();
} catch {
// If we can't refresh the token, clear the cookies
pb.authStore.clear();
}// If we have a user, continue
if (pb.authStore.record) return response;// If we are already on the login page, continue
if (request.nextUrl.pathname === "/login") return response;// Redirect to the login page
const url = request.nextUrl.clone();
url.pathname = "/login";
return NextResponse.redirect(url);
}export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
*/
"/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
],
};
```### 4. Login Page
You can uses regular PocketBase's SDK authentication functions [see here](https://pocketbase.io/docs/authentication/), as long as you:
- create the client with `createBrowserClient()` function
- refresh the page (or redirect) once the authentication is finishedFor example to login with GitHub:
```tsx
export function LoginForm(): React.ReactElement {
const [submitError, setSubmitError] = useState("");
const router = useRouter();const handleGitHubLogin = async () => {
try {
setSubmitError("");await pb.collection("users").authWithOAuth2({ provider: "github" });
router.push("/");
} catch {
setSubmitError("An unexpected error occurred");
}
};return (
Login with GitHub
);
}
```### 5. Access user info from Server Components
```tsx
import { to } from "await-to-js";
import { createServerClient } from "next-pocketbase-auth";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";export default async function AccountPage() {
const pb = createServerClient(await cookies());
const [error, result] = await to(pb.collection("users").authRefresh());if (error) {
redirect("/login");
}return
Hello {result.record.name}
;
}
```## Common Patterns
### Mixed Public/Private Routes
For applications with both public and protected routes the middleware can stop at refreshing the auth token if it's present:
```ts
// middleware.tsimport { createServerClient } from "next-pocketbase-auth";
import { NextResponse, type NextRequest } from "next/server";export async function middleware(request: NextRequest) {
const response = NextResponse.next({ request });const pb = createServerClient({
get: (name) => request.cookies.get(name),
set: (name, value, opts) => {
request.cookies.set(name, value);
response.cookies.set(name, value, opts);
},
delete: (name) => {
request.cookies.delete(name);
response.cookies.delete(name);
},
});// If we have a valid token, refresh the token
try {
if (pb.authStore.isValid) await pb.collection("users").authRefresh();
} catch {
// If we can't refresh the token, clear the cookies
pb.authStore.clear();
}return response;
}
```### Using with TypeScript
Add type safety using [pocketbase-typegen](https://github.com/patmood/pocketbase-typegen) package.
Use it as:
```ts
import { createServerClient, createBrowserClient } from "next-pocketbase-auth";
import { TypedPocketBase } from "./lib/pb.generated";const pb = createServerClient();
const pb2 = createBrowserClient();
```### API
#### `createBrowserClient(baseUrl?: string, lang?: string, cookieOptions?: CookieOptions): T`
Creates a PocketBase client instance for use in client components.
**Parameters:**
- `baseUrl` - (optional) PocketBase API URL (defaults to `process.env.NEXT_PUBLIC_PB_URL` or `http://127.0.0.1:8090/api/`)
- `lang` - (optional) language code passed to PocketBase client
- `cookieOptions` - (optional) options to set cookies (see below)**Returns:**
- PocketBase client instance
#### `createServerClient(cookies: CookiesAdapter, baseUrl?: string, lang?: string, cookieOptions?: CookieOptions): T`
Creates a PocketBase client instance for use in server components.
**Parameters:**
- `cookies` - **required** server cookies (see examples)
- `baseUrl` - (optional) PocketBase API URL (defaults to `process.env.NEXT_PUBLIC_PB_URL` or `http://127.0.0.1:8090/api/`)
- `lang` - (optional) language code passed to PocketBase client
- `cookieOptions` - (optional) options to set cookies (see below)**Returns:**
- PocketBase client instance
##### `CookieOptions`
By default the cookie is set with following settings:
```ts
export const defaultCookieOptions: CookieOptions = {
httpOnly: false,
secure: process.env.NODE_ENV === "production",
sameSite: "strict" as const,
path: "/",
expires: new Date(Date.now() + 1209600), // 14 days
};
```