@cfast/auth 0.0.1 → 0.2.0
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 +3 -3
- package/dist/client.d.ts +25 -1
- package/dist/client.js +59 -1
- package/dist/index.d.ts +43 -4
- package/dist/index.js +36 -0
- package/llms.txt +216 -0
- package/package.json +20 -11
- package/LICENSE +0 -21
- package/dist/types-19GeiMs4.d.ts +0 -64
- package/dist/types-8eZilolN.d.ts +0 -63
- package/dist/types-DZ7AeznW.d.ts +0 -74
- package/dist/types-DdbPIOVK.d.ts +0 -85
- package/dist/types-DrnTPiku.d.ts +0 -62
package/README.md
CHANGED
|
@@ -138,12 +138,12 @@ function Header() {
|
|
|
138
138
|
|
|
139
139
|
### Login Page
|
|
140
140
|
|
|
141
|
-
The consumer creates their own login route and renders `<LoginPage>`. The component accepts an `authClient` prop and a `components` prop for UI slot overrides. Default slots render plain HTML — use `@cfast/
|
|
141
|
+
The consumer creates their own login route and renders `<LoginPage>`. The component accepts an `authClient` prop and a `components` prop for UI slot overrides. Default slots render plain HTML — use `@cfast/joy` for Joy UI styling.
|
|
142
142
|
|
|
143
143
|
```typescript
|
|
144
144
|
// routes/login.tsx
|
|
145
145
|
import { LoginPage } from "@cfast/auth/client";
|
|
146
|
-
import { joyLoginComponents } from "@cfast/
|
|
146
|
+
import { joyLoginComponents } from "@cfast/joy";
|
|
147
147
|
import { authClient } from "~/auth.client";
|
|
148
148
|
|
|
149
149
|
export default function Login() {
|
|
@@ -196,7 +196,7 @@ export default function Login() {
|
|
|
196
196
|
}
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
For Joy UI, use the pre-built `joyLoginComponents` from `@cfast/
|
|
199
|
+
For Joy UI, use the pre-built `joyLoginComponents` from `@cfast/joy` instead of writing custom slots.
|
|
200
200
|
|
|
201
201
|
### Redirect Flow
|
|
202
202
|
|
package/dist/client.d.ts
CHANGED
|
@@ -72,9 +72,15 @@ type LoginComponents = {
|
|
|
72
72
|
ErrorMessage?: ComponentType<{
|
|
73
73
|
error: string;
|
|
74
74
|
}>;
|
|
75
|
+
PasskeySignUpButton?: ComponentType<{
|
|
76
|
+
onClick: () => void;
|
|
77
|
+
loading: boolean;
|
|
78
|
+
}>;
|
|
75
79
|
};
|
|
76
80
|
type LoginPageProps = {
|
|
77
|
-
|
|
81
|
+
/** Pass `undefined` during SSR (e.g. from a `.client.ts` module). The
|
|
82
|
+
* component renders a static shell and hydrates with full interactivity. */
|
|
83
|
+
authClient?: {
|
|
78
84
|
signIn: {
|
|
79
85
|
magicLink: (opts: {
|
|
80
86
|
email: string;
|
|
@@ -89,6 +95,24 @@ type LoginPageProps = {
|
|
|
89
95
|
} | null;
|
|
90
96
|
} | undefined>;
|
|
91
97
|
};
|
|
98
|
+
signUp?: {
|
|
99
|
+
email: (opts: {
|
|
100
|
+
email: string;
|
|
101
|
+
password: string;
|
|
102
|
+
name: string;
|
|
103
|
+
}) => Promise<{
|
|
104
|
+
error?: {
|
|
105
|
+
message?: string;
|
|
106
|
+
} | null;
|
|
107
|
+
}>;
|
|
108
|
+
};
|
|
109
|
+
passkey?: {
|
|
110
|
+
addPasskey: () => Promise<{
|
|
111
|
+
error?: {
|
|
112
|
+
message?: string;
|
|
113
|
+
} | null;
|
|
114
|
+
} | undefined>;
|
|
115
|
+
};
|
|
92
116
|
};
|
|
93
117
|
components?: LoginComponents;
|
|
94
118
|
title?: string;
|
package/dist/client.js
CHANGED
|
@@ -133,6 +133,21 @@ function DefaultPasskeyButton({
|
|
|
133
133
|
}
|
|
134
134
|
);
|
|
135
135
|
}
|
|
136
|
+
function DefaultPasskeySignUpButton({
|
|
137
|
+
onClick,
|
|
138
|
+
loading
|
|
139
|
+
}) {
|
|
140
|
+
return /* @__PURE__ */ jsx4(
|
|
141
|
+
"button",
|
|
142
|
+
{
|
|
143
|
+
type: "button",
|
|
144
|
+
onClick,
|
|
145
|
+
disabled: loading,
|
|
146
|
+
style: { width: "100%", padding: 8 },
|
|
147
|
+
children: loading ? "Creating account..." : "Sign up with Passkey"
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
}
|
|
136
151
|
function DefaultSuccessMessage({ email }) {
|
|
137
152
|
return /* @__PURE__ */ jsxs("div", { role: "status", children: [
|
|
138
153
|
"Check your email (",
|
|
@@ -154,6 +169,7 @@ function LoginPage({
|
|
|
154
169
|
const EmailInput = components.EmailInput ?? DefaultEmailInput;
|
|
155
170
|
const MagicLinkBtn = components.MagicLinkButton ?? DefaultMagicLinkButton;
|
|
156
171
|
const PasskeyBtn = components.PasskeyButton ?? DefaultPasskeyButton;
|
|
172
|
+
const PasskeySignUpBtn = components.PasskeySignUpButton ?? DefaultPasskeySignUpButton;
|
|
157
173
|
const SuccessMsg = components.SuccessMessage ?? DefaultSuccessMessage;
|
|
158
174
|
const ErrorMsg = components.ErrorMessage ?? DefaultErrorMessage;
|
|
159
175
|
const [email, setEmail] = useState("");
|
|
@@ -161,11 +177,14 @@ function LoginPage({
|
|
|
161
177
|
const [loading, setLoading] = useState(false);
|
|
162
178
|
const [error, setError] = useState(null);
|
|
163
179
|
const [passkeyLoading, setPasskeyLoading] = useState(false);
|
|
180
|
+
const [passkeySignUpLoading, setPasskeySignUpLoading] = useState(false);
|
|
181
|
+
const canPasskeySignUp = !!(authClient?.signUp?.email && authClient?.passkey?.addPasskey);
|
|
164
182
|
async function handleMagicLink() {
|
|
165
183
|
if (!email.trim()) {
|
|
166
184
|
setError("Please enter your email address.");
|
|
167
185
|
return;
|
|
168
186
|
}
|
|
187
|
+
if (!authClient) return;
|
|
169
188
|
setLoading(true);
|
|
170
189
|
setError(null);
|
|
171
190
|
try {
|
|
@@ -183,6 +202,7 @@ function LoginPage({
|
|
|
183
202
|
}
|
|
184
203
|
}
|
|
185
204
|
async function handlePasskey() {
|
|
205
|
+
if (!authClient) return;
|
|
186
206
|
setPasskeyLoading(true);
|
|
187
207
|
setError(null);
|
|
188
208
|
try {
|
|
@@ -198,6 +218,36 @@ function LoginPage({
|
|
|
198
218
|
setPasskeyLoading(false);
|
|
199
219
|
}
|
|
200
220
|
}
|
|
221
|
+
async function handlePasskeySignUp() {
|
|
222
|
+
if (!email.trim()) {
|
|
223
|
+
setError("Please enter your email address.");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!authClient) return;
|
|
227
|
+
setPasskeySignUpLoading(true);
|
|
228
|
+
setError(null);
|
|
229
|
+
try {
|
|
230
|
+
const signUpResult = await authClient.signUp.email({
|
|
231
|
+
email,
|
|
232
|
+
password: crypto.randomUUID(),
|
|
233
|
+
name: ""
|
|
234
|
+
});
|
|
235
|
+
if (signUpResult.error) {
|
|
236
|
+
setError(signUpResult.error.message ?? "Sign-up failed.");
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const passkeyResult = await authClient.passkey.addPasskey();
|
|
240
|
+
if (passkeyResult?.error) {
|
|
241
|
+
setError(passkeyResult.error.message ?? "Passkey registration failed.");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
onSuccess?.();
|
|
245
|
+
} catch {
|
|
246
|
+
setError("Passkey sign-up failed. Please try again.");
|
|
247
|
+
} finally {
|
|
248
|
+
setPasskeySignUpLoading(false);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
201
251
|
return /* @__PURE__ */ jsxs(Layout, { children: [
|
|
202
252
|
/* @__PURE__ */ jsx4("h2", { children: title }),
|
|
203
253
|
subtitle && /* @__PURE__ */ jsx4("p", { children: subtitle }),
|
|
@@ -205,7 +255,14 @@ function LoginPage({
|
|
|
205
255
|
sent ? /* @__PURE__ */ jsx4(SuccessMsg, { email }) : /* @__PURE__ */ jsxs("div", { children: [
|
|
206
256
|
/* @__PURE__ */ jsx4(EmailInput, { value: email, onChange: setEmail }),
|
|
207
257
|
/* @__PURE__ */ jsx4("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsx4(MagicLinkBtn, { onClick: handleMagicLink, loading }) }),
|
|
208
|
-
authClient?.signIn?.passkey && /* @__PURE__ */ jsx4("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsx4(PasskeyBtn, { onClick: handlePasskey, loading: passkeyLoading }) })
|
|
258
|
+
authClient?.signIn?.passkey && /* @__PURE__ */ jsx4("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsx4(PasskeyBtn, { onClick: handlePasskey, loading: passkeyLoading }) }),
|
|
259
|
+
canPasskeySignUp && /* @__PURE__ */ jsx4("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsx4(
|
|
260
|
+
PasskeySignUpBtn,
|
|
261
|
+
{
|
|
262
|
+
onClick: handlePasskeySignUp,
|
|
263
|
+
loading: passkeySignUpLoading
|
|
264
|
+
}
|
|
265
|
+
) })
|
|
209
266
|
] })
|
|
210
267
|
] });
|
|
211
268
|
}
|
|
@@ -213,6 +270,7 @@ function LoginPage({
|
|
|
213
270
|
// src/client/create-auth-client.ts
|
|
214
271
|
import { createAuthClient } from "better-auth/react";
|
|
215
272
|
import { magicLinkClient } from "better-auth/client/plugins";
|
|
273
|
+
import { passkeyClient } from "@better-auth/passkey/client";
|
|
216
274
|
export {
|
|
217
275
|
AuthClientProvider,
|
|
218
276
|
AuthGuard,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as AuthConfig, b as AuthEnvConfig, c as AuthInstance } from './types-ghXti5CW.js';
|
|
2
|
-
export { d as AuthContext,
|
|
3
|
-
import '@cfast/permissions';
|
|
1
|
+
import { a as AuthConfig, b as AuthEnvConfig, c as AuthInstance, A as AuthUser } from './types-ghXti5CW.js';
|
|
2
|
+
export { d as AuthContext, e as AuthenticatedContext } from './types-ghXti5CW.js';
|
|
3
|
+
import { Grant } from '@cfast/permissions';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates a pre-configured auth factory for Cloudflare Workers.
|
|
@@ -164,4 +164,43 @@ declare function createAuthRouteHandlers(getAuth: () => AuthInstance): {
|
|
|
164
164
|
}) => Promise<Response>;
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Adapter type matching @cfast/admin's AdminAuthConfig interface.
|
|
169
|
+
* Duplicated here to avoid a circular dependency on @cfast/admin.
|
|
170
|
+
*/
|
|
171
|
+
type AdminAuthBridge = {
|
|
172
|
+
requireUser(request: Request): Promise<{
|
|
173
|
+
user: AuthUser;
|
|
174
|
+
grants: Grant[];
|
|
175
|
+
}>;
|
|
176
|
+
hasRole(user: {
|
|
177
|
+
roles: string[];
|
|
178
|
+
}, role: string): boolean;
|
|
179
|
+
getRoles(userId: string): Promise<string[]>;
|
|
180
|
+
setRole(userId: string, role: string): Promise<void>;
|
|
181
|
+
removeRole(userId: string, role: string): Promise<void>;
|
|
182
|
+
setRoles(userId: string, roles: string[]): Promise<void>;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Creates an admin auth adapter from a cfast auth instance factory.
|
|
186
|
+
*
|
|
187
|
+
* Replaces ~150 lines of manual adapter boilerplate with a single call.
|
|
188
|
+
* The factory is called per-operation to ensure fresh env bindings on Workers.
|
|
189
|
+
*
|
|
190
|
+
* @param getAuth - Factory that returns an initialized AuthInstance.
|
|
191
|
+
* @returns An object satisfying `@cfast/admin`'s `AdminAuthConfig` interface.
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* import { createAdminAuth } from "@cfast/auth";
|
|
196
|
+
*
|
|
197
|
+
* const auth = createAdminAuth(() =>
|
|
198
|
+
* initAuth({ d1: env.get().DB, appUrl: env.get().APP_URL })
|
|
199
|
+
* );
|
|
200
|
+
*
|
|
201
|
+
* const admin = createAdmin({ auth, db: createDbForAdmin, schema });
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
declare function createAdminAuth(getAuth: () => AuthInstance): AdminAuthBridge;
|
|
205
|
+
|
|
206
|
+
export { AuthConfig, AuthEnvConfig, AuthInstance, AuthUser, createAdminAuth, createAuth, createAuthRouteHandlers, createImpersonationManager, createRoleManager };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { betterAuth } from "better-auth";
|
|
3
3
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
4
4
|
import { magicLink } from "better-auth/plugins/magic-link";
|
|
5
|
+
import { passkey } from "@better-auth/passkey";
|
|
5
6
|
import { drizzle } from "drizzle-orm/d1";
|
|
6
7
|
import { resolveGrants } from "@cfast/permissions";
|
|
7
8
|
|
|
@@ -127,6 +128,15 @@ function createAuth(config) {
|
|
|
127
128
|
magicLink({ sendMagicLink: config.magicLink.sendMagicLink })
|
|
128
129
|
);
|
|
129
130
|
}
|
|
131
|
+
if (config.passkeys) {
|
|
132
|
+
plugins.push(
|
|
133
|
+
passkey({
|
|
134
|
+
rpName: config.passkeys.rpName,
|
|
135
|
+
rpID: config.passkeys.rpId,
|
|
136
|
+
origin: env.appUrl
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
}
|
|
130
140
|
const expiresIn = config.session?.expiresIn ? parseExpiresIn(config.session.expiresIn) : void 0;
|
|
131
141
|
const auth = betterAuth({
|
|
132
142
|
baseURL: env.appUrl,
|
|
@@ -252,7 +262,33 @@ function createAuthRouteHandlers(getAuth) {
|
|
|
252
262
|
}
|
|
253
263
|
return { loader: handleRequest, action: handleRequest };
|
|
254
264
|
}
|
|
265
|
+
|
|
266
|
+
// src/admin-auth.ts
|
|
267
|
+
function createAdminAuth(getAuth) {
|
|
268
|
+
return {
|
|
269
|
+
async requireUser(request) {
|
|
270
|
+
const ctx = await getAuth().requireUser(request);
|
|
271
|
+
return { user: ctx.user, grants: ctx.grants };
|
|
272
|
+
},
|
|
273
|
+
hasRole(user, role) {
|
|
274
|
+
return user.roles.includes(role);
|
|
275
|
+
},
|
|
276
|
+
async getRoles(userId) {
|
|
277
|
+
return getAuth().getRoles(userId);
|
|
278
|
+
},
|
|
279
|
+
async setRole(userId, role) {
|
|
280
|
+
await getAuth().setRole(userId, role);
|
|
281
|
+
},
|
|
282
|
+
async removeRole(userId, role) {
|
|
283
|
+
await getAuth().removeRole(userId, role);
|
|
284
|
+
},
|
|
285
|
+
async setRoles(userId, roles) {
|
|
286
|
+
await getAuth().setRoles(userId, roles);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
255
290
|
export {
|
|
291
|
+
createAdminAuth,
|
|
256
292
|
createAuth,
|
|
257
293
|
createAuthRouteHandlers,
|
|
258
294
|
createImpersonationManager,
|
package/llms.txt
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# @cfast/auth
|
|
2
|
+
|
|
3
|
+
> Authentication for Cloudflare Workers: magic email links, passkeys, roles, impersonation. Built on Better Auth.
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Use `@cfast/auth` to add authentication to a cfast app. It handles login (magic link + passkeys), session management, role assignment, and user impersonation. It produces the `user` and `grants` objects that `@cfast/db` needs for permission-aware queries.
|
|
8
|
+
|
|
9
|
+
## Key concepts
|
|
10
|
+
|
|
11
|
+
- **Two-phase initialization.** `createAuth(config)` returns an `initAuth` factory. Call `initAuth({ d1, appUrl })` per-request to get an `AuthInstance`.
|
|
12
|
+
- **Roles bridge auth and permissions.** Auth assigns roles to users. `@cfast/permissions` defines what roles can do. The `grants` from auth feed directly into `createDb()`.
|
|
13
|
+
- **Cookie-based redirect-back.** Unauthenticated users get a `cfast_redirect_to` cookie before redirecting to `/login`. After login, the cookie restores their intended destination.
|
|
14
|
+
|
|
15
|
+
## API Reference
|
|
16
|
+
|
|
17
|
+
### Server: createAuth(config) => initAuth
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { createAuth } from "@cfast/auth";
|
|
21
|
+
|
|
22
|
+
export const initAuth = createAuth({
|
|
23
|
+
permissions: Permissions, // from definePermissions()
|
|
24
|
+
magicLink?: {
|
|
25
|
+
sendMagicLink: (params: { email: string; url: string }) => Promise<void>,
|
|
26
|
+
},
|
|
27
|
+
passkeys?: { rpName: string; rpId: string },
|
|
28
|
+
session?: { expiresIn?: string }, // e.g. "30d"
|
|
29
|
+
redirects?: { afterLogin?: string; loginPath?: string },
|
|
30
|
+
anonymousRoles?: string[],
|
|
31
|
+
defaultRoles?: string[], // default: ["reader"]
|
|
32
|
+
roleGrants?: Record<string, string[]>, // who can assign which roles
|
|
33
|
+
impersonation?: { allowedRoles?: string[] },
|
|
34
|
+
templates?: { magicLink?: (props: { url: string; email: string }) => string },
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### initAuth(env) => AuthInstance
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const auth: AuthInstance = initAuth({ d1: env.DB, appUrl: "https://myapp.com" });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### AuthInstance methods
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
auth.createContext(request): Promise<AuthContext>
|
|
48
|
+
// { user: AuthUser | null, grants: Grant[] }
|
|
49
|
+
|
|
50
|
+
auth.requireUser(request): Promise<AuthenticatedContext>
|
|
51
|
+
// { user: AuthUser, grants: Grant[] } -- throws 302 redirect if not authenticated
|
|
52
|
+
|
|
53
|
+
auth.getRoles(userId): Promise<string[]>
|
|
54
|
+
auth.setRole(userId, role, caller?): Promise<void>
|
|
55
|
+
auth.setRoles(userId, roles, caller?): Promise<void>
|
|
56
|
+
auth.removeRole(userId, role): Promise<void>
|
|
57
|
+
|
|
58
|
+
auth.impersonate(adminUserId, targetUserId): Promise<void>
|
|
59
|
+
auth.stopImpersonating(adminUserId): Promise<void>
|
|
60
|
+
|
|
61
|
+
auth.sendMagicLink({ email, callbackURL? }): Promise<void>
|
|
62
|
+
auth.handler(request): Promise<Response> // forwards to Better Auth
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### AuthUser type
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
type AuthUser = {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
name: string;
|
|
72
|
+
avatarUrl: string | null;
|
|
73
|
+
roles: string[];
|
|
74
|
+
isImpersonating?: boolean;
|
|
75
|
+
realUser?: { id: string; name: string };
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### createAdminAuth(getAuth): AdminAuthBridge
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { createAdminAuth } from "@cfast/auth";
|
|
83
|
+
|
|
84
|
+
function createAdminAuth(getAuth: () => AuthInstance): AdminAuthBridge
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Creates an admin auth adapter from a cfast auth instance factory. Replaces ~150 lines of manual adapter boilerplate with a single call. The factory is called per-operation to ensure fresh env bindings on Workers.
|
|
88
|
+
|
|
89
|
+
Returns an object satisfying `@cfast/admin`'s `AdminAuthConfig` interface with: `requireUser`, `hasRole`, `getRoles`, `setRole`, `removeRole`, `setRoles`.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const auth = createAdminAuth(() =>
|
|
93
|
+
initAuth({ d1: env.get().DB, appUrl: env.get().APP_URL })
|
|
94
|
+
);
|
|
95
|
+
const admin = createAdmin({ auth, db: createDbForAdmin, schema });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Client exports (`@cfast/auth/client`)
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Providers
|
|
102
|
+
<AuthClientProvider authClient={authClient}> // wraps app root
|
|
103
|
+
<AuthProvider user={user}> // wraps layout routes
|
|
104
|
+
<AuthGuard user={user}> // layout component, provides user to children
|
|
105
|
+
|
|
106
|
+
// Hooks
|
|
107
|
+
useCurrentUser(): AuthUser | null // user from nearest AuthGuard/AuthProvider
|
|
108
|
+
useAuth(): UseAuthReturn // { signOut, registerPasskey, deletePasskey, stopImpersonating, authClient }
|
|
109
|
+
|
|
110
|
+
// Login
|
|
111
|
+
<LoginPage authClient={authClient} components? title? subtitle? />
|
|
112
|
+
createAuthClient(): AuthClientInstance
|
|
113
|
+
magicLinkClient(): plugin // re-exported from better-auth
|
|
114
|
+
passkeyClient(): plugin // re-exported from @better-auth/passkey
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### LoginComponents slots
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
type LoginComponents = {
|
|
121
|
+
Layout?, EmailInput?, PasskeyButton?, PasskeySignUpButton?,
|
|
122
|
+
MagicLinkButton?, SuccessMessage?, ErrorMessage?,
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Passkey sign-up
|
|
127
|
+
|
|
128
|
+
When the `authClient` passed to `<LoginPage>` has both `signUp.email` and `passkey.addPasskey` (i.e., the client was created with `passkeyClient()`), a "Sign up with Passkey" button appears automatically. The flow:
|
|
129
|
+
|
|
130
|
+
1. User enters email, clicks "Sign up with Passkey"
|
|
131
|
+
2. Account is created via `signUp.email` with a random password
|
|
132
|
+
3. Browser's WebAuthn registration prompt fires immediately via `addPasskey()`
|
|
133
|
+
4. User is signed up with a passkey — no magic link email needed
|
|
134
|
+
|
|
135
|
+
To enable passkey sign-up, configure both server and client:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Server: createAuth config
|
|
139
|
+
const initAuth = createAuth({
|
|
140
|
+
permissions,
|
|
141
|
+
passkeys: { rpName: "My App", rpId: "myapp.com" },
|
|
142
|
+
magicLink: { sendMagicLink: ... },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Client: auth client
|
|
146
|
+
import { createAuthClient, magicLinkClient, passkeyClient } from "@cfast/auth/client";
|
|
147
|
+
const authClient = createAuthClient({
|
|
148
|
+
plugins: [magicLinkClient(), passkeyClient()],
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Route plugin (`@cfast/auth/plugin`)
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { authRoutes } from "@cfast/auth/plugin";
|
|
156
|
+
// In routes.ts:
|
|
157
|
+
export default [...authRoutes({ handlerFile: "routes/auth.$.tsx" }), ...otherRoutes];
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Schema (`@cfast/auth/schema`)
|
|
161
|
+
|
|
162
|
+
Drizzle tables: `user`, `session`, `passkey`, `role`, `impersonation_log`.
|
|
163
|
+
|
|
164
|
+
## Usage Examples
|
|
165
|
+
|
|
166
|
+
### Protect a layout route
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// routes/_protected.tsx
|
|
170
|
+
import { AuthGuard } from "@cfast/auth/client";
|
|
171
|
+
|
|
172
|
+
export async function loader({ request, context }) {
|
|
173
|
+
const auth = initAuth({ d1: context.env.DB, appUrl: context.env.APP_URL });
|
|
174
|
+
const { user, grants } = await auth.requireUser(request);
|
|
175
|
+
return { user };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export default function ProtectedLayout() {
|
|
179
|
+
const { user } = useLoaderData<typeof loader>();
|
|
180
|
+
return <AuthGuard user={user}><Outlet /></AuthGuard>;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Auth -> Db flow in a loader
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
export async function loader({ request, context }) {
|
|
188
|
+
const auth = initAuth({ d1: context.env.DB, appUrl: context.env.APP_URL });
|
|
189
|
+
const { user, grants } = await auth.requireUser(request);
|
|
190
|
+
|
|
191
|
+
const db = createDb({ d1: context.env.DB, schema, grants, user });
|
|
192
|
+
const posts = await db.query(postsTable).findMany().run({});
|
|
193
|
+
return { user, posts };
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Role management
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
await auth.setRole(userId, "editor");
|
|
201
|
+
await auth.setRoles(userId, ["editor", "moderator"]);
|
|
202
|
+
await auth.removeRole(userId, "editor");
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Integration
|
|
206
|
+
|
|
207
|
+
- **@cfast/permissions** -- `createAuth({ permissions })` takes the permissions config. Roles defined in permissions are the same roles assigned to users. `auth.requireUser()` returns `grants` resolved from the user's roles.
|
|
208
|
+
- **@cfast/db** -- Pass `{ user, grants }` from `auth.requireUser()` directly to `createDb()`. Role changes via `auth.setRole()` take effect on the next request.
|
|
209
|
+
- **@cfast/joy** -- Provides `joyLoginComponents` for Joy UI styled login page slots.
|
|
210
|
+
|
|
211
|
+
## Common Mistakes
|
|
212
|
+
|
|
213
|
+
- **Calling `createAuth()` per-request** -- `createAuth()` is called once at module level. Only `initAuth()` is called per-request with the D1 binding.
|
|
214
|
+
- **Forgetting to add auth routes** -- The magic link callback and passkey endpoints need `authRoutes()` in your `routes.ts`.
|
|
215
|
+
- **Using `unsafe()` instead of roles for admin** -- Admins should have a proper role with grants, not bypass permissions entirely.
|
|
216
|
+
- **Not wrapping the app with `AuthClientProvider`** -- `useAuth()` and `LoginPage` require the provider at the app root.
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cfast/auth",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Authentication for Cloudflare Workers: magic email, passkeys, roles, and impersonation",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cfast",
|
|
7
|
+
"cloudflare-workers",
|
|
8
|
+
"authentication",
|
|
9
|
+
"passkeys",
|
|
10
|
+
"magic-link"
|
|
11
|
+
],
|
|
5
12
|
"license": "MIT",
|
|
6
13
|
"repository": {
|
|
7
14
|
"type": "git",
|
|
@@ -30,19 +37,28 @@
|
|
|
30
37
|
}
|
|
31
38
|
},
|
|
32
39
|
"files": [
|
|
33
|
-
"dist"
|
|
40
|
+
"dist",
|
|
41
|
+
"llms.txt"
|
|
34
42
|
],
|
|
35
43
|
"sideEffects": false,
|
|
36
44
|
"publishConfig": {
|
|
37
45
|
"access": "public"
|
|
38
46
|
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup src/index.ts src/client.ts src/plugin.ts src/schema.ts --format esm --dts",
|
|
49
|
+
"dev": "tsup src/index.ts src/client.ts src/plugin.ts src/schema.ts --format esm --dts --watch",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"lint": "eslint src/"
|
|
53
|
+
},
|
|
39
54
|
"peerDependencies": {
|
|
55
|
+
"@better-auth/passkey": ">=1",
|
|
40
56
|
"better-auth": ">=1",
|
|
41
57
|
"drizzle-orm": ">=0.35",
|
|
42
58
|
"react": ">=18"
|
|
43
59
|
},
|
|
44
60
|
"dependencies": {
|
|
45
|
-
"@cfast/permissions": "
|
|
61
|
+
"@cfast/permissions": "workspace:*"
|
|
46
62
|
},
|
|
47
63
|
"devDependencies": {
|
|
48
64
|
"@cloudflare/workers-types": "^4.20260305.1",
|
|
@@ -55,12 +71,5 @@
|
|
|
55
71
|
"tsup": "^8",
|
|
56
72
|
"typescript": "^5.7",
|
|
57
73
|
"vitest": "^4.1.0"
|
|
58
|
-
},
|
|
59
|
-
"scripts": {
|
|
60
|
-
"build": "tsup src/index.ts src/client.ts src/plugin.ts src/schema.ts --format esm --dts",
|
|
61
|
-
"dev": "tsup src/index.ts src/client.ts src/plugin.ts src/schema.ts --format esm --dts --watch",
|
|
62
|
-
"test": "vitest run",
|
|
63
|
-
"typecheck": "tsc --noEmit",
|
|
64
|
-
"lint": "eslint src/"
|
|
65
74
|
}
|
|
66
|
-
}
|
|
75
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Daniel Schmidt
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/types-19GeiMs4.d.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Permissions, Grant } from '@cfast/permissions';
|
|
2
|
-
|
|
3
|
-
type AuthUser = {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string;
|
|
7
|
-
avatarUrl: string | null;
|
|
8
|
-
roles: string[];
|
|
9
|
-
isImpersonating?: boolean;
|
|
10
|
-
realUser?: {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
type AuthContext = {
|
|
16
|
-
user: AuthUser | null;
|
|
17
|
-
grants: Grant[];
|
|
18
|
-
};
|
|
19
|
-
type AuthenticatedContext = {
|
|
20
|
-
user: AuthUser;
|
|
21
|
-
grants: Grant[];
|
|
22
|
-
};
|
|
23
|
-
type AuthConfig = {
|
|
24
|
-
permissions: Permissions;
|
|
25
|
-
schema?: Record<string, unknown>;
|
|
26
|
-
passkeys?: {
|
|
27
|
-
rpName: string;
|
|
28
|
-
rpId: string;
|
|
29
|
-
};
|
|
30
|
-
magicLink?: {
|
|
31
|
-
sendMagicLink: (params: {
|
|
32
|
-
email: string;
|
|
33
|
-
url: string;
|
|
34
|
-
}) => Promise<void>;
|
|
35
|
-
};
|
|
36
|
-
session?: {
|
|
37
|
-
expiresIn?: string;
|
|
38
|
-
};
|
|
39
|
-
redirects?: {
|
|
40
|
-
afterLogin?: string;
|
|
41
|
-
loginPath?: string;
|
|
42
|
-
};
|
|
43
|
-
anonymousRoles?: string[];
|
|
44
|
-
defaultRoles?: string[];
|
|
45
|
-
roleTableName?: string;
|
|
46
|
-
};
|
|
47
|
-
type AuthEnvConfig = {
|
|
48
|
-
d1: D1Database;
|
|
49
|
-
appUrl: string;
|
|
50
|
-
};
|
|
51
|
-
type AuthInstance = {
|
|
52
|
-
createContext: (request: Request) => Promise<AuthContext>;
|
|
53
|
-
requireUser: (request: Request) => Promise<AuthenticatedContext>;
|
|
54
|
-
getRoles: (userId: string) => Promise<string[]>;
|
|
55
|
-
setRole: (userId: string, role: string) => Promise<void>;
|
|
56
|
-
setRoles: (userId: string, roles: string[]) => Promise<void>;
|
|
57
|
-
removeRole: (userId: string, role: string) => Promise<void>;
|
|
58
|
-
/** Handle auth API requests (forwards to Better Auth) */
|
|
59
|
-
handler: (request: Request) => Promise<Response>;
|
|
60
|
-
/** The underlying Better Auth instance */
|
|
61
|
-
api: unknown;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export type { AuthUser as A, AuthConfig as a, AuthEnvConfig as b, AuthInstance as c, AuthContext as d, AuthenticatedContext as e };
|
package/dist/types-8eZilolN.d.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { Permissions, Grant } from '@cfast/permissions';
|
|
2
|
-
|
|
3
|
-
type AuthUser = {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string;
|
|
7
|
-
avatarUrl: string | null;
|
|
8
|
-
roles: string[];
|
|
9
|
-
isImpersonating?: boolean;
|
|
10
|
-
realUser?: {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
type AuthContext = {
|
|
16
|
-
user: AuthUser | null;
|
|
17
|
-
grants: Grant[];
|
|
18
|
-
};
|
|
19
|
-
type AuthenticatedContext = {
|
|
20
|
-
user: AuthUser;
|
|
21
|
-
grants: Grant[];
|
|
22
|
-
};
|
|
23
|
-
type AuthConfig = {
|
|
24
|
-
permissions: Permissions;
|
|
25
|
-
schema?: Record<string, unknown>;
|
|
26
|
-
passkeys?: {
|
|
27
|
-
rpName: string;
|
|
28
|
-
rpId: string;
|
|
29
|
-
};
|
|
30
|
-
magicLink?: {
|
|
31
|
-
sendMagicLink: (params: {
|
|
32
|
-
email: string;
|
|
33
|
-
url: string;
|
|
34
|
-
}) => Promise<void>;
|
|
35
|
-
};
|
|
36
|
-
session?: {
|
|
37
|
-
expiresIn?: string;
|
|
38
|
-
};
|
|
39
|
-
redirects?: {
|
|
40
|
-
afterLogin?: string;
|
|
41
|
-
loginPath?: string;
|
|
42
|
-
};
|
|
43
|
-
anonymousRoles?: string[];
|
|
44
|
-
defaultRoles?: string[];
|
|
45
|
-
};
|
|
46
|
-
type AuthEnvConfig = {
|
|
47
|
-
d1: D1Database;
|
|
48
|
-
appUrl: string;
|
|
49
|
-
};
|
|
50
|
-
type AuthInstance = {
|
|
51
|
-
createContext: (request: Request) => Promise<AuthContext>;
|
|
52
|
-
requireUser: (request: Request) => Promise<AuthenticatedContext>;
|
|
53
|
-
getRoles: (userId: string) => Promise<string[]>;
|
|
54
|
-
setRole: (userId: string, role: string) => Promise<void>;
|
|
55
|
-
setRoles: (userId: string, roles: string[]) => Promise<void>;
|
|
56
|
-
removeRole: (userId: string, role: string) => Promise<void>;
|
|
57
|
-
/** Handle auth API requests (forwards to Better Auth) */
|
|
58
|
-
handler: (request: Request) => Promise<Response>;
|
|
59
|
-
/** The underlying Better Auth instance */
|
|
60
|
-
api: unknown;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export type { AuthUser as A, AuthConfig as a, AuthEnvConfig as b, AuthInstance as c, AuthContext as d, AuthenticatedContext as e };
|
package/dist/types-DZ7AeznW.d.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Permissions, Grant } from '@cfast/permissions';
|
|
2
|
-
|
|
3
|
-
type AuthUser = {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string;
|
|
7
|
-
avatarUrl: string | null;
|
|
8
|
-
roles: string[];
|
|
9
|
-
isImpersonating?: boolean;
|
|
10
|
-
realUser?: {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
type AuthContext = {
|
|
16
|
-
user: AuthUser | null;
|
|
17
|
-
grants: Grant[];
|
|
18
|
-
};
|
|
19
|
-
type AuthenticatedContext = {
|
|
20
|
-
user: AuthUser;
|
|
21
|
-
grants: Grant[];
|
|
22
|
-
};
|
|
23
|
-
type AuthConfig = {
|
|
24
|
-
permissions: Permissions;
|
|
25
|
-
schema?: Record<string, unknown>;
|
|
26
|
-
passkeys?: {
|
|
27
|
-
rpName: string;
|
|
28
|
-
rpId: string;
|
|
29
|
-
};
|
|
30
|
-
magicLink?: {
|
|
31
|
-
sendMagicLink: (params: {
|
|
32
|
-
email: string;
|
|
33
|
-
url: string;
|
|
34
|
-
}) => Promise<void>;
|
|
35
|
-
};
|
|
36
|
-
session?: {
|
|
37
|
-
expiresIn?: string;
|
|
38
|
-
};
|
|
39
|
-
redirects?: {
|
|
40
|
-
afterLogin?: string;
|
|
41
|
-
loginPath?: string;
|
|
42
|
-
};
|
|
43
|
-
anonymousRoles?: string[];
|
|
44
|
-
defaultRoles?: string[];
|
|
45
|
-
roleTableName?: string;
|
|
46
|
-
roleGrants?: Record<string, string[]>;
|
|
47
|
-
impersonation?: {
|
|
48
|
-
allowedRoles?: string[];
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
type AuthEnvConfig = {
|
|
52
|
-
d1: D1Database;
|
|
53
|
-
appUrl: string;
|
|
54
|
-
};
|
|
55
|
-
type AuthInstance = {
|
|
56
|
-
createContext: (request: Request) => Promise<AuthContext>;
|
|
57
|
-
requireUser: (request: Request) => Promise<AuthenticatedContext>;
|
|
58
|
-
getRoles: (userId: string) => Promise<string[]>;
|
|
59
|
-
setRole: (userId: string, role: string, caller?: {
|
|
60
|
-
callerRoles?: string[];
|
|
61
|
-
}) => Promise<void>;
|
|
62
|
-
setRoles: (userId: string, roles: string[], caller?: {
|
|
63
|
-
callerRoles?: string[];
|
|
64
|
-
}) => Promise<void>;
|
|
65
|
-
removeRole: (userId: string, role: string) => Promise<void>;
|
|
66
|
-
impersonate: (adminUserId: string, targetUserId: string) => Promise<void>;
|
|
67
|
-
stopImpersonating: (adminUserId: string) => Promise<void>;
|
|
68
|
-
/** Handle auth API requests (forwards to Better Auth) */
|
|
69
|
-
handler: (request: Request) => Promise<Response>;
|
|
70
|
-
/** The underlying Better Auth instance */
|
|
71
|
-
api: unknown;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export type { AuthUser as A, AuthConfig as a, AuthEnvConfig as b, AuthInstance as c, AuthContext as d, AuthenticatedContext as e };
|
package/dist/types-DdbPIOVK.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Permissions, Grant } from '@cfast/permissions';
|
|
2
|
-
|
|
3
|
-
type AuthUser = {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string;
|
|
7
|
-
avatarUrl: string | null;
|
|
8
|
-
roles: string[];
|
|
9
|
-
isImpersonating?: boolean;
|
|
10
|
-
realUser?: {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
type AuthContext = {
|
|
16
|
-
user: AuthUser | null;
|
|
17
|
-
grants: Grant[];
|
|
18
|
-
};
|
|
19
|
-
type AuthenticatedContext = {
|
|
20
|
-
user: AuthUser;
|
|
21
|
-
grants: Grant[];
|
|
22
|
-
};
|
|
23
|
-
type AuthConfig = {
|
|
24
|
-
permissions: Permissions;
|
|
25
|
-
schema?: Record<string, unknown>;
|
|
26
|
-
passkeys?: {
|
|
27
|
-
rpName: string;
|
|
28
|
-
rpId: string;
|
|
29
|
-
};
|
|
30
|
-
magicLink?: {
|
|
31
|
-
sendMagicLink: (params: {
|
|
32
|
-
email: string;
|
|
33
|
-
url: string;
|
|
34
|
-
}) => Promise<void>;
|
|
35
|
-
};
|
|
36
|
-
session?: {
|
|
37
|
-
expiresIn?: string;
|
|
38
|
-
};
|
|
39
|
-
redirects?: {
|
|
40
|
-
afterLogin?: string;
|
|
41
|
-
loginPath?: string;
|
|
42
|
-
};
|
|
43
|
-
anonymousRoles?: string[];
|
|
44
|
-
defaultRoles?: string[];
|
|
45
|
-
roleTableName?: string;
|
|
46
|
-
roleGrants?: Record<string, string[]>;
|
|
47
|
-
impersonation?: {
|
|
48
|
-
allowedRoles?: string[];
|
|
49
|
-
};
|
|
50
|
-
templates?: {
|
|
51
|
-
magicLink?: (props: {
|
|
52
|
-
url: string;
|
|
53
|
-
email: string;
|
|
54
|
-
}) => string;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
type AuthEnvConfig = {
|
|
58
|
-
d1: D1Database;
|
|
59
|
-
appUrl: string;
|
|
60
|
-
};
|
|
61
|
-
type AuthInstance = {
|
|
62
|
-
createContext: (request: Request) => Promise<AuthContext>;
|
|
63
|
-
requireUser: (request: Request) => Promise<AuthenticatedContext>;
|
|
64
|
-
getRoles: (userId: string) => Promise<string[]>;
|
|
65
|
-
setRole: (userId: string, role: string, caller?: {
|
|
66
|
-
callerRoles?: string[];
|
|
67
|
-
}) => Promise<void>;
|
|
68
|
-
setRoles: (userId: string, roles: string[], caller?: {
|
|
69
|
-
callerRoles?: string[];
|
|
70
|
-
}) => Promise<void>;
|
|
71
|
-
removeRole: (userId: string, role: string) => Promise<void>;
|
|
72
|
-
impersonate: (adminUserId: string, targetUserId: string) => Promise<void>;
|
|
73
|
-
stopImpersonating: (adminUserId: string) => Promise<void>;
|
|
74
|
-
/** Send a magic link email to the given address */
|
|
75
|
-
sendMagicLink: (params: {
|
|
76
|
-
email: string;
|
|
77
|
-
callbackURL?: string;
|
|
78
|
-
}) => Promise<void>;
|
|
79
|
-
/** Handle auth API requests (forwards to Better Auth) */
|
|
80
|
-
handler: (request: Request) => Promise<Response>;
|
|
81
|
-
/** The underlying Better Auth instance */
|
|
82
|
-
api: unknown;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
export type { AuthUser as A, AuthConfig as a, AuthEnvConfig as b, AuthInstance as c, AuthContext as d, AuthenticatedContext as e };
|
package/dist/types-DrnTPiku.d.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Permissions, Grant } from '@cfast/permissions';
|
|
2
|
-
|
|
3
|
-
type AuthUser = {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string;
|
|
7
|
-
avatarUrl: string | null;
|
|
8
|
-
roles: string[];
|
|
9
|
-
isImpersonating?: boolean;
|
|
10
|
-
realUser?: {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
type AuthContext = {
|
|
16
|
-
user: AuthUser | null;
|
|
17
|
-
grants: Grant[];
|
|
18
|
-
};
|
|
19
|
-
type AuthenticatedContext = {
|
|
20
|
-
user: AuthUser;
|
|
21
|
-
grants: Grant[];
|
|
22
|
-
};
|
|
23
|
-
type AuthConfig = {
|
|
24
|
-
permissions: Permissions;
|
|
25
|
-
passkeys?: {
|
|
26
|
-
rpName: string;
|
|
27
|
-
rpId: string;
|
|
28
|
-
};
|
|
29
|
-
magicLink?: {
|
|
30
|
-
sendMagicLink: (params: {
|
|
31
|
-
email: string;
|
|
32
|
-
url: string;
|
|
33
|
-
}) => Promise<void>;
|
|
34
|
-
};
|
|
35
|
-
session?: {
|
|
36
|
-
expiresIn?: string;
|
|
37
|
-
};
|
|
38
|
-
redirects?: {
|
|
39
|
-
afterLogin?: string;
|
|
40
|
-
loginPath?: string;
|
|
41
|
-
};
|
|
42
|
-
anonymousRoles?: string[];
|
|
43
|
-
defaultRoles?: string[];
|
|
44
|
-
};
|
|
45
|
-
type AuthEnvConfig = {
|
|
46
|
-
d1: D1Database;
|
|
47
|
-
appUrl: string;
|
|
48
|
-
};
|
|
49
|
-
type AuthInstance = {
|
|
50
|
-
createContext: (request: Request) => Promise<AuthContext>;
|
|
51
|
-
requireUser: (request: Request) => Promise<AuthenticatedContext>;
|
|
52
|
-
getRoles: (userId: string) => Promise<string[]>;
|
|
53
|
-
setRole: (userId: string, role: string) => Promise<void>;
|
|
54
|
-
setRoles: (userId: string, roles: string[]) => Promise<void>;
|
|
55
|
-
removeRole: (userId: string, role: string) => Promise<void>;
|
|
56
|
-
/** Handle auth API requests (forwards to Better Auth) */
|
|
57
|
-
handler: (request: Request) => Promise<Response>;
|
|
58
|
-
/** The underlying Better Auth instance */
|
|
59
|
-
api: unknown;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type { AuthUser as A, AuthConfig as a, AuthEnvConfig as b, AuthInstance as c, AuthContext as d, AuthenticatedContext as e };
|