next-lite-auth 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -138
- package/dist/client.d.mts +20 -11
- package/dist/client.d.ts +20 -11
- package/dist/client.js +132 -15
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +128 -13
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +23 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +23 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Lightweight JWT auth for Next.js using static JSON users — no database required.
|
|
4
4
|
|
|
5
|
+
**[Documentation](https://amide-init.github.io/next-lite-auth/)**
|
|
6
|
+
|
|
5
7
|
> **Not for production.** Designed for demos, OSS projects, internal tools, and quick Vercel deployments.
|
|
6
8
|
|
|
7
9
|
---
|
|
@@ -12,6 +14,7 @@ Lightweight JWT auth for Next.js using static JSON users — no database require
|
|
|
12
14
|
- Next.js >= 13
|
|
13
15
|
- React >= 18
|
|
14
16
|
- TypeScript
|
|
17
|
+
- Tailwind CSS + shadcn/ui (for built-in login UI)
|
|
15
18
|
|
|
16
19
|
---
|
|
17
20
|
|
|
@@ -23,194 +26,123 @@ pnpm add next-lite-auth jose
|
|
|
23
26
|
|
|
24
27
|
---
|
|
25
28
|
|
|
26
|
-
## Setup
|
|
29
|
+
## Setup in 3 steps
|
|
27
30
|
|
|
28
|
-
### 1. Create your
|
|
31
|
+
### 1. Create `auth.ts` at your project root
|
|
29
32
|
|
|
30
33
|
```ts
|
|
31
|
-
// lib/auth.ts
|
|
32
34
|
import { createLiteAuth } from "next-lite-auth";
|
|
33
35
|
|
|
34
|
-
export const
|
|
36
|
+
export const { handlers, middleware, getUserFromCookies } = createLiteAuth({
|
|
35
37
|
users: [
|
|
36
38
|
{ email: "admin@example.com", password: "secret", role: "admin", name: "Admin" },
|
|
37
|
-
{ email: "user@example.com", password: "pass123", role: "user", name: "User" },
|
|
38
39
|
],
|
|
39
40
|
jwtSecret: process.env.JWT_SECRET!,
|
|
40
|
-
cookieName: "lite-auth-token", // optional, this is the default
|
|
41
41
|
});
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// app/api/auth/login/route.ts
|
|
48
|
-
import { auth } from "@/lib/auth";
|
|
49
|
-
export const POST = auth.handlers.login;
|
|
50
|
-
|
|
51
|
-
// app/api/auth/logout/route.ts
|
|
52
|
-
import { auth } from "@/lib/auth";
|
|
53
|
-
export const POST = auth.handlers.logout;
|
|
54
|
-
|
|
55
|
-
// app/api/auth/me/route.ts
|
|
56
|
-
import { auth } from "@/lib/auth";
|
|
57
|
-
export const GET = auth.handlers.me;
|
|
44
|
+
```bash
|
|
45
|
+
# .env.local
|
|
46
|
+
JWT_SECRET=your-random-secret-here
|
|
58
47
|
```
|
|
59
48
|
|
|
60
|
-
###
|
|
49
|
+
### 2. Add one route file
|
|
61
50
|
|
|
62
51
|
```ts
|
|
63
|
-
//
|
|
64
|
-
import {
|
|
65
|
-
|
|
66
|
-
export default auth.middleware({
|
|
67
|
-
protect: ["/dashboard", "/settings"],
|
|
68
|
-
redirectTo: "/login", // optional, default is "/login"
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export const config = {
|
|
72
|
-
matcher: ["/((?!_next|api/auth).*)"],
|
|
73
|
-
};
|
|
52
|
+
// app/api/auth/[...liteauth]/route.ts
|
|
53
|
+
import { handlers } from "@/auth";
|
|
54
|
+
export const { GET, POST } = handlers;
|
|
74
55
|
```
|
|
75
56
|
|
|
76
|
-
###
|
|
57
|
+
### 3. Wrap root layout
|
|
77
58
|
|
|
78
59
|
```tsx
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
import { useLiteAuth } from "next-lite-auth/client";
|
|
82
|
-
|
|
83
|
-
export default function LoginPage() {
|
|
84
|
-
const { user, loading, login, logout } = useLiteAuth();
|
|
85
|
-
|
|
86
|
-
if (loading) return <p>Loading...</p>;
|
|
87
|
-
|
|
88
|
-
if (user) {
|
|
89
|
-
return (
|
|
90
|
-
<div>
|
|
91
|
-
<p>Hello, {user.name ?? user.email}</p>
|
|
92
|
-
<button onClick={logout}>Logout</button>
|
|
93
|
-
</div>
|
|
94
|
-
);
|
|
95
|
-
}
|
|
60
|
+
// app/layout.tsx
|
|
61
|
+
import { LiteAuthProvider } from "next-lite-auth/client";
|
|
96
62
|
|
|
63
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
97
64
|
return (
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
if (result.error) alert(result.error);
|
|
107
|
-
}}
|
|
108
|
-
>
|
|
109
|
-
<input name="email" type="email" placeholder="Email" />
|
|
110
|
-
<input name="password" type="password" placeholder="Password" />
|
|
111
|
-
<button type="submit">Login</button>
|
|
112
|
-
</form>
|
|
65
|
+
<html>
|
|
66
|
+
<body>
|
|
67
|
+
<LiteAuthProvider protect={["/dashboard", "/settings"]}>
|
|
68
|
+
{children}
|
|
69
|
+
</LiteAuthProvider>
|
|
70
|
+
</body>
|
|
71
|
+
</html>
|
|
113
72
|
);
|
|
114
73
|
}
|
|
115
74
|
```
|
|
116
75
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
// app/dashboard/page.tsx
|
|
121
|
-
import { cookies } from "next/headers";
|
|
122
|
-
import { auth } from "@/lib/auth";
|
|
123
|
-
|
|
124
|
-
export default async function DashboardPage() {
|
|
125
|
-
const user = await auth.getUserFromCookies(cookies());
|
|
126
|
-
return <p>Welcome, {user?.name}</p>;
|
|
127
|
-
}
|
|
128
|
-
```
|
|
76
|
+
**Done.** Visiting a protected route while logged out automatically shows the built-in login UI. After login, the original page renders instantly — no redirects, no separate login page needed.
|
|
129
77
|
|
|
130
78
|
---
|
|
131
79
|
|
|
132
|
-
##
|
|
80
|
+
## Tailwind setup
|
|
133
81
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
| Option | Type | Required | Description |
|
|
137
|
-
|---|---|---|---|
|
|
138
|
-
| `users` | `User[]` | Yes | Hardcoded list of users |
|
|
139
|
-
| `jwtSecret` | `string` | Yes | Secret used to sign JWTs |
|
|
140
|
-
| `cookieName` | `string` | No | Cookie name (default: `"lite-auth-token"`) |
|
|
141
|
-
|
|
142
|
-
Returns `{ handlers, middleware, getUserFromCookies }`.
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
### `User` type
|
|
82
|
+
Add the library to your Tailwind `content` config so login UI styles are included:
|
|
147
83
|
|
|
148
84
|
```ts
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
85
|
+
// tailwind.config.ts
|
|
86
|
+
content: [
|
|
87
|
+
"./app/**/*.{ts,tsx}",
|
|
88
|
+
"./node_modules/next-lite-auth/dist/**/*.{js,mjs}",
|
|
89
|
+
]
|
|
155
90
|
```
|
|
156
91
|
|
|
157
92
|
---
|
|
158
93
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
| Handler | Method | Route |
|
|
162
|
-
|---|---|---|
|
|
163
|
-
| `login` | POST | `/api/auth/login` |
|
|
164
|
-
| `logout` | POST | `/api/auth/logout` |
|
|
165
|
-
| `me` | GET | `/api/auth/me` |
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
### `auth.middleware(options)`
|
|
94
|
+
## Optional: server-side middleware
|
|
170
95
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
96
|
+
```ts
|
|
97
|
+
// middleware.ts
|
|
98
|
+
import { middleware } from "@/auth";
|
|
99
|
+
export default middleware({ protect: ["/dashboard", "/settings"] });
|
|
175
100
|
|
|
176
|
-
|
|
101
|
+
export const config = {
|
|
102
|
+
matcher: ["/((?!_next|api/auth).*)"],
|
|
103
|
+
};
|
|
104
|
+
```
|
|
177
105
|
|
|
178
106
|
---
|
|
179
107
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
Server-side helper. Accepts Next.js `cookies()` and returns the current `PublicUser` or `null`.
|
|
108
|
+
## Use anywhere
|
|
183
109
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
110
|
+
```tsx
|
|
111
|
+
"use client";
|
|
112
|
+
import { useLiteAuth } from "next-lite-auth/client";
|
|
187
113
|
|
|
188
|
-
|
|
114
|
+
export function Navbar() {
|
|
115
|
+
const { user, logout } = useLiteAuth();
|
|
116
|
+
if (!user) return null;
|
|
117
|
+
return (
|
|
118
|
+
<div>
|
|
119
|
+
<span>{user.name ?? user.email}</span>
|
|
120
|
+
<button onClick={logout}>Logout</button>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
189
125
|
|
|
190
126
|
```ts
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
mePath: "/api/auth/me", // optional
|
|
195
|
-
});
|
|
196
|
-
```
|
|
127
|
+
// Server Component
|
|
128
|
+
import { cookies } from "next/headers";
|
|
129
|
+
import { getUserFromCookies } from "@/auth";
|
|
197
130
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
| `user` | `PublicUser \| null` | Current authenticated user |
|
|
201
|
-
| `loading` | `boolean` | True while fetching session |
|
|
202
|
-
| `login(creds)` | `Promise<{ error?: string }>` | Logs in and sets user state |
|
|
203
|
-
| `logout()` | `Promise<void>` | Clears cookie and user state |
|
|
131
|
+
const user = await getUserFromCookies(cookies());
|
|
132
|
+
```
|
|
204
133
|
|
|
205
134
|
---
|
|
206
135
|
|
|
207
|
-
##
|
|
136
|
+
## API
|
|
208
137
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
138
|
+
| Export | Description |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `createLiteAuth(config)` | Factory — returns `handlers`, `middleware`, `getUserFromCookies` |
|
|
141
|
+
| `handlers.GET / POST` | Catch-all route handlers |
|
|
142
|
+
| `middleware(options)` | Edge middleware for server-side route protection |
|
|
143
|
+
| `getUserFromCookies(cookies)` | Server-side session helper |
|
|
144
|
+
| `LiteAuthProvider` | Root provider — manages session and auto-shows login UI |
|
|
145
|
+
| `useLiteAuth()` | Hook — `user`, `loading`, `login`, `logout` |
|
|
214
146
|
|
|
215
147
|
---
|
|
216
148
|
|
package/dist/client.d.mts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
1
4
|
type User = {
|
|
2
5
|
email: string;
|
|
3
6
|
password: string;
|
|
@@ -6,25 +9,31 @@ type User = {
|
|
|
6
9
|
};
|
|
7
10
|
type PublicUser = Omit<User, "password">;
|
|
8
11
|
|
|
9
|
-
type
|
|
12
|
+
type LiteAuthContextValue = {
|
|
10
13
|
user: PublicUser | null;
|
|
11
14
|
loading: boolean;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
type UseLiteAuthReturn = LiteAuthState & {
|
|
18
|
-
login: (credentials: LoginCredentials) => Promise<{
|
|
15
|
+
login: (creds: {
|
|
16
|
+
email: string;
|
|
17
|
+
password: string;
|
|
18
|
+
}) => Promise<{
|
|
19
19
|
error?: string;
|
|
20
20
|
}>;
|
|
21
21
|
logout: () => Promise<void>;
|
|
22
22
|
};
|
|
23
|
-
type
|
|
23
|
+
type LiteAuthProviderProps = {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
protect?: string[];
|
|
24
26
|
loginPath?: string;
|
|
25
27
|
logoutPath?: string;
|
|
26
28
|
mePath?: string;
|
|
27
29
|
};
|
|
28
|
-
declare function
|
|
30
|
+
declare function LiteAuthProvider({ children, protect, loginPath, logoutPath, mePath, }: LiteAuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
31
|
+
declare function useLiteAuth(): LiteAuthContextValue;
|
|
32
|
+
|
|
33
|
+
type LiteLoginPageProps = {
|
|
34
|
+
title?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
};
|
|
37
|
+
declare function LiteLoginPage({ title, description, }: LiteLoginPageProps): react_jsx_runtime.JSX.Element;
|
|
29
38
|
|
|
30
|
-
export { useLiteAuth };
|
|
39
|
+
export { LiteAuthProvider, LiteLoginPage, useLiteAuth };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
1
4
|
type User = {
|
|
2
5
|
email: string;
|
|
3
6
|
password: string;
|
|
@@ -6,25 +9,31 @@ type User = {
|
|
|
6
9
|
};
|
|
7
10
|
type PublicUser = Omit<User, "password">;
|
|
8
11
|
|
|
9
|
-
type
|
|
12
|
+
type LiteAuthContextValue = {
|
|
10
13
|
user: PublicUser | null;
|
|
11
14
|
loading: boolean;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
type UseLiteAuthReturn = LiteAuthState & {
|
|
18
|
-
login: (credentials: LoginCredentials) => Promise<{
|
|
15
|
+
login: (creds: {
|
|
16
|
+
email: string;
|
|
17
|
+
password: string;
|
|
18
|
+
}) => Promise<{
|
|
19
19
|
error?: string;
|
|
20
20
|
}>;
|
|
21
21
|
logout: () => Promise<void>;
|
|
22
22
|
};
|
|
23
|
-
type
|
|
23
|
+
type LiteAuthProviderProps = {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
protect?: string[];
|
|
24
26
|
loginPath?: string;
|
|
25
27
|
logoutPath?: string;
|
|
26
28
|
mePath?: string;
|
|
27
29
|
};
|
|
28
|
-
declare function
|
|
30
|
+
declare function LiteAuthProvider({ children, protect, loginPath, logoutPath, mePath, }: LiteAuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
31
|
+
declare function useLiteAuth(): LiteAuthContextValue;
|
|
32
|
+
|
|
33
|
+
type LiteLoginPageProps = {
|
|
34
|
+
title?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
};
|
|
37
|
+
declare function LiteLoginPage({ title, description, }: LiteLoginPageProps): react_jsx_runtime.JSX.Element;
|
|
29
38
|
|
|
30
|
-
export { useLiteAuth };
|
|
39
|
+
export { LiteAuthProvider, LiteLoginPage, useLiteAuth };
|
package/dist/client.js
CHANGED
|
@@ -20,23 +20,123 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/client/index.ts
|
|
21
21
|
var client_exports = {};
|
|
22
22
|
__export(client_exports, {
|
|
23
|
+
LiteAuthProvider: () => LiteAuthProvider,
|
|
24
|
+
LiteLoginPage: () => LiteLoginPage,
|
|
23
25
|
useLiteAuth: () => useLiteAuth
|
|
24
26
|
});
|
|
25
27
|
module.exports = __toCommonJS(client_exports);
|
|
26
28
|
|
|
27
|
-
// src/client/
|
|
29
|
+
// src/client/LiteAuthProvider.tsx
|
|
30
|
+
var import_react2 = require("react");
|
|
31
|
+
var import_navigation = require("next/navigation");
|
|
32
|
+
|
|
33
|
+
// src/client/LiteLoginPage.tsx
|
|
28
34
|
var import_react = require("react");
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} =
|
|
35
|
-
const [
|
|
36
|
-
(0, import_react.
|
|
37
|
-
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
function LiteLoginPage({
|
|
37
|
+
title = "Sign in",
|
|
38
|
+
description = "Enter your credentials to continue"
|
|
39
|
+
}) {
|
|
40
|
+
const { login } = useLiteAuth();
|
|
41
|
+
const [error, setError] = (0, import_react.useState)("");
|
|
42
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
43
|
+
async function handleSubmit(e) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
setError("");
|
|
46
|
+
setLoading(true);
|
|
47
|
+
const form = e.currentTarget;
|
|
48
|
+
const result = await login({
|
|
49
|
+
email: form.elements.namedItem("email").value,
|
|
50
|
+
password: form.elements.namedItem("password").value
|
|
51
|
+
});
|
|
52
|
+
setLoading(false);
|
|
53
|
+
if (result.error) {
|
|
54
|
+
setError(result.error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-screen flex items-center justify-center bg-background px-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full max-w-sm space-y-6", children: [
|
|
58
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1 text-center", children: [
|
|
59
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
|
|
60
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground", children: description })
|
|
61
|
+
] }),
|
|
62
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-xl border bg-card text-card-foreground shadow-sm p-6 space-y-4", children: [
|
|
63
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
|
|
64
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
65
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1.5", children: [
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "email", className: "text-sm font-medium leading-none", children: "Email" }),
|
|
67
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
68
|
+
"input",
|
|
69
|
+
{
|
|
70
|
+
id: "email",
|
|
71
|
+
name: "email",
|
|
72
|
+
type: "email",
|
|
73
|
+
required: true,
|
|
74
|
+
autoComplete: "email",
|
|
75
|
+
placeholder: "you@example.com",
|
|
76
|
+
className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1.5", children: [
|
|
81
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "password", className: "text-sm font-medium leading-none", children: "Password" }),
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
83
|
+
"input",
|
|
84
|
+
{
|
|
85
|
+
id: "password",
|
|
86
|
+
name: "password",
|
|
87
|
+
type: "password",
|
|
88
|
+
required: true,
|
|
89
|
+
autoComplete: "current-password",
|
|
90
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
91
|
+
className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
type: "submit",
|
|
99
|
+
disabled: loading,
|
|
100
|
+
className: "inline-flex h-9 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 transition-colors",
|
|
101
|
+
children: loading ? "Signing in\u2026" : "Sign in"
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-center text-xs text-muted-foreground", children: [
|
|
107
|
+
"Powered by",
|
|
108
|
+
" ",
|
|
109
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
110
|
+
"a",
|
|
111
|
+
{
|
|
112
|
+
href: "https://github.com/amide-init/next-lite-auth",
|
|
113
|
+
target: "_blank",
|
|
114
|
+
rel: "noopener noreferrer",
|
|
115
|
+
className: "underline underline-offset-4 hover:text-primary transition-colors",
|
|
116
|
+
children: "next-lite-auth"
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
] })
|
|
120
|
+
] }) });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/client/LiteAuthProvider.tsx
|
|
124
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
125
|
+
var LiteAuthContext = (0, import_react2.createContext)(null);
|
|
126
|
+
function LiteAuthProvider({
|
|
127
|
+
children,
|
|
128
|
+
protect = [],
|
|
129
|
+
loginPath = "/api/auth/login",
|
|
130
|
+
logoutPath = "/api/auth/logout",
|
|
131
|
+
mePath = "/api/auth/me"
|
|
132
|
+
}) {
|
|
133
|
+
const [user, setUser] = (0, import_react2.useState)(null);
|
|
134
|
+
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
135
|
+
const pathname = (0, import_navigation.usePathname)();
|
|
136
|
+
(0, import_react2.useEffect)(() => {
|
|
137
|
+
fetch(mePath).then((r) => r.json()).then(({ user: user2 }) => setUser(user2 ?? null)).catch(() => setUser(null)).finally(() => setLoading(false));
|
|
38
138
|
}, [mePath]);
|
|
39
|
-
const login = (0,
|
|
139
|
+
const login = (0, import_react2.useCallback)(
|
|
40
140
|
async ({ email, password }) => {
|
|
41
141
|
const res = await fetch(loginPath, {
|
|
42
142
|
method: "POST",
|
|
@@ -45,19 +145,36 @@ function useLiteAuth(options = {}) {
|
|
|
45
145
|
});
|
|
46
146
|
const data = await res.json();
|
|
47
147
|
if (!res.ok) return { error: data.error ?? "Login failed" };
|
|
48
|
-
|
|
148
|
+
setUser(data.user);
|
|
49
149
|
return {};
|
|
50
150
|
},
|
|
51
151
|
[loginPath]
|
|
52
152
|
);
|
|
53
|
-
const logout = (0,
|
|
153
|
+
const logout = (0, import_react2.useCallback)(async () => {
|
|
54
154
|
await fetch(logoutPath, { method: "POST" });
|
|
55
|
-
|
|
155
|
+
setUser(null);
|
|
56
156
|
}, [logoutPath]);
|
|
57
|
-
|
|
157
|
+
const value = { user, loading, login, logout };
|
|
158
|
+
const isProtected = protect.some(
|
|
159
|
+
(p) => pathname === p || pathname.startsWith(p + "/")
|
|
160
|
+
);
|
|
161
|
+
if (loading) {
|
|
162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LiteAuthContext.Provider, { value, children });
|
|
163
|
+
}
|
|
164
|
+
if (!user && isProtected) {
|
|
165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LiteAuthContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LiteLoginPage, {}) });
|
|
166
|
+
}
|
|
167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LiteAuthContext.Provider, { value, children });
|
|
168
|
+
}
|
|
169
|
+
function useLiteAuth() {
|
|
170
|
+
const ctx = (0, import_react2.useContext)(LiteAuthContext);
|
|
171
|
+
if (!ctx) throw new Error("useLiteAuth must be used inside <LiteAuthProvider>");
|
|
172
|
+
return ctx;
|
|
58
173
|
}
|
|
59
174
|
// Annotate the CommonJS export names for ESM import in node:
|
|
60
175
|
0 && (module.exports = {
|
|
176
|
+
LiteAuthProvider,
|
|
177
|
+
LiteLoginPage,
|
|
61
178
|
useLiteAuth
|
|
62
179
|
});
|
|
63
180
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/index.ts","../src/client/useLiteAuth.ts"],"sourcesContent":["export { useLiteAuth } from \"./useLiteAuth\";\n","\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { PublicUser } from \"../core/types\";\n\ntype LiteAuthState = {\n user: PublicUser | null;\n loading: boolean;\n};\n\ntype LoginCredentials = {\n email: string;\n password: string;\n};\n\ntype UseLiteAuthReturn = LiteAuthState & {\n login: (credentials: LoginCredentials) => Promise<{ error?: string }>;\n logout: () => Promise<void>;\n};\n\ntype UseLiteAuthOptions = {\n loginPath?: string;\n logoutPath?: string;\n mePath?: string;\n};\n\nexport function useLiteAuth(options: UseLiteAuthOptions = {}): UseLiteAuthReturn {\n const {\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n mePath = \"/api/auth/me\",\n } = options;\n\n const [state, setState] = useState<LiteAuthState>({ user: null, loading: true });\n\n useEffect(() => {\n fetch(mePath)\n .then((r) => r.json())\n .then(({ user }) => setState({ user: user ?? null, loading: false }))\n .catch(() => setState({ user: null, loading: false }));\n }, [mePath]);\n\n const login = useCallback(\n async ({ email, password }: LoginCredentials) => {\n const res = await fetch(loginPath, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n const data = await res.json();\n if (!res.ok) return { error: data.error ?? \"Login failed\" };\n setState({ user: data.user, loading: false });\n return {};\n },\n [loginPath]\n );\n\n const logout = useCallback(async () => {\n await fetch(logoutPath, { method: \"POST\" });\n setState({ user: null, loading: false });\n }, [logoutPath]);\n\n return { ...state, login, logout };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,mBAAiD;AAwB1C,SAAS,YAAY,UAA8B,CAAC,GAAsB;AAC/E,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,EAAE,MAAM,MAAM,SAAS,KAAK,CAAC;AAE/E,8BAAU,MAAM;AACd,UAAM,MAAM,EACT,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EACpB,KAAK,CAAC,EAAE,KAAK,MAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,SAAS,MAAM,CAAC,CAAC,EACnE,MAAM,MAAM,SAAS,EAAE,MAAM,MAAM,SAAS,MAAM,CAAC,CAAC;AAAA,EACzD,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,YAAQ;AAAA,IACZ,OAAO,EAAE,OAAO,SAAS,MAAwB;AAC/C,YAAM,MAAM,MAAM,MAAM,WAAW;AAAA,QACjC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,MAC1C,CAAC;AACD,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,CAAC,IAAI,GAAI,QAAO,EAAE,OAAO,KAAK,SAAS,eAAe;AAC1D,eAAS,EAAE,MAAM,KAAK,MAAM,SAAS,MAAM,CAAC;AAC5C,aAAO,CAAC;AAAA,IACV;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,aAAS,0BAAY,YAAY;AACrC,UAAM,MAAM,YAAY,EAAE,QAAQ,OAAO,CAAC;AAC1C,aAAS,EAAE,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,EACzC,GAAG,CAAC,UAAU,CAAC;AAEf,SAAO,EAAE,GAAG,OAAO,OAAO,OAAO;AACnC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client/index.ts","../src/client/LiteAuthProvider.tsx","../src/client/LiteLoginPage.tsx"],"sourcesContent":["export { LiteAuthProvider, useLiteAuth } from \"./LiteAuthProvider\";\nexport { LiteLoginPage } from \"./LiteLoginPage\";\n","\"use client\";\n\nimport { createContext, useContext, useState, useEffect, useCallback, ReactNode } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { PublicUser } from \"../core/types\";\nimport { LiteLoginPage } from \"./LiteLoginPage\";\n\ntype LiteAuthContextValue = {\n user: PublicUser | null;\n loading: boolean;\n login: (creds: { email: string; password: string }) => Promise<{ error?: string }>;\n logout: () => Promise<void>;\n};\n\nconst LiteAuthContext = createContext<LiteAuthContextValue | null>(null);\n\ntype LiteAuthProviderProps = {\n children: ReactNode;\n protect?: string[];\n loginPath?: string;\n logoutPath?: string;\n mePath?: string;\n};\n\nexport function LiteAuthProvider({\n children,\n protect = [],\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n mePath = \"/api/auth/me\",\n}: LiteAuthProviderProps) {\n const [user, setUser] = useState<PublicUser | null>(null);\n const [loading, setLoading] = useState(true);\n const pathname = usePathname();\n\n useEffect(() => {\n fetch(mePath)\n .then((r) => r.json())\n .then(({ user }) => setUser(user ?? null))\n .catch(() => setUser(null))\n .finally(() => setLoading(false));\n }, [mePath]);\n\n const login = useCallback(\n async ({ email, password }: { email: string; password: string }) => {\n const res = await fetch(loginPath, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n const data = await res.json();\n if (!res.ok) return { error: data.error ?? \"Login failed\" };\n setUser(data.user);\n return {};\n },\n [loginPath]\n );\n\n const logout = useCallback(async () => {\n await fetch(logoutPath, { method: \"POST\" });\n setUser(null);\n }, [logoutPath]);\n\n const value = { user, loading, login, logout };\n\n const isProtected = protect.some(\n (p) => pathname === p || pathname.startsWith(p + \"/\")\n );\n\n if (loading) {\n return (\n <LiteAuthContext.Provider value={value}>\n {children}\n </LiteAuthContext.Provider>\n );\n }\n\n if (!user && isProtected) {\n return (\n <LiteAuthContext.Provider value={value}>\n <LiteLoginPage />\n </LiteAuthContext.Provider>\n );\n }\n\n return (\n <LiteAuthContext.Provider value={value}>\n {children}\n </LiteAuthContext.Provider>\n );\n}\n\nexport function useLiteAuth(): LiteAuthContextValue {\n const ctx = useContext(LiteAuthContext);\n if (!ctx) throw new Error(\"useLiteAuth must be used inside <LiteAuthProvider>\");\n return ctx;\n}\n","\"use client\";\n\nimport { FormEvent, useState } from \"react\";\nimport { useLiteAuth } from \"./LiteAuthProvider\";\n\ntype LiteLoginPageProps = {\n title?: string;\n description?: string;\n};\n\nexport function LiteLoginPage({\n title = \"Sign in\",\n description = \"Enter your credentials to continue\",\n}: LiteLoginPageProps) {\n const { login } = useLiteAuth();\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n async function handleSubmit(e: FormEvent<HTMLFormElement>) {\n e.preventDefault();\n setError(\"\");\n setLoading(true);\n\n const form = e.currentTarget;\n const result = await login({\n email: (form.elements.namedItem(\"email\") as HTMLInputElement).value,\n password: (form.elements.namedItem(\"password\") as HTMLInputElement).value,\n });\n\n setLoading(false);\n\n if (result.error) {\n setError(result.error);\n }\n // no redirect needed — login() sets user in context,\n // LiteAuthProvider re-renders children automatically\n }\n\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-background px-4\">\n <div className=\"w-full max-w-sm space-y-6\">\n\n <div className=\"space-y-1 text-center\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">{title}</h1>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n\n <div className=\"rounded-xl border bg-card text-card-foreground shadow-sm p-6 space-y-4\">\n {error && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"email\" className=\"text-sm font-medium leading-none\">\n Email\n </label>\n <input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n required\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <label htmlFor=\"password\" className=\"text-sm font-medium leading-none\">\n Password\n </label>\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n required\n autoComplete=\"current-password\"\n placeholder=\"••••••••\"\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={loading}\n className=\"inline-flex h-9 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 transition-colors\"\n >\n {loading ? \"Signing in…\" : \"Sign in\"}\n </button>\n </form>\n </div>\n\n <p className=\"text-center text-xs text-muted-foreground\">\n Powered by{\" \"}\n <a\n href=\"https://github.com/amide-init/next-lite-auth\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline underline-offset-4 hover:text-primary transition-colors\"\n >\n next-lite-auth\n </a>\n </p>\n\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","user"]}
|
package/dist/client.mjs
CHANGED
|
@@ -1,14 +1,112 @@
|
|
|
1
|
-
// src/client/
|
|
2
|
-
import { useState, useEffect, useCallback } from "react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
// src/client/LiteAuthProvider.tsx
|
|
2
|
+
import { createContext, useContext, useState as useState2, useEffect, useCallback } from "react";
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
|
|
5
|
+
// src/client/LiteLoginPage.tsx
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function LiteLoginPage({
|
|
9
|
+
title = "Sign in",
|
|
10
|
+
description = "Enter your credentials to continue"
|
|
11
|
+
}) {
|
|
12
|
+
const { login } = useLiteAuth();
|
|
13
|
+
const [error, setError] = useState("");
|
|
14
|
+
const [loading, setLoading] = useState(false);
|
|
15
|
+
async function handleSubmit(e) {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
setError("");
|
|
18
|
+
setLoading(true);
|
|
19
|
+
const form = e.currentTarget;
|
|
20
|
+
const result = await login({
|
|
21
|
+
email: form.elements.namedItem("email").value,
|
|
22
|
+
password: form.elements.namedItem("password").value
|
|
23
|
+
});
|
|
24
|
+
setLoading(false);
|
|
25
|
+
if (result.error) {
|
|
26
|
+
setError(result.error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-background px-4", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-sm space-y-6", children: [
|
|
30
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1 text-center", children: [
|
|
31
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
|
|
32
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
|
|
33
|
+
] }),
|
|
34
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card text-card-foreground shadow-sm p-6 space-y-4", children: [
|
|
35
|
+
error && /* @__PURE__ */ jsx("div", { className: "rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
|
|
36
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
38
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "email", className: "text-sm font-medium leading-none", children: "Email" }),
|
|
39
|
+
/* @__PURE__ */ jsx(
|
|
40
|
+
"input",
|
|
41
|
+
{
|
|
42
|
+
id: "email",
|
|
43
|
+
name: "email",
|
|
44
|
+
type: "email",
|
|
45
|
+
required: true,
|
|
46
|
+
autoComplete: "email",
|
|
47
|
+
placeholder: "you@example.com",
|
|
48
|
+
className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
] }),
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
53
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "password", className: "text-sm font-medium leading-none", children: "Password" }),
|
|
54
|
+
/* @__PURE__ */ jsx(
|
|
55
|
+
"input",
|
|
56
|
+
{
|
|
57
|
+
id: "password",
|
|
58
|
+
name: "password",
|
|
59
|
+
type: "password",
|
|
60
|
+
required: true,
|
|
61
|
+
autoComplete: "current-password",
|
|
62
|
+
placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
|
63
|
+
className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
] }),
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
"button",
|
|
69
|
+
{
|
|
70
|
+
type: "submit",
|
|
71
|
+
disabled: loading,
|
|
72
|
+
className: "inline-flex h-9 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 transition-colors",
|
|
73
|
+
children: loading ? "Signing in\u2026" : "Sign in"
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
] })
|
|
77
|
+
] }),
|
|
78
|
+
/* @__PURE__ */ jsxs("p", { className: "text-center text-xs text-muted-foreground", children: [
|
|
79
|
+
"Powered by",
|
|
80
|
+
" ",
|
|
81
|
+
/* @__PURE__ */ jsx(
|
|
82
|
+
"a",
|
|
83
|
+
{
|
|
84
|
+
href: "https://github.com/amide-init/next-lite-auth",
|
|
85
|
+
target: "_blank",
|
|
86
|
+
rel: "noopener noreferrer",
|
|
87
|
+
className: "underline underline-offset-4 hover:text-primary transition-colors",
|
|
88
|
+
children: "next-lite-auth"
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
] })
|
|
92
|
+
] }) });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/client/LiteAuthProvider.tsx
|
|
96
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
97
|
+
var LiteAuthContext = createContext(null);
|
|
98
|
+
function LiteAuthProvider({
|
|
99
|
+
children,
|
|
100
|
+
protect = [],
|
|
101
|
+
loginPath = "/api/auth/login",
|
|
102
|
+
logoutPath = "/api/auth/logout",
|
|
103
|
+
mePath = "/api/auth/me"
|
|
104
|
+
}) {
|
|
105
|
+
const [user, setUser] = useState2(null);
|
|
106
|
+
const [loading, setLoading] = useState2(true);
|
|
107
|
+
const pathname = usePathname();
|
|
10
108
|
useEffect(() => {
|
|
11
|
-
fetch(mePath).then((r) => r.json()).then(({ user }) =>
|
|
109
|
+
fetch(mePath).then((r) => r.json()).then(({ user: user2 }) => setUser(user2 ?? null)).catch(() => setUser(null)).finally(() => setLoading(false));
|
|
12
110
|
}, [mePath]);
|
|
13
111
|
const login = useCallback(
|
|
14
112
|
async ({ email, password }) => {
|
|
@@ -19,18 +117,35 @@ function useLiteAuth(options = {}) {
|
|
|
19
117
|
});
|
|
20
118
|
const data = await res.json();
|
|
21
119
|
if (!res.ok) return { error: data.error ?? "Login failed" };
|
|
22
|
-
|
|
120
|
+
setUser(data.user);
|
|
23
121
|
return {};
|
|
24
122
|
},
|
|
25
123
|
[loginPath]
|
|
26
124
|
);
|
|
27
125
|
const logout = useCallback(async () => {
|
|
28
126
|
await fetch(logoutPath, { method: "POST" });
|
|
29
|
-
|
|
127
|
+
setUser(null);
|
|
30
128
|
}, [logoutPath]);
|
|
31
|
-
|
|
129
|
+
const value = { user, loading, login, logout };
|
|
130
|
+
const isProtected = protect.some(
|
|
131
|
+
(p) => pathname === p || pathname.startsWith(p + "/")
|
|
132
|
+
);
|
|
133
|
+
if (loading) {
|
|
134
|
+
return /* @__PURE__ */ jsx2(LiteAuthContext.Provider, { value, children });
|
|
135
|
+
}
|
|
136
|
+
if (!user && isProtected) {
|
|
137
|
+
return /* @__PURE__ */ jsx2(LiteAuthContext.Provider, { value, children: /* @__PURE__ */ jsx2(LiteLoginPage, {}) });
|
|
138
|
+
}
|
|
139
|
+
return /* @__PURE__ */ jsx2(LiteAuthContext.Provider, { value, children });
|
|
140
|
+
}
|
|
141
|
+
function useLiteAuth() {
|
|
142
|
+
const ctx = useContext(LiteAuthContext);
|
|
143
|
+
if (!ctx) throw new Error("useLiteAuth must be used inside <LiteAuthProvider>");
|
|
144
|
+
return ctx;
|
|
32
145
|
}
|
|
33
146
|
export {
|
|
147
|
+
LiteAuthProvider,
|
|
148
|
+
LiteLoginPage,
|
|
34
149
|
useLiteAuth
|
|
35
150
|
};
|
|
36
151
|
//# sourceMappingURL=client.mjs.map
|
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/useLiteAuth.ts"],"sourcesContent":["\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { PublicUser } from \"../core/types\";\n\ntype LiteAuthState = {\n user: PublicUser | null;\n loading: boolean;\n};\n\ntype LoginCredentials = {\n email: string;\n password: string;\n};\n\ntype UseLiteAuthReturn = LiteAuthState & {\n login: (credentials: LoginCredentials) => Promise<{ error?: string }>;\n logout: () => Promise<void>;\n};\n\ntype UseLiteAuthOptions = {\n loginPath?: string;\n logoutPath?: string;\n mePath?: string;\n};\n\nexport function useLiteAuth(options: UseLiteAuthOptions = {}): UseLiteAuthReturn {\n const {\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n mePath = \"/api/auth/me\",\n } = options;\n\n const [state, setState] = useState<LiteAuthState>({ user: null, loading: true });\n\n useEffect(() => {\n fetch(mePath)\n .then((r) => r.json())\n .then(({ user }) => setState({ user: user ?? null, loading: false }))\n .catch(() => setState({ user: null, loading: false }));\n }, [mePath]);\n\n const login = useCallback(\n async ({ email, password }: LoginCredentials) => {\n const res = await fetch(loginPath, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n const data = await res.json();\n if (!res.ok) return { error: data.error ?? \"Login failed\" };\n setState({ user: data.user, loading: false });\n return {};\n },\n [loginPath]\n );\n\n const logout = useCallback(async () => {\n await fetch(logoutPath, { method: \"POST\" });\n setState({ user: null, loading: false });\n }, [logoutPath]);\n\n return { ...state, login, logout };\n}\n"],"mappings":";AAEA,SAAS,UAAU,WAAW,mBAAmB;AAwB1C,SAAS,YAAY,UAA8B,CAAC,GAAsB;AAC/E,QAAM;AAAA,IACJ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,EAAE,MAAM,MAAM,SAAS,KAAK,CAAC;AAE/E,YAAU,MAAM;AACd,UAAM,MAAM,EACT,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EACpB,KAAK,CAAC,EAAE,KAAK,MAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,SAAS,MAAM,CAAC,CAAC,EACnE,MAAM,MAAM,SAAS,EAAE,MAAM,MAAM,SAAS,MAAM,CAAC,CAAC;AAAA,EACzD,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAAQ;AAAA,IACZ,OAAO,EAAE,OAAO,SAAS,MAAwB;AAC/C,YAAM,MAAM,MAAM,MAAM,WAAW;AAAA,QACjC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,MAC1C,CAAC;AACD,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,CAAC,IAAI,GAAI,QAAO,EAAE,OAAO,KAAK,SAAS,eAAe;AAC1D,eAAS,EAAE,MAAM,KAAK,MAAM,SAAS,MAAM,CAAC;AAC5C,aAAO,CAAC;AAAA,IACV;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,SAAS,YAAY,YAAY;AACrC,UAAM,MAAM,YAAY,EAAE,QAAQ,OAAO,CAAC;AAC1C,aAAS,EAAE,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,EACzC,GAAG,CAAC,UAAU,CAAC;AAEf,SAAO,EAAE,GAAG,OAAO,OAAO,OAAO;AACnC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client/LiteAuthProvider.tsx","../src/client/LiteLoginPage.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useState, useEffect, useCallback, ReactNode } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { PublicUser } from \"../core/types\";\nimport { LiteLoginPage } from \"./LiteLoginPage\";\n\ntype LiteAuthContextValue = {\n user: PublicUser | null;\n loading: boolean;\n login: (creds: { email: string; password: string }) => Promise<{ error?: string }>;\n logout: () => Promise<void>;\n};\n\nconst LiteAuthContext = createContext<LiteAuthContextValue | null>(null);\n\ntype LiteAuthProviderProps = {\n children: ReactNode;\n protect?: string[];\n loginPath?: string;\n logoutPath?: string;\n mePath?: string;\n};\n\nexport function LiteAuthProvider({\n children,\n protect = [],\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n mePath = \"/api/auth/me\",\n}: LiteAuthProviderProps) {\n const [user, setUser] = useState<PublicUser | null>(null);\n const [loading, setLoading] = useState(true);\n const pathname = usePathname();\n\n useEffect(() => {\n fetch(mePath)\n .then((r) => r.json())\n .then(({ user }) => setUser(user ?? null))\n .catch(() => setUser(null))\n .finally(() => setLoading(false));\n }, [mePath]);\n\n const login = useCallback(\n async ({ email, password }: { email: string; password: string }) => {\n const res = await fetch(loginPath, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n const data = await res.json();\n if (!res.ok) return { error: data.error ?? \"Login failed\" };\n setUser(data.user);\n return {};\n },\n [loginPath]\n );\n\n const logout = useCallback(async () => {\n await fetch(logoutPath, { method: \"POST\" });\n setUser(null);\n }, [logoutPath]);\n\n const value = { user, loading, login, logout };\n\n const isProtected = protect.some(\n (p) => pathname === p || pathname.startsWith(p + \"/\")\n );\n\n if (loading) {\n return (\n <LiteAuthContext.Provider value={value}>\n {children}\n </LiteAuthContext.Provider>\n );\n }\n\n if (!user && isProtected) {\n return (\n <LiteAuthContext.Provider value={value}>\n <LiteLoginPage />\n </LiteAuthContext.Provider>\n );\n }\n\n return (\n <LiteAuthContext.Provider value={value}>\n {children}\n </LiteAuthContext.Provider>\n );\n}\n\nexport function useLiteAuth(): LiteAuthContextValue {\n const ctx = useContext(LiteAuthContext);\n if (!ctx) throw new Error(\"useLiteAuth must be used inside <LiteAuthProvider>\");\n return ctx;\n}\n","\"use client\";\n\nimport { FormEvent, useState } from \"react\";\nimport { useLiteAuth } from \"./LiteAuthProvider\";\n\ntype LiteLoginPageProps = {\n title?: string;\n description?: string;\n};\n\nexport function LiteLoginPage({\n title = \"Sign in\",\n description = \"Enter your credentials to continue\",\n}: LiteLoginPageProps) {\n const { login } = useLiteAuth();\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n async function handleSubmit(e: FormEvent<HTMLFormElement>) {\n e.preventDefault();\n setError(\"\");\n setLoading(true);\n\n const form = e.currentTarget;\n const result = await login({\n email: (form.elements.namedItem(\"email\") as HTMLInputElement).value,\n password: (form.elements.namedItem(\"password\") as HTMLInputElement).value,\n });\n\n setLoading(false);\n\n if (result.error) {\n setError(result.error);\n }\n // no redirect needed — login() sets user in context,\n // LiteAuthProvider re-renders children automatically\n }\n\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-background px-4\">\n <div className=\"w-full max-w-sm space-y-6\">\n\n <div className=\"space-y-1 text-center\">\n <h1 className=\"text-2xl font-semibold tracking-tight\">{title}</h1>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n\n <div className=\"rounded-xl border bg-card text-card-foreground shadow-sm p-6 space-y-4\">\n {error && (\n <div className=\"rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"email\" className=\"text-sm font-medium leading-none\">\n Email\n </label>\n <input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n required\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <label htmlFor=\"password\" className=\"text-sm font-medium leading-none\">\n Password\n </label>\n <input\n id=\"password\"\n name=\"password\"\n type=\"password\"\n required\n autoComplete=\"current-password\"\n placeholder=\"••••••••\"\n className=\"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\"\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={loading}\n className=\"inline-flex h-9 w-full items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 transition-colors\"\n >\n {loading ? \"Signing in…\" : \"Sign in\"}\n </button>\n </form>\n </div>\n\n <p className=\"text-center text-xs text-muted-foreground\">\n Powered by{\" \"}\n <a\n href=\"https://github.com/amide-init/next-lite-auth\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline underline-offset-4 hover:text-primary transition-colors\"\n >\n next-lite-auth\n </a>\n </p>\n\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useState","jsx","useState","user"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -16,9 +16,8 @@ type LiteAuthConfig = {
|
|
|
16
16
|
|
|
17
17
|
declare function createLiteAuth(config: LiteAuthConfig): {
|
|
18
18
|
handlers: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
me: (req: next_server.NextRequest) => Promise<next_server.NextResponse>;
|
|
19
|
+
GET: (req: next_server.NextRequest) => Promise<next_server.NextResponse>;
|
|
20
|
+
POST: (req: next_server.NextRequest) => Promise<next_server.NextResponse>;
|
|
22
21
|
};
|
|
23
22
|
middleware: (options: {
|
|
24
23
|
protect: string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -16,9 +16,8 @@ type LiteAuthConfig = {
|
|
|
16
16
|
|
|
17
17
|
declare function createLiteAuth(config: LiteAuthConfig): {
|
|
18
18
|
handlers: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
me: (req: next_server.NextRequest) => Promise<next_server.NextResponse>;
|
|
19
|
+
GET: (req: next_server.NextRequest) => Promise<next_server.NextResponse>;
|
|
20
|
+
POST: (req: next_server.NextRequest) => Promise<next_server.NextResponse>;
|
|
22
21
|
};
|
|
23
22
|
middleware: (options: {
|
|
24
23
|
protect: string[];
|
package/dist/index.js
CHANGED
|
@@ -45,8 +45,8 @@ async function verifyToken(token, secret) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// src/server/handlers.ts
|
|
48
|
-
function
|
|
49
|
-
|
|
48
|
+
function makeHandlers(ctx) {
|
|
49
|
+
async function login(req) {
|
|
50
50
|
let body;
|
|
51
51
|
try {
|
|
52
52
|
body = await req.json();
|
|
@@ -70,34 +70,33 @@ function makeLoginHandler(ctx) {
|
|
|
70
70
|
sameSite: "lax",
|
|
71
71
|
secure: process.env.NODE_ENV === "production",
|
|
72
72
|
maxAge: 60 * 60 * 24 * 7
|
|
73
|
-
// 7 days
|
|
74
73
|
});
|
|
75
74
|
return res;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function makeLogoutHandler(ctx) {
|
|
79
|
-
return async function logoutHandler(_req) {
|
|
75
|
+
}
|
|
76
|
+
async function logout(_req) {
|
|
80
77
|
const res = import_server.NextResponse.json({ ok: true });
|
|
81
|
-
res.cookies.set(ctx.cookieName, "", {
|
|
82
|
-
httpOnly: true,
|
|
83
|
-
path: "/",
|
|
84
|
-
maxAge: 0
|
|
85
|
-
});
|
|
78
|
+
res.cookies.set(ctx.cookieName, "", { httpOnly: true, path: "/", maxAge: 0 });
|
|
86
79
|
return res;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function makeMeHandler(ctx) {
|
|
90
|
-
return async function meHandler(req) {
|
|
80
|
+
}
|
|
81
|
+
async function me(req) {
|
|
91
82
|
const token = req.cookies.get(ctx.cookieName)?.value;
|
|
92
|
-
if (!token) {
|
|
93
|
-
return import_server.NextResponse.json({ user: null }, { status: 401 });
|
|
94
|
-
}
|
|
83
|
+
if (!token) return import_server.NextResponse.json({ user: null }, { status: 401 });
|
|
95
84
|
const user = await verifyToken(token, ctx.jwtSecret);
|
|
96
|
-
if (!user) {
|
|
97
|
-
return import_server.NextResponse.json({ user: null }, { status: 401 });
|
|
98
|
-
}
|
|
85
|
+
if (!user) return import_server.NextResponse.json({ user: null }, { status: 401 });
|
|
99
86
|
return import_server.NextResponse.json({ user });
|
|
100
|
-
}
|
|
87
|
+
}
|
|
88
|
+
async function GET(req) {
|
|
89
|
+
const action = req.nextUrl.pathname.split("/").pop();
|
|
90
|
+
if (action === "me") return me(req);
|
|
91
|
+
return import_server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
92
|
+
}
|
|
93
|
+
async function POST(req) {
|
|
94
|
+
const action = req.nextUrl.pathname.split("/").pop();
|
|
95
|
+
if (action === "login") return login(req);
|
|
96
|
+
if (action === "logout") return logout(req);
|
|
97
|
+
return import_server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
98
|
+
}
|
|
99
|
+
return { GET, POST };
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
// src/server/getUserFromCookies.ts
|
|
@@ -142,11 +141,7 @@ function createLiteAuth(config) {
|
|
|
142
141
|
cookieName: config.cookieName ?? "lite-auth-token"
|
|
143
142
|
};
|
|
144
143
|
return {
|
|
145
|
-
handlers:
|
|
146
|
-
login: makeLoginHandler(ctx),
|
|
147
|
-
logout: makeLogoutHandler(ctx),
|
|
148
|
-
me: makeMeHandler(ctx)
|
|
149
|
-
},
|
|
144
|
+
handlers: makeHandlers(ctx),
|
|
150
145
|
middleware: makeMiddleware(ctx),
|
|
151
146
|
getUserFromCookies: getUserFromCookies(ctx)
|
|
152
147
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/server/handlers.ts","../src/server/jwt.ts","../src/server/getUserFromCookies.ts","../src/middleware/index.ts","../src/core/createLiteAuth.ts"],"sourcesContent":["export { createLiteAuth } from \"./core/createLiteAuth\";\nexport type { User, PublicUser, LiteAuthConfig } from \"./core/types\";\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { LiteAuthContext } from \"../core/types\";\nimport { signToken, verifyToken } from \"./jwt\";\n\nexport function
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server/handlers.ts","../src/server/jwt.ts","../src/server/getUserFromCookies.ts","../src/middleware/index.ts","../src/core/createLiteAuth.ts"],"sourcesContent":["export { createLiteAuth } from \"./core/createLiteAuth\";\nexport type { User, PublicUser, LiteAuthConfig } from \"./core/types\";\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { LiteAuthContext } from \"../core/types\";\nimport { signToken, verifyToken } from \"./jwt\";\n\nexport function makeHandlers(ctx: LiteAuthContext) {\n async function login(req: NextRequest): Promise<NextResponse> {\n let body: { email?: string; password?: string };\n try {\n body = await req.json();\n } catch {\n return NextResponse.json({ error: \"Invalid JSON\" }, { status: 400 });\n }\n\n const { email, password } = body;\n if (!email || !password) {\n return NextResponse.json({ error: \"Email and password are required\" }, { status: 400 });\n }\n\n const user = ctx.users.find((u) => u.email === email && u.password === password);\n if (!user) {\n return NextResponse.json({ error: \"Invalid credentials\" }, { status: 401 });\n }\n\n const { password: _, ...publicUser } = user;\n const token = await signToken(publicUser, ctx.jwtSecret);\n\n const res = NextResponse.json({ user: publicUser });\n res.cookies.set(ctx.cookieName, token, {\n httpOnly: true,\n path: \"/\",\n sameSite: \"lax\",\n secure: process.env.NODE_ENV === \"production\",\n maxAge: 60 * 60 * 24 * 7,\n });\n return res;\n }\n\n async function logout(_req: NextRequest): Promise<NextResponse> {\n const res = NextResponse.json({ ok: true });\n res.cookies.set(ctx.cookieName, \"\", { httpOnly: true, path: \"/\", maxAge: 0 });\n return res;\n }\n\n async function me(req: NextRequest): Promise<NextResponse> {\n const token = req.cookies.get(ctx.cookieName)?.value;\n if (!token) return NextResponse.json({ user: null }, { status: 401 });\n const user = await verifyToken(token, ctx.jwtSecret);\n if (!user) return NextResponse.json({ user: null }, { status: 401 });\n return NextResponse.json({ user });\n }\n\n async function GET(req: NextRequest): Promise<NextResponse> {\n const action = req.nextUrl.pathname.split(\"/\").pop();\n if (action === \"me\") return me(req);\n return NextResponse.json({ error: \"Not found\" }, { status: 404 });\n }\n\n async function POST(req: NextRequest): Promise<NextResponse> {\n const action = req.nextUrl.pathname.split(\"/\").pop();\n if (action === \"login\") return login(req);\n if (action === \"logout\") return logout(req);\n return NextResponse.json({ error: \"Not found\" }, { status: 404 });\n }\n\n return { GET, POST };\n}\n","import { SignJWT, jwtVerify } from \"jose\";\nimport { PublicUser } from \"../core/types\";\n\nfunction getSecret(secret: string) {\n return new TextEncoder().encode(secret);\n}\n\nexport async function signToken(user: PublicUser, secret: string): Promise<string> {\n return new SignJWT({ ...user })\n .setProtectedHeader({ alg: \"HS256\" })\n .setIssuedAt()\n .setExpirationTime(\"7d\")\n .sign(getSecret(secret));\n}\n\nexport async function verifyToken(token: string, secret: string): Promise<PublicUser | null> {\n try {\n const { payload } = await jwtVerify(token, getSecret(secret));\n return payload as unknown as PublicUser;\n } catch {\n return null;\n }\n}\n","import { ReadonlyRequestCookies } from \"next/dist/server/web/spec-extension/adapters/request-cookies\";\nimport { LiteAuthContext, PublicUser } from \"../core/types\";\nimport { verifyToken } from \"./jwt\";\n\nexport function getUserFromCookies(ctx: LiteAuthContext) {\n return async function (cookies: ReadonlyRequestCookies): Promise<PublicUser | null> {\n const token = cookies.get(ctx.cookieName)?.value;\n if (!token) return null;\n return verifyToken(token, ctx.jwtSecret);\n };\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { LiteAuthContext } from \"../core/types\";\nimport { verifyToken } from \"../server/jwt\";\n\ntype MiddlewareOptions = {\n protect: string[];\n redirectTo?: string;\n};\n\nexport function makeMiddleware(ctx: LiteAuthContext) {\n return function middleware(options: MiddlewareOptions) {\n return async function (req: NextRequest): Promise<NextResponse> {\n const { protect, redirectTo = \"/login\" } = options;\n const { pathname } = req.nextUrl;\n\n const isProtected = protect.some(\n (pattern) => pathname === pattern || pathname.startsWith(pattern + \"/\")\n );\n\n if (!isProtected) {\n return NextResponse.next();\n }\n\n const token = req.cookies.get(ctx.cookieName)?.value;\n if (token) {\n const user = await verifyToken(token, ctx.jwtSecret);\n if (user) return NextResponse.next();\n }\n\n const loginUrl = new URL(redirectTo, req.url);\n loginUrl.searchParams.set(\"from\", pathname);\n return NextResponse.redirect(loginUrl);\n };\n };\n}\n","import { LiteAuthConfig, LiteAuthContext } from \"./types\";\nimport { makeHandlers, getUserFromCookies } from \"../server\";\nimport { makeMiddleware } from \"../middleware\";\n\nexport function createLiteAuth(config: LiteAuthConfig) {\n const ctx: LiteAuthContext = {\n users: config.users,\n jwtSecret: config.jwtSecret,\n cookieName: config.cookieName ?? \"lite-auth-token\",\n };\n\n return {\n handlers: makeHandlers(ctx),\n middleware: makeMiddleware(ctx),\n getUserFromCookies: getUserFromCookies(ctx),\n };\n}\n"],"mappings":"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","names":["import_server"]}
|
package/dist/index.mjs
CHANGED
|
@@ -19,8 +19,8 @@ async function verifyToken(token, secret) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// src/server/handlers.ts
|
|
22
|
-
function
|
|
23
|
-
|
|
22
|
+
function makeHandlers(ctx) {
|
|
23
|
+
async function login(req) {
|
|
24
24
|
let body;
|
|
25
25
|
try {
|
|
26
26
|
body = await req.json();
|
|
@@ -44,34 +44,33 @@ function makeLoginHandler(ctx) {
|
|
|
44
44
|
sameSite: "lax",
|
|
45
45
|
secure: process.env.NODE_ENV === "production",
|
|
46
46
|
maxAge: 60 * 60 * 24 * 7
|
|
47
|
-
// 7 days
|
|
48
47
|
});
|
|
49
48
|
return res;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function makeLogoutHandler(ctx) {
|
|
53
|
-
return async function logoutHandler(_req) {
|
|
49
|
+
}
|
|
50
|
+
async function logout(_req) {
|
|
54
51
|
const res = NextResponse.json({ ok: true });
|
|
55
|
-
res.cookies.set(ctx.cookieName, "", {
|
|
56
|
-
httpOnly: true,
|
|
57
|
-
path: "/",
|
|
58
|
-
maxAge: 0
|
|
59
|
-
});
|
|
52
|
+
res.cookies.set(ctx.cookieName, "", { httpOnly: true, path: "/", maxAge: 0 });
|
|
60
53
|
return res;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function makeMeHandler(ctx) {
|
|
64
|
-
return async function meHandler(req) {
|
|
54
|
+
}
|
|
55
|
+
async function me(req) {
|
|
65
56
|
const token = req.cookies.get(ctx.cookieName)?.value;
|
|
66
|
-
if (!token) {
|
|
67
|
-
return NextResponse.json({ user: null }, { status: 401 });
|
|
68
|
-
}
|
|
57
|
+
if (!token) return NextResponse.json({ user: null }, { status: 401 });
|
|
69
58
|
const user = await verifyToken(token, ctx.jwtSecret);
|
|
70
|
-
if (!user) {
|
|
71
|
-
return NextResponse.json({ user: null }, { status: 401 });
|
|
72
|
-
}
|
|
59
|
+
if (!user) return NextResponse.json({ user: null }, { status: 401 });
|
|
73
60
|
return NextResponse.json({ user });
|
|
74
|
-
}
|
|
61
|
+
}
|
|
62
|
+
async function GET(req) {
|
|
63
|
+
const action = req.nextUrl.pathname.split("/").pop();
|
|
64
|
+
if (action === "me") return me(req);
|
|
65
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
66
|
+
}
|
|
67
|
+
async function POST(req) {
|
|
68
|
+
const action = req.nextUrl.pathname.split("/").pop();
|
|
69
|
+
if (action === "login") return login(req);
|
|
70
|
+
if (action === "logout") return logout(req);
|
|
71
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
72
|
+
}
|
|
73
|
+
return { GET, POST };
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
// src/server/getUserFromCookies.ts
|
|
@@ -116,11 +115,7 @@ function createLiteAuth(config) {
|
|
|
116
115
|
cookieName: config.cookieName ?? "lite-auth-token"
|
|
117
116
|
};
|
|
118
117
|
return {
|
|
119
|
-
handlers:
|
|
120
|
-
login: makeLoginHandler(ctx),
|
|
121
|
-
logout: makeLogoutHandler(ctx),
|
|
122
|
-
me: makeMeHandler(ctx)
|
|
123
|
-
},
|
|
118
|
+
handlers: makeHandlers(ctx),
|
|
124
119
|
middleware: makeMiddleware(ctx),
|
|
125
120
|
getUserFromCookies: getUserFromCookies(ctx)
|
|
126
121
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/handlers.ts","../src/server/jwt.ts","../src/server/getUserFromCookies.ts","../src/middleware/index.ts","../src/core/createLiteAuth.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport { LiteAuthContext } from \"../core/types\";\nimport { signToken, verifyToken } from \"./jwt\";\n\nexport function
|
|
1
|
+
{"version":3,"sources":["../src/server/handlers.ts","../src/server/jwt.ts","../src/server/getUserFromCookies.ts","../src/middleware/index.ts","../src/core/createLiteAuth.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport { LiteAuthContext } from \"../core/types\";\nimport { signToken, verifyToken } from \"./jwt\";\n\nexport function makeHandlers(ctx: LiteAuthContext) {\n async function login(req: NextRequest): Promise<NextResponse> {\n let body: { email?: string; password?: string };\n try {\n body = await req.json();\n } catch {\n return NextResponse.json({ error: \"Invalid JSON\" }, { status: 400 });\n }\n\n const { email, password } = body;\n if (!email || !password) {\n return NextResponse.json({ error: \"Email and password are required\" }, { status: 400 });\n }\n\n const user = ctx.users.find((u) => u.email === email && u.password === password);\n if (!user) {\n return NextResponse.json({ error: \"Invalid credentials\" }, { status: 401 });\n }\n\n const { password: _, ...publicUser } = user;\n const token = await signToken(publicUser, ctx.jwtSecret);\n\n const res = NextResponse.json({ user: publicUser });\n res.cookies.set(ctx.cookieName, token, {\n httpOnly: true,\n path: \"/\",\n sameSite: \"lax\",\n secure: process.env.NODE_ENV === \"production\",\n maxAge: 60 * 60 * 24 * 7,\n });\n return res;\n }\n\n async function logout(_req: NextRequest): Promise<NextResponse> {\n const res = NextResponse.json({ ok: true });\n res.cookies.set(ctx.cookieName, \"\", { httpOnly: true, path: \"/\", maxAge: 0 });\n return res;\n }\n\n async function me(req: NextRequest): Promise<NextResponse> {\n const token = req.cookies.get(ctx.cookieName)?.value;\n if (!token) return NextResponse.json({ user: null }, { status: 401 });\n const user = await verifyToken(token, ctx.jwtSecret);\n if (!user) return NextResponse.json({ user: null }, { status: 401 });\n return NextResponse.json({ user });\n }\n\n async function GET(req: NextRequest): Promise<NextResponse> {\n const action = req.nextUrl.pathname.split(\"/\").pop();\n if (action === \"me\") return me(req);\n return NextResponse.json({ error: \"Not found\" }, { status: 404 });\n }\n\n async function POST(req: NextRequest): Promise<NextResponse> {\n const action = req.nextUrl.pathname.split(\"/\").pop();\n if (action === \"login\") return login(req);\n if (action === \"logout\") return logout(req);\n return NextResponse.json({ error: \"Not found\" }, { status: 404 });\n }\n\n return { GET, POST };\n}\n","import { SignJWT, jwtVerify } from \"jose\";\nimport { PublicUser } from \"../core/types\";\n\nfunction getSecret(secret: string) {\n return new TextEncoder().encode(secret);\n}\n\nexport async function signToken(user: PublicUser, secret: string): Promise<string> {\n return new SignJWT({ ...user })\n .setProtectedHeader({ alg: \"HS256\" })\n .setIssuedAt()\n .setExpirationTime(\"7d\")\n .sign(getSecret(secret));\n}\n\nexport async function verifyToken(token: string, secret: string): Promise<PublicUser | null> {\n try {\n const { payload } = await jwtVerify(token, getSecret(secret));\n return payload as unknown as PublicUser;\n } catch {\n return null;\n }\n}\n","import { ReadonlyRequestCookies } from \"next/dist/server/web/spec-extension/adapters/request-cookies\";\nimport { LiteAuthContext, PublicUser } from \"../core/types\";\nimport { verifyToken } from \"./jwt\";\n\nexport function getUserFromCookies(ctx: LiteAuthContext) {\n return async function (cookies: ReadonlyRequestCookies): Promise<PublicUser | null> {\n const token = cookies.get(ctx.cookieName)?.value;\n if (!token) return null;\n return verifyToken(token, ctx.jwtSecret);\n };\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { LiteAuthContext } from \"../core/types\";\nimport { verifyToken } from \"../server/jwt\";\n\ntype MiddlewareOptions = {\n protect: string[];\n redirectTo?: string;\n};\n\nexport function makeMiddleware(ctx: LiteAuthContext) {\n return function middleware(options: MiddlewareOptions) {\n return async function (req: NextRequest): Promise<NextResponse> {\n const { protect, redirectTo = \"/login\" } = options;\n const { pathname } = req.nextUrl;\n\n const isProtected = protect.some(\n (pattern) => pathname === pattern || pathname.startsWith(pattern + \"/\")\n );\n\n if (!isProtected) {\n return NextResponse.next();\n }\n\n const token = req.cookies.get(ctx.cookieName)?.value;\n if (token) {\n const user = await verifyToken(token, ctx.jwtSecret);\n if (user) return NextResponse.next();\n }\n\n const loginUrl = new URL(redirectTo, req.url);\n loginUrl.searchParams.set(\"from\", pathname);\n return NextResponse.redirect(loginUrl);\n };\n };\n}\n","import { LiteAuthConfig, LiteAuthContext } from \"./types\";\nimport { makeHandlers, getUserFromCookies } from \"../server\";\nimport { makeMiddleware } from \"../middleware\";\n\nexport function createLiteAuth(config: LiteAuthConfig) {\n const ctx: LiteAuthContext = {\n users: config.users,\n jwtSecret: config.jwtSecret,\n cookieName: config.cookieName ?? \"lite-auth-token\",\n };\n\n return {\n handlers: makeHandlers(ctx),\n middleware: makeMiddleware(ctx),\n getUserFromCookies: getUserFromCookies(ctx),\n };\n}\n"],"mappings":";AAAA,SAAsB,oBAAoB;;;ACA1C,SAAS,SAAS,iBAAiB;AAGnC,SAAS,UAAU,QAAgB;AACjC,SAAO,IAAI,YAAY,EAAE,OAAO,MAAM;AACxC;AAEA,eAAsB,UAAU,MAAkB,QAAiC;AACjF,SAAO,IAAI,QAAQ,EAAE,GAAG,KAAK,CAAC,EAC3B,mBAAmB,EAAE,KAAK,QAAQ,CAAC,EACnC,YAAY,EACZ,kBAAkB,IAAI,EACtB,KAAK,UAAU,MAAM,CAAC;AAC3B;AAEA,eAAsB,YAAY,OAAe,QAA4C;AAC3F,MAAI;AACF,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,UAAU,MAAM,CAAC;AAC5D,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADlBO,SAAS,aAAa,KAAsB;AACjD,iBAAe,MAAM,KAAyC;AAC5D,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,QAAQ;AACN,aAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AAEA,UAAM,EAAE,OAAO,SAAS,IAAI;AAC5B,QAAI,CAAC,SAAS,CAAC,UAAU;AACvB,aAAO,aAAa,KAAK,EAAE,OAAO,kCAAkC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACxF;AAEA,UAAM,OAAO,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,EAAE,aAAa,QAAQ;AAC/E,QAAI,CAAC,MAAM;AACT,aAAO,aAAa,KAAK,EAAE,OAAO,sBAAsB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5E;AAEA,UAAM,EAAE,UAAU,GAAG,GAAG,WAAW,IAAI;AACvC,UAAM,QAAQ,MAAM,UAAU,YAAY,IAAI,SAAS;AAEvD,UAAM,MAAM,aAAa,KAAK,EAAE,MAAM,WAAW,CAAC;AAClD,QAAI,QAAQ,IAAI,IAAI,YAAY,OAAO;AAAA,MACrC,UAAU;AAAA,MACV,MAAM;AAAA,MACN,UAAU;AAAA,MACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,MACjC,QAAQ,KAAK,KAAK,KAAK;AAAA,IACzB,CAAC;AACD,WAAO;AAAA,EACT;AAEA,iBAAe,OAAO,MAA0C;AAC9D,UAAM,MAAM,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AAC1C,QAAI,QAAQ,IAAI,IAAI,YAAY,IAAI,EAAE,UAAU,MAAM,MAAM,KAAK,QAAQ,EAAE,CAAC;AAC5E,WAAO;AAAA,EACT;AAEA,iBAAe,GAAG,KAAyC;AACzD,UAAM,QAAQ,IAAI,QAAQ,IAAI,IAAI,UAAU,GAAG;AAC/C,QAAI,CAAC,MAAO,QAAO,aAAa,KAAK,EAAE,MAAM,KAAK,GAAG,EAAE,QAAQ,IAAI,CAAC;AACpE,UAAM,OAAO,MAAM,YAAY,OAAO,IAAI,SAAS;AACnD,QAAI,CAAC,KAAM,QAAO,aAAa,KAAK,EAAE,MAAM,KAAK,GAAG,EAAE,QAAQ,IAAI,CAAC;AACnE,WAAO,aAAa,KAAK,EAAE,KAAK,CAAC;AAAA,EACnC;AAEA,iBAAe,IAAI,KAAyC;AAC1D,UAAM,SAAS,IAAI,QAAQ,SAAS,MAAM,GAAG,EAAE,IAAI;AACnD,QAAI,WAAW,KAAM,QAAO,GAAG,GAAG;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClE;AAEA,iBAAe,KAAK,KAAyC;AAC3D,UAAM,SAAS,IAAI,QAAQ,SAAS,MAAM,GAAG,EAAE,IAAI;AACnD,QAAI,WAAW,QAAS,QAAO,MAAM,GAAG;AACxC,QAAI,WAAW,SAAU,QAAO,OAAO,GAAG;AAC1C,WAAO,aAAa,KAAK,EAAE,OAAO,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClE;AAEA,SAAO,EAAE,KAAK,KAAK;AACrB;;;AE7DO,SAAS,mBAAmB,KAAsB;AACvD,SAAO,eAAgB,SAA6D;AAClF,UAAM,QAAQ,QAAQ,IAAI,IAAI,UAAU,GAAG;AAC3C,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,YAAY,OAAO,IAAI,SAAS;AAAA,EACzC;AACF;;;ACVA,SAAsB,gBAAAA,qBAAoB;AASnC,SAAS,eAAe,KAAsB;AACnD,SAAO,SAAS,WAAW,SAA4B;AACrD,WAAO,eAAgB,KAAyC;AAC9D,YAAM,EAAE,SAAS,aAAa,SAAS,IAAI;AAC3C,YAAM,EAAE,SAAS,IAAI,IAAI;AAEzB,YAAM,cAAc,QAAQ;AAAA,QAC1B,CAAC,YAAY,aAAa,WAAW,SAAS,WAAW,UAAU,GAAG;AAAA,MACxE;AAEA,UAAI,CAAC,aAAa;AAChB,eAAOC,cAAa,KAAK;AAAA,MAC3B;AAEA,YAAM,QAAQ,IAAI,QAAQ,IAAI,IAAI,UAAU,GAAG;AAC/C,UAAI,OAAO;AACT,cAAM,OAAO,MAAM,YAAY,OAAO,IAAI,SAAS;AACnD,YAAI,KAAM,QAAOA,cAAa,KAAK;AAAA,MACrC;AAEA,YAAM,WAAW,IAAI,IAAI,YAAY,IAAI,GAAG;AAC5C,eAAS,aAAa,IAAI,QAAQ,QAAQ;AAC1C,aAAOA,cAAa,SAAS,QAAQ;AAAA,IACvC;AAAA,EACF;AACF;;;AC9BO,SAAS,eAAe,QAAwB;AACrD,QAAM,MAAuB;AAAA,IAC3B,OAAO,OAAO;AAAA,IACd,WAAW,OAAO;AAAA,IAClB,YAAY,OAAO,cAAc;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,UAAU,aAAa,GAAG;AAAA,IAC1B,YAAY,eAAe,GAAG;AAAA,IAC9B,oBAAoB,mBAAmB,GAAG;AAAA,EAC5C;AACF;","names":["NextResponse","NextResponse"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-lite-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Lightweight JWT auth for Next.js using static JSON users (no database)",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -29,7 +29,13 @@
|
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"next": ">=13.0.0",
|
|
32
|
-
"react": ">=18.0.0"
|
|
32
|
+
"react": ">=18.0.0",
|
|
33
|
+
"tailwindcss": ">=3.0.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"tailwindcss": {
|
|
37
|
+
"optional": true
|
|
38
|
+
}
|
|
33
39
|
},
|
|
34
40
|
"dependencies": {
|
|
35
41
|
"jose": "^5.0.0"
|