@opensaas/stack-auth 0.1.7 → 0.3.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +126 -0
- package/CLAUDE.md +61 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/plugin.d.ts.map +1 -1
- package/dist/config/plugin.js +33 -1
- package/dist/config/plugin.js.map +1 -1
- package/dist/config/types.d.ts +38 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/types.d.ts +26 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +6 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +42 -9
- package/dist/server/index.js.map +1 -1
- package/dist/ui/components/ForgotPasswordForm.js +1 -1
- package/dist/ui/components/ForgotPasswordForm.js.map +1 -1
- package/dist/ui/components/SignInForm.d.ts.map +1 -1
- package/dist/ui/components/SignInForm.js +11 -1
- package/dist/ui/components/SignInForm.js.map +1 -1
- package/dist/ui/components/SignUpForm.d.ts.map +1 -1
- package/dist/ui/components/SignUpForm.js +11 -1
- package/dist/ui/components/SignUpForm.js.map +1 -1
- package/package.json +5 -5
- package/src/config/index.ts +1 -0
- package/src/config/plugin.ts +37 -2
- package/src/config/types.ts +42 -1
- package/src/index.ts +3 -0
- package/src/runtime/types.ts +27 -0
- package/src/server/index.ts +47 -9
- package/src/ui/components/ForgotPasswordForm.tsx +1 -1
- package/src/ui/components/SignInForm.tsx +10 -1
- package/src/ui/components/SignUpForm.tsx +10 -1
- package/tests/config.test.ts +4 -13
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import { useRouter } from 'next/navigation.js';
|
|
4
5
|
/**
|
|
5
6
|
* Sign up form component
|
|
6
7
|
* Provides email/password registration and OAuth provider buttons
|
|
@@ -16,6 +17,7 @@ import { useState } from 'react';
|
|
|
16
17
|
* ```
|
|
17
18
|
*/
|
|
18
19
|
export function SignUpForm({ authClient, redirectTo = '/', showSocialProviders = true, socialProviders = ['github', 'google'], requirePasswordConfirmation = true, className = '', onSuccess, onError, }) {
|
|
20
|
+
const router = useRouter();
|
|
19
21
|
const [name, setName] = useState('');
|
|
20
22
|
const [email, setEmail] = useState('');
|
|
21
23
|
const [password, setPassword] = useState('');
|
|
@@ -41,7 +43,13 @@ export function SignUpForm({ authClient, redirectTo = '/', showSocialProviders =
|
|
|
41
43
|
if (result.error) {
|
|
42
44
|
throw new Error(result.error.message);
|
|
43
45
|
}
|
|
44
|
-
onSuccess
|
|
46
|
+
// If onSuccess is provided, call it. Otherwise, automatically redirect
|
|
47
|
+
if (onSuccess) {
|
|
48
|
+
onSuccess();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
router.push(redirectTo);
|
|
52
|
+
}
|
|
45
53
|
}
|
|
46
54
|
catch (err) {
|
|
47
55
|
const message = err instanceof Error ? err.message : 'Sign up failed';
|
|
@@ -60,6 +68,8 @@ export function SignUpForm({ authClient, redirectTo = '/', showSocialProviders =
|
|
|
60
68
|
provider,
|
|
61
69
|
callbackURL: redirectTo,
|
|
62
70
|
});
|
|
71
|
+
// Social sign-in handles its own redirect via OAuth flow
|
|
72
|
+
// Only call onSuccess if provided
|
|
63
73
|
onSuccess?.();
|
|
64
74
|
}
|
|
65
75
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignUpForm.js","sourceRoot":"","sources":["../../../src/ui/components/SignUpForm.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SignUpForm.js","sourceRoot":"","sources":["../../../src/ui/components/SignUpForm.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensaas/stack-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Better-auth integration for OpenSaas Stack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"url": "https://github.com/OpenSaasAU/stack/issues"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
+
"@opensaas/stack-core": "^0",
|
|
52
53
|
"better-auth": "^1.3.29",
|
|
53
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
54
54
|
"next": "^15.0.0 || ^16.0.0",
|
|
55
|
-
"
|
|
55
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {},
|
|
58
58
|
"devDependencies": {
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"@vitest/coverage-v8": "^4.0.4",
|
|
62
62
|
"@vitest/ui": "^4.0.0",
|
|
63
63
|
"better-auth": "^1.3.29",
|
|
64
|
-
"next": "^16.0.
|
|
64
|
+
"next": "^16.0.1",
|
|
65
65
|
"react": "^19.2.0",
|
|
66
66
|
"typescript": "^5.9.3",
|
|
67
67
|
"vitest": "^4.0.0",
|
|
68
|
-
"@opensaas/stack-core": "0.
|
|
68
|
+
"@opensaas/stack-core": "0.3.0"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "tsc",
|
package/src/config/index.ts
CHANGED
package/src/config/plugin.ts
CHANGED
|
@@ -32,6 +32,11 @@ export function authPlugin(config: AuthConfig): Plugin {
|
|
|
32
32
|
name: 'auth',
|
|
33
33
|
version: '0.1.0',
|
|
34
34
|
|
|
35
|
+
runtimeServiceTypes: {
|
|
36
|
+
import: "import type { AuthRuntimeServices } from '@opensaas/stack-auth'",
|
|
37
|
+
typeName: 'AuthRuntimeServices',
|
|
38
|
+
},
|
|
39
|
+
|
|
35
40
|
init: async (context) => {
|
|
36
41
|
// Get auth lists from base Better Auth schema
|
|
37
42
|
const authLists = getAuthLists(normalized.extendUserList)
|
|
@@ -40,8 +45,7 @@ export function authPlugin(config: AuthConfig): Plugin {
|
|
|
40
45
|
for (const plugin of normalized.betterAuthPlugins) {
|
|
41
46
|
if (plugin && typeof plugin === 'object' && 'schema' in plugin) {
|
|
42
47
|
// Plugin has schema property - convert to OpenSaaS lists
|
|
43
|
-
|
|
44
|
-
const pluginSchema = plugin.schema as any
|
|
48
|
+
const pluginSchema = plugin.schema
|
|
45
49
|
const pluginLists = convertBetterAuthSchema(pluginSchema)
|
|
46
50
|
|
|
47
51
|
// Add or extend lists from plugin
|
|
@@ -82,5 +86,36 @@ export function authPlugin(config: AuthConfig): Plugin {
|
|
|
82
86
|
// Access at runtime via: config._pluginData.auth
|
|
83
87
|
context.setPluginData<NormalizedAuthConfig>('auth', normalized)
|
|
84
88
|
},
|
|
89
|
+
|
|
90
|
+
runtime: (context) => {
|
|
91
|
+
// Provide auth-related utilities at runtime
|
|
92
|
+
return {
|
|
93
|
+
/**
|
|
94
|
+
* Get user by ID
|
|
95
|
+
* Uses the access-controlled context to fetch user data
|
|
96
|
+
*/
|
|
97
|
+
getUser: async (userId: string) => {
|
|
98
|
+
// Use 'authUser' if custom User list name was provided, otherwise 'user'
|
|
99
|
+
const userListKey = 'user' // TODO: Make this configurable based on list name
|
|
100
|
+
return await context.db[userListKey].findUnique({
|
|
101
|
+
where: { id: userId },
|
|
102
|
+
})
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get current user from session
|
|
107
|
+
* Extracts userId from session and fetches user data
|
|
108
|
+
*/
|
|
109
|
+
getCurrentUser: async () => {
|
|
110
|
+
if (!context.session?.userId) {
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
const userListKey = 'user'
|
|
114
|
+
return await context.db[userListKey].findUnique({
|
|
115
|
+
where: { id: context.session.userId },
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
},
|
|
85
120
|
}
|
|
86
121
|
}
|
package/src/config/types.ts
CHANGED
|
@@ -167,6 +167,39 @@ export type AuthConfig = {
|
|
|
167
167
|
*/
|
|
168
168
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Better Auth plugin types are not exposed, must use any
|
|
169
169
|
betterAuthPlugins?: any[]
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Rate limiting configuration
|
|
173
|
+
* Controls rate limiting for authentication endpoints
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* // Disable rate limiting for testing
|
|
178
|
+
* rateLimit: {
|
|
179
|
+
* enabled: process.env.DISABLE_RATE_LIMITING !== 'true',
|
|
180
|
+
* }
|
|
181
|
+
*
|
|
182
|
+
* // Custom rate limits
|
|
183
|
+
* rateLimit: {
|
|
184
|
+
* enabled: true,
|
|
185
|
+
* window: 60, // 60 seconds
|
|
186
|
+
* max: 100, // 100 requests per window
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
rateLimit?: {
|
|
191
|
+
enabled: boolean
|
|
192
|
+
/**
|
|
193
|
+
* Time window in seconds
|
|
194
|
+
* @default 60
|
|
195
|
+
*/
|
|
196
|
+
window?: number
|
|
197
|
+
/**
|
|
198
|
+
* Maximum requests per window
|
|
199
|
+
* @default 100
|
|
200
|
+
*/
|
|
201
|
+
max?: number
|
|
202
|
+
}
|
|
170
203
|
}
|
|
171
204
|
|
|
172
205
|
/**
|
|
@@ -174,11 +207,19 @@ export type AuthConfig = {
|
|
|
174
207
|
* Used after parsing user config
|
|
175
208
|
*/
|
|
176
209
|
export type NormalizedAuthConfig = Required<
|
|
177
|
-
Omit<
|
|
210
|
+
Omit<
|
|
211
|
+
AuthConfig,
|
|
212
|
+
'emailAndPassword' | 'emailVerification' | 'passwordReset' | 'betterAuthPlugins' | 'rateLimit'
|
|
213
|
+
>
|
|
178
214
|
> & {
|
|
179
215
|
emailAndPassword: Required<EmailPasswordConfig>
|
|
180
216
|
emailVerification: Required<EmailVerificationConfig>
|
|
181
217
|
passwordReset: Required<PasswordResetConfig>
|
|
182
218
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Better Auth plugin types are not exposed, must use any
|
|
183
219
|
betterAuthPlugins: any[]
|
|
220
|
+
rateLimit?: {
|
|
221
|
+
enabled: boolean
|
|
222
|
+
window?: number
|
|
223
|
+
max?: number
|
|
224
|
+
}
|
|
184
225
|
}
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,9 @@ export { authPlugin } from './config/plugin.js'
|
|
|
34
34
|
export type { AuthConfig, NormalizedAuthConfig } from './config/index.js'
|
|
35
35
|
export type * from './config/types.js'
|
|
36
36
|
|
|
37
|
+
// Runtime type exports
|
|
38
|
+
export type { AuthRuntimeServices } from './runtime/types.js'
|
|
39
|
+
|
|
37
40
|
// List generators (for advanced use cases)
|
|
38
41
|
export {
|
|
39
42
|
getAuthLists,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for auth plugin runtime services
|
|
3
|
+
* These types are used for type-safe access to context.plugins.auth
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Runtime services provided by the auth plugin
|
|
8
|
+
* Available via context.plugins.auth
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthRuntimeServices {
|
|
11
|
+
/**
|
|
12
|
+
* Get user by ID
|
|
13
|
+
* Uses the access-controlled context to fetch user data
|
|
14
|
+
*
|
|
15
|
+
* @param userId - The ID of the user to fetch
|
|
16
|
+
* @returns User object or null if not found or access denied
|
|
17
|
+
*/
|
|
18
|
+
getUser: (userId: string) => Promise<unknown>
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get current user from session
|
|
22
|
+
* Extracts userId from session and fetches user data
|
|
23
|
+
*
|
|
24
|
+
* @returns Current user object or null if not authenticated or not found
|
|
25
|
+
*/
|
|
26
|
+
getCurrentUser: () => Promise<unknown>
|
|
27
|
+
}
|
package/src/server/index.ts
CHANGED
|
@@ -98,6 +98,15 @@ export function createAuth(
|
|
|
98
98
|
{} as Record<string, { clientId: string; clientSecret: string }>,
|
|
99
99
|
),
|
|
100
100
|
|
|
101
|
+
// Rate limiting configuration
|
|
102
|
+
rateLimit: authConfig.rateLimit
|
|
103
|
+
? {
|
|
104
|
+
enabled: authConfig.rateLimit.enabled,
|
|
105
|
+
window: authConfig.rateLimit.window,
|
|
106
|
+
max: authConfig.rateLimit.max,
|
|
107
|
+
}
|
|
108
|
+
: undefined,
|
|
109
|
+
|
|
101
110
|
// Pass through any additional Better Auth plugins
|
|
102
111
|
plugins: authConfig.betterAuthPlugins || [],
|
|
103
112
|
}
|
|
@@ -113,16 +122,45 @@ export function createAuth(
|
|
|
113
122
|
// Return a proxy that lazily initializes the auth instance
|
|
114
123
|
return new Proxy({} as ReturnType<typeof betterAuth>, {
|
|
115
124
|
get(_, prop) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
if (prop === 'then') {
|
|
126
|
+
// Support await on the proxy itself
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Create a lazy wrapper function
|
|
131
|
+
const lazyWrapper = async (...args: unknown[]) => {
|
|
132
|
+
const instance = await getAuthInstance()
|
|
133
|
+
const value = instance[prop as keyof typeof instance]
|
|
134
|
+
if (typeof value === 'function') {
|
|
135
|
+
return (value as (...args: unknown[]) => unknown).apply(instance, args)
|
|
136
|
+
}
|
|
137
|
+
return value
|
|
125
138
|
}
|
|
139
|
+
|
|
140
|
+
// Return a proxy that supports both direct calls and nested property access
|
|
141
|
+
return new Proxy(lazyWrapper, {
|
|
142
|
+
get(target, subProp) {
|
|
143
|
+
if (subProp === 'then') {
|
|
144
|
+
// Support await on nested properties
|
|
145
|
+
return undefined
|
|
146
|
+
}
|
|
147
|
+
// Handle nested property access (e.g., auth.api.getSession)
|
|
148
|
+
return async (...args: unknown[]) => {
|
|
149
|
+
const instance = await getAuthInstance()
|
|
150
|
+
const parentValue = instance[prop as keyof typeof instance]
|
|
151
|
+
if (parentValue && typeof parentValue === 'object') {
|
|
152
|
+
const childValue = (parentValue as Record<string, unknown>)[subProp as string]
|
|
153
|
+
if (typeof childValue === 'function') {
|
|
154
|
+
return (childValue as (...args: unknown[]) => unknown).apply(parentValue, args)
|
|
155
|
+
}
|
|
156
|
+
return childValue
|
|
157
|
+
}
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Property ${String(prop)}.${String(subProp)} not found on auth instance`,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
})
|
|
126
164
|
},
|
|
127
165
|
})
|
|
128
166
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react'
|
|
4
|
+
import { useRouter } from 'next/navigation.js'
|
|
4
5
|
import type { createAuthClient } from 'better-auth/react'
|
|
5
6
|
|
|
6
7
|
export type SignInFormProps = {
|
|
@@ -62,6 +63,7 @@ export function SignInForm({
|
|
|
62
63
|
onSuccess,
|
|
63
64
|
onError,
|
|
64
65
|
}: SignInFormProps) {
|
|
66
|
+
const router = useRouter()
|
|
65
67
|
const [email, setEmail] = useState('')
|
|
66
68
|
const [password, setPassword] = useState('')
|
|
67
69
|
const [error, setError] = useState('')
|
|
@@ -83,7 +85,12 @@ export function SignInForm({
|
|
|
83
85
|
throw new Error(result.error.message)
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
onSuccess
|
|
88
|
+
// If onSuccess is provided, call it. Otherwise, automatically redirect
|
|
89
|
+
if (onSuccess) {
|
|
90
|
+
onSuccess()
|
|
91
|
+
} else {
|
|
92
|
+
router.push(redirectTo)
|
|
93
|
+
}
|
|
87
94
|
} catch (err) {
|
|
88
95
|
const message = err instanceof Error ? err.message : 'Sign in failed'
|
|
89
96
|
setError(message)
|
|
@@ -102,6 +109,8 @@ export function SignInForm({
|
|
|
102
109
|
provider,
|
|
103
110
|
callbackURL: redirectTo,
|
|
104
111
|
})
|
|
112
|
+
// Social sign-in handles its own redirect via OAuth flow
|
|
113
|
+
// Only call onSuccess if provided
|
|
105
114
|
onSuccess?.()
|
|
106
115
|
} catch (err) {
|
|
107
116
|
const message = err instanceof Error ? err.message : 'Sign in failed'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react'
|
|
4
|
+
import { useRouter } from 'next/navigation.js'
|
|
4
5
|
import type { createAuthClient } from 'better-auth/react'
|
|
5
6
|
|
|
6
7
|
export type SignUpFormProps = {
|
|
@@ -67,6 +68,7 @@ export function SignUpForm({
|
|
|
67
68
|
onSuccess,
|
|
68
69
|
onError,
|
|
69
70
|
}: SignUpFormProps) {
|
|
71
|
+
const router = useRouter()
|
|
70
72
|
const [name, setName] = useState('')
|
|
71
73
|
const [email, setEmail] = useState('')
|
|
72
74
|
const [password, setPassword] = useState('')
|
|
@@ -98,7 +100,12 @@ export function SignUpForm({
|
|
|
98
100
|
throw new Error(result.error.message)
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
onSuccess
|
|
103
|
+
// If onSuccess is provided, call it. Otherwise, automatically redirect
|
|
104
|
+
if (onSuccess) {
|
|
105
|
+
onSuccess()
|
|
106
|
+
} else {
|
|
107
|
+
router.push(redirectTo)
|
|
108
|
+
}
|
|
102
109
|
} catch (err) {
|
|
103
110
|
const message = err instanceof Error ? err.message : 'Sign up failed'
|
|
104
111
|
setError(message)
|
|
@@ -117,6 +124,8 @@ export function SignUpForm({
|
|
|
117
124
|
provider,
|
|
118
125
|
callbackURL: redirectTo,
|
|
119
126
|
})
|
|
127
|
+
// Social sign-in handles its own redirect via OAuth flow
|
|
128
|
+
// Only call onSuccess if provided
|
|
120
129
|
onSuccess?.()
|
|
121
130
|
} catch (err) {
|
|
122
131
|
const message = err instanceof Error ? err.message : 'Sign up failed'
|
package/tests/config.test.ts
CHANGED
|
@@ -154,7 +154,6 @@ describe('authPlugin', () => {
|
|
|
154
154
|
const result = await config({
|
|
155
155
|
db: {
|
|
156
156
|
provider: 'sqlite',
|
|
157
|
-
url: 'file:./test.db',
|
|
158
157
|
},
|
|
159
158
|
plugins: [authPlugin({})],
|
|
160
159
|
lists: {
|
|
@@ -175,22 +174,23 @@ describe('authPlugin', () => {
|
|
|
175
174
|
})
|
|
176
175
|
|
|
177
176
|
it('should preserve database config', async () => {
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
178
|
+
const mockConstructor = (() => null) as any
|
|
178
179
|
const result = await config({
|
|
179
180
|
db: {
|
|
180
181
|
provider: 'postgresql',
|
|
181
|
-
|
|
182
|
+
prismaClientConstructor: mockConstructor,
|
|
182
183
|
},
|
|
183
184
|
plugins: [authPlugin({})],
|
|
184
185
|
lists: {},
|
|
185
186
|
})
|
|
186
187
|
|
|
187
188
|
expect(result.db.provider).toBe('postgresql')
|
|
188
|
-
expect(result.db.
|
|
189
|
+
expect(result.db.prismaClientConstructor).toBe(mockConstructor)
|
|
189
190
|
})
|
|
190
191
|
|
|
191
192
|
it('should store normalized auth config in _pluginData', async () => {
|
|
192
193
|
const result = await config({
|
|
193
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
194
194
|
plugins: [
|
|
195
195
|
authPlugin({
|
|
196
196
|
emailAndPassword: { enabled: true, minPasswordLength: 12 },
|
|
@@ -209,7 +209,6 @@ describe('authPlugin', () => {
|
|
|
209
209
|
|
|
210
210
|
it('should extend User list with custom fields', async () => {
|
|
211
211
|
const result = await config({
|
|
212
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
213
212
|
plugins: [
|
|
214
213
|
authPlugin({
|
|
215
214
|
extendUserList: {
|
|
@@ -239,7 +238,6 @@ describe('authPlugin', () => {
|
|
|
239
238
|
|
|
240
239
|
it('should generate User list with correct fields', async () => {
|
|
241
240
|
const result = await config({
|
|
242
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
243
241
|
plugins: [authPlugin({})],
|
|
244
242
|
lists: {},
|
|
245
243
|
})
|
|
@@ -256,7 +254,6 @@ describe('authPlugin', () => {
|
|
|
256
254
|
|
|
257
255
|
it('should generate Session list with correct fields', async () => {
|
|
258
256
|
const result = await config({
|
|
259
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
260
257
|
plugins: [authPlugin({})],
|
|
261
258
|
lists: {},
|
|
262
259
|
})
|
|
@@ -272,7 +269,6 @@ describe('authPlugin', () => {
|
|
|
272
269
|
|
|
273
270
|
it('should generate Account list with correct fields', async () => {
|
|
274
271
|
const result = await config({
|
|
275
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
276
272
|
plugins: [authPlugin({})],
|
|
277
273
|
lists: {},
|
|
278
274
|
})
|
|
@@ -289,7 +285,6 @@ describe('authPlugin', () => {
|
|
|
289
285
|
|
|
290
286
|
it('should generate Verification list with correct fields', async () => {
|
|
291
287
|
const result = await config({
|
|
292
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
293
288
|
plugins: [authPlugin({})],
|
|
294
289
|
lists: {},
|
|
295
290
|
})
|
|
@@ -303,7 +298,6 @@ describe('authPlugin', () => {
|
|
|
303
298
|
|
|
304
299
|
it('should work with empty auth config', async () => {
|
|
305
300
|
const result = await config({
|
|
306
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
307
301
|
plugins: [authPlugin({})],
|
|
308
302
|
lists: {},
|
|
309
303
|
})
|
|
@@ -316,7 +310,6 @@ describe('authPlugin', () => {
|
|
|
316
310
|
|
|
317
311
|
it('should merge with other user-defined lists', async () => {
|
|
318
312
|
const result = await config({
|
|
319
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
320
313
|
plugins: [authPlugin({})],
|
|
321
314
|
lists: {
|
|
322
315
|
Post: list({
|
|
@@ -344,7 +337,6 @@ describe('authPlugin', () => {
|
|
|
344
337
|
|
|
345
338
|
it('should pass through config options to normalized config', async () => {
|
|
346
339
|
const result = await config({
|
|
347
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
348
340
|
plugins: [
|
|
349
341
|
authPlugin({
|
|
350
342
|
emailAndPassword: {
|
|
@@ -407,7 +399,6 @@ describe('authPlugin', () => {
|
|
|
407
399
|
}
|
|
408
400
|
|
|
409
401
|
const result = await config({
|
|
410
|
-
db: { provider: 'sqlite', url: 'file:./test.db' },
|
|
411
402
|
plugins: [
|
|
412
403
|
authPlugin({
|
|
413
404
|
betterAuthPlugins: [mockPlugin],
|