wabe 0.6.12 → 0.6.13
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/package.json +4 -1
- package/dev/index.ts +0 -215
- package/generated/schema.graphql +0 -1945
- package/generated/wabe.ts +0 -448
- package/src/authentication/OTP.test.ts +0 -69
- package/src/authentication/OTP.ts +0 -64
- package/src/authentication/Session.test.ts +0 -629
- package/src/authentication/Session.ts +0 -517
- package/src/authentication/cookies.ts +0 -10
- package/src/authentication/defaultAuthentication.ts +0 -209
- package/src/authentication/index.ts +0 -4
- package/src/authentication/interface.ts +0 -177
- package/src/authentication/oauth/GitHub.test.ts +0 -91
- package/src/authentication/oauth/GitHub.ts +0 -121
- package/src/authentication/oauth/Google.test.ts +0 -91
- package/src/authentication/oauth/Google.ts +0 -101
- package/src/authentication/oauth/Oauth2Client.test.ts +0 -219
- package/src/authentication/oauth/Oauth2Client.ts +0 -135
- package/src/authentication/oauth/index.ts +0 -2
- package/src/authentication/oauth/utils.test.ts +0 -33
- package/src/authentication/oauth/utils.ts +0 -27
- package/src/authentication/providers/EmailOTP.test.ts +0 -127
- package/src/authentication/providers/EmailOTP.ts +0 -95
- package/src/authentication/providers/EmailPassword.test.ts +0 -263
- package/src/authentication/providers/EmailPassword.ts +0 -138
- package/src/authentication/providers/EmailPasswordSRP.test.ts +0 -208
- package/src/authentication/providers/EmailPasswordSRP.ts +0 -191
- package/src/authentication/providers/GitHub.ts +0 -24
- package/src/authentication/providers/Google.ts +0 -24
- package/src/authentication/providers/OAuth.test.ts +0 -185
- package/src/authentication/providers/OAuth.ts +0 -106
- package/src/authentication/providers/PhonePassword.test.ts +0 -221
- package/src/authentication/providers/PhonePassword.ts +0 -136
- package/src/authentication/providers/QRCodeOTP.test.ts +0 -77
- package/src/authentication/providers/QRCodeOTP.ts +0 -69
- package/src/authentication/providers/index.ts +0 -6
- package/src/authentication/resolvers/refreshResolver.test.ts +0 -30
- package/src/authentication/resolvers/refreshResolver.ts +0 -19
- package/src/authentication/resolvers/signInWithResolver.inte.test.ts +0 -59
- package/src/authentication/resolvers/signInWithResolver.test.ts +0 -306
- package/src/authentication/resolvers/signInWithResolver.ts +0 -106
- package/src/authentication/resolvers/signOutResolver.test.ts +0 -38
- package/src/authentication/resolvers/signOutResolver.ts +0 -18
- package/src/authentication/resolvers/signUpWithResolver.test.ts +0 -180
- package/src/authentication/resolvers/signUpWithResolver.ts +0 -68
- package/src/authentication/resolvers/verifyChallenge.test.ts +0 -230
- package/src/authentication/resolvers/verifyChallenge.ts +0 -78
- package/src/authentication/roles.test.ts +0 -49
- package/src/authentication/roles.ts +0 -40
- package/src/authentication/security.ts +0 -278
- package/src/authentication/utils.test.ts +0 -97
- package/src/authentication/utils.ts +0 -39
- package/src/cache/InMemoryCache.test.ts +0 -62
- package/src/cache/InMemoryCache.ts +0 -45
- package/src/cron/index.test.ts +0 -17
- package/src/cron/index.ts +0 -43
- package/src/database/DatabaseController.test.ts +0 -613
- package/src/database/DatabaseController.ts +0 -1415
- package/src/database/index.test.ts +0 -1551
- package/src/database/index.ts +0 -9
- package/src/database/interface.ts +0 -308
- package/src/email/DevAdapter.ts +0 -7
- package/src/email/EmailController.test.ts +0 -29
- package/src/email/EmailController.ts +0 -13
- package/src/email/index.ts +0 -2
- package/src/email/interface.ts +0 -36
- package/src/email/templates/sendOtpCode.ts +0 -120
- package/src/file/FileController.ts +0 -28
- package/src/file/FileDevAdapter.ts +0 -51
- package/src/file/hookDeleteFile.ts +0 -25
- package/src/file/hookReadFile.ts +0 -66
- package/src/file/hookUploadFile.ts +0 -52
- package/src/file/index.test.ts +0 -1031
- package/src/file/index.ts +0 -2
- package/src/file/interface.ts +0 -63
- package/src/file/security.ts +0 -156
- package/src/graphql/GraphQLSchema.test.ts +0 -5099
- package/src/graphql/GraphQLSchema.ts +0 -886
- package/src/graphql/index.ts +0 -2
- package/src/graphql/parseGraphqlSchema.ts +0 -85
- package/src/graphql/parser.test.ts +0 -203
- package/src/graphql/parser.ts +0 -707
- package/src/graphql/pointerAndRelationFunction.ts +0 -191
- package/src/graphql/resolvers.ts +0 -464
- package/src/graphql/tests/aggregation.test.ts +0 -1115
- package/src/graphql/tests/e2e.test.ts +0 -590
- package/src/graphql/tests/scalars.test.ts +0 -250
- package/src/graphql/types.ts +0 -227
- package/src/hooks/HookObject.test.ts +0 -122
- package/src/hooks/HookObject.ts +0 -165
- package/src/hooks/authentication.ts +0 -67
- package/src/hooks/createUser.test.ts +0 -77
- package/src/hooks/createUser.ts +0 -10
- package/src/hooks/defaultFields.test.ts +0 -176
- package/src/hooks/defaultFields.ts +0 -32
- package/src/hooks/deleteSession.test.ts +0 -181
- package/src/hooks/deleteSession.ts +0 -20
- package/src/hooks/hashFieldHook.test.ts +0 -152
- package/src/hooks/hashFieldHook.ts +0 -89
- package/src/hooks/index.test.ts +0 -258
- package/src/hooks/index.ts +0 -420
- package/src/hooks/permissions.test.ts +0 -412
- package/src/hooks/permissions.ts +0 -93
- package/src/hooks/protected.test.ts +0 -551
- package/src/hooks/protected.ts +0 -74
- package/src/hooks/searchableFields.test.ts +0 -147
- package/src/hooks/searchableFields.ts +0 -86
- package/src/hooks/session.test.ts +0 -134
- package/src/hooks/session.ts +0 -76
- package/src/hooks/setEmail.test.ts +0 -216
- package/src/hooks/setEmail.ts +0 -33
- package/src/hooks/setupAcl.test.ts +0 -618
- package/src/hooks/setupAcl.ts +0 -25
- package/src/hooks/virtualFields.test.ts +0 -228
- package/src/hooks/virtualFields.ts +0 -48
- package/src/index.ts +0 -9
- package/src/schema/Schema.test.ts +0 -482
- package/src/schema/Schema.ts +0 -839
- package/src/schema/defaultResolvers.ts +0 -93
- package/src/schema/index.ts +0 -1
- package/src/schema/resolvers/meResolver.test.ts +0 -62
- package/src/schema/resolvers/meResolver.ts +0 -10
- package/src/schema/resolvers/resetPassword.test.ts +0 -341
- package/src/schema/resolvers/resetPassword.ts +0 -63
- package/src/schema/resolvers/sendEmail.test.ts +0 -118
- package/src/schema/resolvers/sendEmail.ts +0 -21
- package/src/schema/resolvers/sendOtpCode.test.ts +0 -141
- package/src/schema/resolvers/sendOtpCode.ts +0 -52
- package/src/security.test.ts +0 -4136
- package/src/server/defaultSessionHandler.test.ts +0 -62
- package/src/server/defaultSessionHandler.ts +0 -104
- package/src/server/generateCodegen.ts +0 -433
- package/src/server/index.test.ts +0 -843
- package/src/server/index.ts +0 -336
- package/src/server/interface.ts +0 -11
- package/src/server/routes/authHandler.ts +0 -171
- package/src/server/routes/index.ts +0 -48
- package/src/utils/crypto.test.ts +0 -41
- package/src/utils/crypto.ts +0 -105
- package/src/utils/database.ts +0 -8
- package/src/utils/export.ts +0 -12
- package/src/utils/helper.ts +0 -204
- package/src/utils/index.test.ts +0 -11
- package/src/utils/index.ts +0 -196
- package/src/utils/preload.ts +0 -8
- package/src/utils/testHelper.ts +0 -124
- package/tsconfig.json +0 -32
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock, spyOn, afterEach } from 'bun:test'
|
|
2
|
-
import { signInWithResolver } from './signInWithResolver'
|
|
3
|
-
import { Session } from '../Session'
|
|
4
|
-
import { SecondaryFactor } from '../interface'
|
|
5
|
-
|
|
6
|
-
describe('SignInWith', () => {
|
|
7
|
-
const mockOnLogin = mock(() =>
|
|
8
|
-
Promise.resolve({
|
|
9
|
-
user: {
|
|
10
|
-
id: 'id',
|
|
11
|
-
},
|
|
12
|
-
}),
|
|
13
|
-
)
|
|
14
|
-
const mockOnSignUp = mock(() =>
|
|
15
|
-
Promise.resolve({
|
|
16
|
-
authenticationDataToSave: {
|
|
17
|
-
id: 'id',
|
|
18
|
-
},
|
|
19
|
-
}),
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
const mockCreateObject = mock(() => Promise.resolve({}))
|
|
23
|
-
const mockGetObject = mock(() => Promise.resolve({ pendingChallenges: [] }))
|
|
24
|
-
const mockUpdateObject = mock(() => Promise.resolve({}))
|
|
25
|
-
|
|
26
|
-
const mockOnSendChallenge = mock(() => Promise.resolve())
|
|
27
|
-
const mockOnVerifyChallenge = mock(() => Promise.resolve(true))
|
|
28
|
-
|
|
29
|
-
const context = {
|
|
30
|
-
wabe: {
|
|
31
|
-
controllers: {
|
|
32
|
-
database: {
|
|
33
|
-
getObject: mockGetObject,
|
|
34
|
-
updateObject: mockUpdateObject,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
config: {
|
|
38
|
-
authentication: {
|
|
39
|
-
session: {
|
|
40
|
-
cookieSession: true,
|
|
41
|
-
},
|
|
42
|
-
customAuthenticationMethods: [
|
|
43
|
-
{
|
|
44
|
-
name: 'emailPassword',
|
|
45
|
-
input: {
|
|
46
|
-
email: { type: 'Email', required: true },
|
|
47
|
-
password: { type: 'String', required: true },
|
|
48
|
-
},
|
|
49
|
-
provider: {
|
|
50
|
-
onSignUp: mockOnSignUp,
|
|
51
|
-
onSignIn: mockOnLogin,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: 'emailOTP',
|
|
56
|
-
input: {
|
|
57
|
-
email: {
|
|
58
|
-
type: 'Email',
|
|
59
|
-
required: true,
|
|
60
|
-
},
|
|
61
|
-
code: {
|
|
62
|
-
type: 'String',
|
|
63
|
-
required: true,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
provider: {
|
|
67
|
-
onSendChallenge: mockOnSendChallenge,
|
|
68
|
-
onVerifyChallenge: mockOnVerifyChallenge,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
afterEach(() => {
|
|
78
|
-
mockCreateObject.mockClear()
|
|
79
|
-
mockGetObject.mockClear()
|
|
80
|
-
mockUpdateObject.mockClear()
|
|
81
|
-
mockOnLogin.mockClear()
|
|
82
|
-
mockOnSignUp.mockClear()
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('should call the secondary factor authentication on signIn', async () => {
|
|
86
|
-
mockOnLogin.mockResolvedValueOnce({
|
|
87
|
-
user: {
|
|
88
|
-
id: 'id',
|
|
89
|
-
// @ts-expect-error
|
|
90
|
-
email: 'email@test.fr',
|
|
91
|
-
secondFA: {
|
|
92
|
-
enabled: true,
|
|
93
|
-
provider: SecondaryFactor.EmailOTP,
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
const res = await signInWithResolver(
|
|
99
|
-
{},
|
|
100
|
-
{
|
|
101
|
-
input: {
|
|
102
|
-
authentication: {
|
|
103
|
-
emailPassword: {
|
|
104
|
-
email: 'email@test.fr',
|
|
105
|
-
password: 'password',
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
// @ts-expect-error
|
|
111
|
-
context,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
expect(mockOnLogin).toHaveBeenCalledTimes(1)
|
|
115
|
-
expect(mockOnLogin).toHaveBeenCalledWith({
|
|
116
|
-
input: {
|
|
117
|
-
email: 'email@test.fr',
|
|
118
|
-
password: 'password',
|
|
119
|
-
},
|
|
120
|
-
context: expect.any(Object),
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
expect(mockOnSendChallenge).toHaveBeenCalledTimes(1)
|
|
124
|
-
expect(mockOnSendChallenge).toHaveBeenCalledWith({
|
|
125
|
-
context: expect.any(Object),
|
|
126
|
-
user: expect.objectContaining({
|
|
127
|
-
email: 'email@test.fr',
|
|
128
|
-
}),
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
expect(res).toEqual({
|
|
132
|
-
accessToken: null,
|
|
133
|
-
refreshToken: null,
|
|
134
|
-
srp: null,
|
|
135
|
-
challengeToken: expect.any(String),
|
|
136
|
-
user: {
|
|
137
|
-
id: 'id',
|
|
138
|
-
email: 'email@test.fr',
|
|
139
|
-
secondFA: {
|
|
140
|
-
enabled: true,
|
|
141
|
-
// @ts-expect-error
|
|
142
|
-
provider: SecondaryFactor.EmailOTP,
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
it('should throw an error if no custom authentication configuration is provided', () => {
|
|
149
|
-
expect(
|
|
150
|
-
signInWithResolver(
|
|
151
|
-
{},
|
|
152
|
-
{
|
|
153
|
-
input: {
|
|
154
|
-
authentication: {
|
|
155
|
-
emailPassword: {
|
|
156
|
-
email: 'email@test.fr',
|
|
157
|
-
password: 'password',
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
{ wabe: { config: { authentication: undefined } } } as any,
|
|
163
|
-
),
|
|
164
|
-
).rejects.toThrow('No custom authentication methods found')
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('should throw an error if a custom authentication is provided but not in the custom authentication config', () => {
|
|
168
|
-
expect(
|
|
169
|
-
signInWithResolver(
|
|
170
|
-
{},
|
|
171
|
-
{
|
|
172
|
-
input: {
|
|
173
|
-
authentication: {
|
|
174
|
-
emailPassword: {
|
|
175
|
-
email: 'email@test.fr',
|
|
176
|
-
password: 'password',
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
wabe: {
|
|
183
|
-
config: {
|
|
184
|
-
authentication: {
|
|
185
|
-
customAuthenticationMethods: [
|
|
186
|
-
{
|
|
187
|
-
name: 'phonePassword',
|
|
188
|
-
input: {
|
|
189
|
-
email: {
|
|
190
|
-
type: 'Email',
|
|
191
|
-
required: true,
|
|
192
|
-
},
|
|
193
|
-
password: {
|
|
194
|
-
type: 'String',
|
|
195
|
-
required: true,
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
provider: {
|
|
199
|
-
onSignUp: mockOnSignUp,
|
|
200
|
-
onSignIn: mockOnLogin,
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
} as any,
|
|
208
|
-
),
|
|
209
|
-
).rejects.toThrow('No available custom authentication methods found')
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('should signInWith email and password when the user already exist (on cookieSession)', async () => {
|
|
213
|
-
const mockCreateSession = spyOn(Session.prototype, 'create').mockResolvedValue({
|
|
214
|
-
refreshToken: 'refreshToken',
|
|
215
|
-
accessToken: 'accessToken',
|
|
216
|
-
csrfToken: 'csrfToken',
|
|
217
|
-
} as any)
|
|
218
|
-
|
|
219
|
-
const mockSetCookie = mock(() => {})
|
|
220
|
-
|
|
221
|
-
const mockResponse = {
|
|
222
|
-
setCookie: mockSetCookie,
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const res = await signInWithResolver(
|
|
226
|
-
{},
|
|
227
|
-
{
|
|
228
|
-
input: {
|
|
229
|
-
authentication: {
|
|
230
|
-
emailPassword: {
|
|
231
|
-
email: 'email@test.fr',
|
|
232
|
-
password: 'password',
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
...context,
|
|
239
|
-
response: mockResponse,
|
|
240
|
-
} as any,
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
expect(res).toEqual({
|
|
244
|
-
accessToken: 'accessToken',
|
|
245
|
-
refreshToken: 'refreshToken',
|
|
246
|
-
challengeToken: null,
|
|
247
|
-
user: {
|
|
248
|
-
id: 'id',
|
|
249
|
-
},
|
|
250
|
-
srp: undefined,
|
|
251
|
-
})
|
|
252
|
-
expect(mockOnLogin).toHaveBeenCalledTimes(1)
|
|
253
|
-
expect(mockOnLogin).toHaveBeenCalledWith({
|
|
254
|
-
input: {
|
|
255
|
-
email: 'email@test.fr',
|
|
256
|
-
password: 'password',
|
|
257
|
-
},
|
|
258
|
-
context: expect.any(Object),
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
expect(mockSetCookie).toHaveBeenCalledTimes(3)
|
|
262
|
-
expect(mockSetCookie).toHaveBeenNthCalledWith(1, 'refreshToken', 'refreshToken', {
|
|
263
|
-
httpOnly: true,
|
|
264
|
-
path: '/',
|
|
265
|
-
secure: true,
|
|
266
|
-
sameSite: 'Strict',
|
|
267
|
-
expires: expect.any(Date),
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
expect(mockSetCookie).toHaveBeenNthCalledWith(2, 'accessToken', 'accessToken', {
|
|
271
|
-
httpOnly: true,
|
|
272
|
-
path: '/',
|
|
273
|
-
secure: true,
|
|
274
|
-
sameSite: 'Strict',
|
|
275
|
-
expires: expect.any(Date),
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
expect(mockSetCookie).toHaveBeenNthCalledWith(3, 'csrfToken', 'csrfToken', {
|
|
279
|
-
httpOnly: false,
|
|
280
|
-
path: '/',
|
|
281
|
-
secure: true,
|
|
282
|
-
sameSite: 'Strict',
|
|
283
|
-
expires: expect.any(Date),
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
// @ts-expect-error
|
|
287
|
-
const refreshTokenExpiresIn = mockSetCookie.mock.calls[0][2].expires
|
|
288
|
-
// @ts-expect-error
|
|
289
|
-
const accessTokenExpiresIn = mockSetCookie.mock.calls[1][2].expires
|
|
290
|
-
|
|
291
|
-
// - 1000 to avoid flaky
|
|
292
|
-
expect(refreshTokenExpiresIn.getTime() - Date.now()).toBeGreaterThanOrEqual(
|
|
293
|
-
1000 * 7 * 24 * 60 * 60 - 1000,
|
|
294
|
-
)
|
|
295
|
-
expect(accessTokenExpiresIn.getTime() - Date.now()).toBeGreaterThanOrEqual(
|
|
296
|
-
1000 * 15 * 60 - 1000,
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
expect(mockCreateSession).toHaveBeenCalledTimes(1)
|
|
300
|
-
expect(mockCreateSession).toHaveBeenCalledWith('id', expect.anything())
|
|
301
|
-
|
|
302
|
-
expect(mockOnSignUp).toHaveBeenCalledTimes(0)
|
|
303
|
-
|
|
304
|
-
mockCreateSession.mockRestore()
|
|
305
|
-
})
|
|
306
|
-
})
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import type { SignInWithInput } from '../../../generated/wabe'
|
|
2
|
-
import type { WabeContext } from '../../server/interface'
|
|
3
|
-
import type { DevWabeTypes } from '../../utils/helper'
|
|
4
|
-
import { getSessionCookieSameSite } from '../cookies'
|
|
5
|
-
import { createMfaChallenge } from '../security'
|
|
6
|
-
import { Session } from '../Session'
|
|
7
|
-
import type { ProviderInterface, SecondaryProviderInterface } from '../interface'
|
|
8
|
-
import { getAuthenticationMethod } from '../utils'
|
|
9
|
-
|
|
10
|
-
// 0 - Get the authentication method
|
|
11
|
-
// 1 - We check if the signIn is possible (call onSign)
|
|
12
|
-
// 2 - If secondaryFactor is present, we call the onSendChallenge method of the provider
|
|
13
|
-
// 3 - We create session
|
|
14
|
-
export const signInWithResolver = async (
|
|
15
|
-
_: any,
|
|
16
|
-
{
|
|
17
|
-
input,
|
|
18
|
-
}: {
|
|
19
|
-
input: SignInWithInput
|
|
20
|
-
},
|
|
21
|
-
context: WabeContext<DevWabeTypes>,
|
|
22
|
-
) => {
|
|
23
|
-
const { provider, name } = getAuthenticationMethod<DevWabeTypes, ProviderInterface<DevWabeTypes>>(
|
|
24
|
-
Object.keys(input.authentication || {}),
|
|
25
|
-
context,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
const inputOfTheGoodAuthenticationMethod =
|
|
29
|
-
// @ts-expect-error
|
|
30
|
-
input.authentication[name]
|
|
31
|
-
|
|
32
|
-
// 1 - We call the onSignIn method of the provider
|
|
33
|
-
const { user, srp } = await provider.onSignIn({
|
|
34
|
-
input: inputOfTheGoodAuthenticationMethod,
|
|
35
|
-
context,
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
const userId = user.id
|
|
39
|
-
|
|
40
|
-
if (!userId) throw new Error('Authentication failed')
|
|
41
|
-
|
|
42
|
-
const secondFAObject = user.secondFA
|
|
43
|
-
|
|
44
|
-
// 2 - We call the onSendChallenge method of the provider
|
|
45
|
-
if (secondFAObject?.enabled) {
|
|
46
|
-
const secondaryProvider = getAuthenticationMethod<
|
|
47
|
-
DevWabeTypes,
|
|
48
|
-
SecondaryProviderInterface<DevWabeTypes>
|
|
49
|
-
>([secondFAObject.provider], context)
|
|
50
|
-
|
|
51
|
-
await secondaryProvider.provider.onSendChallenge?.({
|
|
52
|
-
context,
|
|
53
|
-
// @ts-expect-error
|
|
54
|
-
user,
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const challengeToken = await createMfaChallenge(context, {
|
|
58
|
-
userId,
|
|
59
|
-
provider: secondFAObject.provider,
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
accessToken: null,
|
|
64
|
-
refreshToken: null,
|
|
65
|
-
user,
|
|
66
|
-
challengeToken,
|
|
67
|
-
srp: null,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const session = new Session<DevWabeTypes>()
|
|
72
|
-
|
|
73
|
-
const { refreshToken, accessToken, csrfToken } = await session.create(userId, context)
|
|
74
|
-
|
|
75
|
-
if (context.wabe.config.authentication?.session?.cookieSession) {
|
|
76
|
-
const accessTokenExpiresAt = session.getAccessTokenExpireAt(context.wabe.config)
|
|
77
|
-
const refreshTokenExpiresAt = session.getRefreshTokenExpireAt(context.wabe.config)
|
|
78
|
-
const sameSite = getSessionCookieSameSite(context.wabe.config)
|
|
79
|
-
|
|
80
|
-
context.response?.setCookie('refreshToken', refreshToken, {
|
|
81
|
-
httpOnly: true,
|
|
82
|
-
path: '/',
|
|
83
|
-
sameSite,
|
|
84
|
-
secure: true,
|
|
85
|
-
expires: refreshTokenExpiresAt,
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
context.response?.setCookie('accessToken', accessToken, {
|
|
89
|
-
httpOnly: true,
|
|
90
|
-
path: '/',
|
|
91
|
-
sameSite,
|
|
92
|
-
secure: true,
|
|
93
|
-
expires: accessTokenExpiresAt,
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
context.response?.setCookie('csrfToken', csrfToken, {
|
|
97
|
-
httpOnly: false, // OWASP specification specify that the csrfToken should not be httpOnly
|
|
98
|
-
path: '/',
|
|
99
|
-
sameSite,
|
|
100
|
-
secure: true,
|
|
101
|
-
expires: accessTokenExpiresAt,
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return { accessToken, refreshToken, user, srp, challengeToken: null }
|
|
106
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, mock, spyOn } from 'bun:test'
|
|
2
|
-
import { signOutResolver } from './signOutResolver'
|
|
3
|
-
import { Session } from '../Session'
|
|
4
|
-
|
|
5
|
-
describe('signOut', () => {
|
|
6
|
-
const mockDeleteCookie = mock(() => {})
|
|
7
|
-
|
|
8
|
-
const context = {
|
|
9
|
-
sessionId: 'sessionId',
|
|
10
|
-
wabe: {
|
|
11
|
-
config: {
|
|
12
|
-
authentication: {
|
|
13
|
-
session: {
|
|
14
|
-
cookieSession: true,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
response: {
|
|
20
|
-
deleteCookie: mockDeleteCookie,
|
|
21
|
-
},
|
|
22
|
-
} as any
|
|
23
|
-
|
|
24
|
-
it('should sign out the current user', async () => {
|
|
25
|
-
const spyDeleteSession = spyOn(Session.prototype, 'delete').mockResolvedValue(undefined)
|
|
26
|
-
|
|
27
|
-
const res = await signOutResolver(undefined, {}, context)
|
|
28
|
-
|
|
29
|
-
expect(res).toBe(true)
|
|
30
|
-
|
|
31
|
-
expect(spyDeleteSession).toHaveBeenCalledTimes(1)
|
|
32
|
-
expect(spyDeleteSession).toHaveBeenCalledWith(context)
|
|
33
|
-
|
|
34
|
-
expect(mockDeleteCookie).toHaveBeenCalledTimes(2)
|
|
35
|
-
expect(mockDeleteCookie).toHaveBeenNthCalledWith(1, 'accessToken')
|
|
36
|
-
expect(mockDeleteCookie).toHaveBeenNthCalledWith(2, 'refreshToken')
|
|
37
|
-
})
|
|
38
|
-
})
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { WabeContext } from '../../server/interface'
|
|
2
|
-
import type { DevWabeTypes } from '../../utils/helper'
|
|
3
|
-
import { Session } from '../Session'
|
|
4
|
-
|
|
5
|
-
export const signOutResolver = async (_: any, __: any, context: WabeContext<DevWabeTypes>) => {
|
|
6
|
-
const session = new Session<DevWabeTypes>()
|
|
7
|
-
|
|
8
|
-
// For the moment we only delete the session because we suppose the token
|
|
9
|
-
// are used with headers. We will need to delete the cookies in the future.
|
|
10
|
-
await session.delete(context)
|
|
11
|
-
|
|
12
|
-
if (context.wabe.config.authentication?.session?.cookieSession) {
|
|
13
|
-
context.response?.deleteCookie('accessToken')
|
|
14
|
-
context.response?.deleteCookie('refreshToken')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return true
|
|
18
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { beforeAll, afterAll, describe, expect, it, beforeEach } from 'bun:test'
|
|
2
|
-
import { type DevWabeTypes, getAnonymousClient } from '../../utils/helper'
|
|
3
|
-
import type { Wabe } from '../../server'
|
|
4
|
-
import { gql } from 'graphql-request'
|
|
5
|
-
import { setupTests, closeTests } from '../../utils/testHelper'
|
|
6
|
-
|
|
7
|
-
describe('SignUpWith', () => {
|
|
8
|
-
let wabe: Wabe<DevWabeTypes>
|
|
9
|
-
|
|
10
|
-
beforeAll(async () => {
|
|
11
|
-
const setup = await setupTests()
|
|
12
|
-
wabe = setup.wabe
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
beforeEach(async () => {
|
|
16
|
-
await wabe.controllers.database.clearDatabase()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
afterAll(async () => {
|
|
20
|
-
await closeTests(wabe)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('should throw an error if user already exist with emailPassword', async () => {
|
|
24
|
-
const anonymousClient = getAnonymousClient(wabe.config.port)
|
|
25
|
-
|
|
26
|
-
await anonymousClient.request<any>(
|
|
27
|
-
gql`
|
|
28
|
-
mutation signUpWith($input: SignUpWithInput!) {
|
|
29
|
-
signUpWith(input: $input) {
|
|
30
|
-
id
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
`,
|
|
34
|
-
{
|
|
35
|
-
input: {
|
|
36
|
-
authentication: {
|
|
37
|
-
emailPassword: {
|
|
38
|
-
email: 'test@gmail.com',
|
|
39
|
-
password: 'password',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
expect(
|
|
47
|
-
anonymousClient.request<any>(
|
|
48
|
-
gql`
|
|
49
|
-
mutation signUpWith($input: SignUpWithInput!) {
|
|
50
|
-
signUpWith(input: $input) {
|
|
51
|
-
id
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
`,
|
|
55
|
-
{
|
|
56
|
-
input: {
|
|
57
|
-
authentication: {
|
|
58
|
-
emailPassword: {
|
|
59
|
-
email: 'test@gmail.com',
|
|
60
|
-
password: 'password',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
),
|
|
66
|
-
).rejects.toThrow('Not authorized to create user')
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should throw an error if the signUp is disabled', () => {
|
|
70
|
-
if (wabe.config) {
|
|
71
|
-
wabe.config.authentication = {
|
|
72
|
-
...wabe.config.authentication,
|
|
73
|
-
disableSignUp: true,
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const anonymousClient = getAnonymousClient(wabe.config.port)
|
|
77
|
-
|
|
78
|
-
const userSchema = wabe.config.schema?.classes?.find((classItem) => classItem.name === 'User')
|
|
79
|
-
|
|
80
|
-
if (!userSchema) throw new Error('Failed to find user schema')
|
|
81
|
-
|
|
82
|
-
// @ts-expect-error
|
|
83
|
-
userSchema.permissions.create.requireAuthentication = true
|
|
84
|
-
|
|
85
|
-
expect(
|
|
86
|
-
anonymousClient.request<any>(
|
|
87
|
-
gql`
|
|
88
|
-
mutation signUpWith($input: SignUpWithInput!) {
|
|
89
|
-
signUpWith(input: $input) {
|
|
90
|
-
id
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
`,
|
|
94
|
-
{
|
|
95
|
-
input: {
|
|
96
|
-
authentication: {
|
|
97
|
-
emailPassword: {
|
|
98
|
-
email: 'email@test.fr',
|
|
99
|
-
password: 'password',
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
),
|
|
105
|
-
).rejects.toThrow('SignUp is disabled')
|
|
106
|
-
|
|
107
|
-
if (wabe.config) {
|
|
108
|
-
wabe.config.authentication = {
|
|
109
|
-
...wabe.config.authentication,
|
|
110
|
-
disableSignUp: false,
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('should block the signUpWith if the user creation is blocked for anonymous (the creation is done with root to avoid ACL issues)', () => {
|
|
116
|
-
const anonymousClient = getAnonymousClient(wabe.config.port)
|
|
117
|
-
|
|
118
|
-
const userSchema = wabe.config.schema?.classes?.find((classItem) => classItem.name === 'User')
|
|
119
|
-
|
|
120
|
-
if (!userSchema) throw new Error('Failed to find user schema')
|
|
121
|
-
|
|
122
|
-
// @ts-expect-error
|
|
123
|
-
userSchema.permissions.create.requireAuthentication = true
|
|
124
|
-
|
|
125
|
-
expect(
|
|
126
|
-
anonymousClient.request<any>(
|
|
127
|
-
gql`
|
|
128
|
-
mutation signUpWith($input: SignUpWithInput!) {
|
|
129
|
-
signUpWith(input: $input) {
|
|
130
|
-
id
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
`,
|
|
134
|
-
{
|
|
135
|
-
input: {
|
|
136
|
-
authentication: {
|
|
137
|
-
emailPassword: {
|
|
138
|
-
email: 'email@test.fr',
|
|
139
|
-
password: 'password',
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
),
|
|
145
|
-
).rejects.toThrow('Permission denied to create class User')
|
|
146
|
-
|
|
147
|
-
// @ts-expect-error
|
|
148
|
-
userSchema.permissions.create.requireAuthentication = false
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('should signUpWith email and password when the user not exist', async () => {
|
|
152
|
-
const anonymousClient = getAnonymousClient(wabe.config.port)
|
|
153
|
-
|
|
154
|
-
const res = await anonymousClient.request<any>(
|
|
155
|
-
gql`
|
|
156
|
-
mutation signUpWith($input: SignUpWithInput!) {
|
|
157
|
-
signUpWith(input: $input) {
|
|
158
|
-
id
|
|
159
|
-
accessToken
|
|
160
|
-
refreshToken
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
`,
|
|
164
|
-
{
|
|
165
|
-
input: {
|
|
166
|
-
authentication: {
|
|
167
|
-
emailPassword: {
|
|
168
|
-
email: 'test@gmail.com',
|
|
169
|
-
password: 'password',
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
expect(res.signUpWith.id).toEqual(expect.any(String))
|
|
177
|
-
expect(res.signUpWith.accessToken).toEqual(expect.any(String))
|
|
178
|
-
expect(res.signUpWith.refreshToken).toEqual(expect.any(String))
|
|
179
|
-
})
|
|
180
|
-
})
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import type { SignUpWithInput } from '../../../generated/wabe'
|
|
2
|
-
import type { WabeContext } from '../../server/interface'
|
|
3
|
-
import { getSessionCookieSameSite } from '../cookies'
|
|
4
|
-
import { Session } from '../Session'
|
|
5
|
-
|
|
6
|
-
// 0 - Get the authentication method
|
|
7
|
-
// 1 - We check if the signUp is possible (call onSign)
|
|
8
|
-
// 2 - We create the user
|
|
9
|
-
// 3 - We create session
|
|
10
|
-
export const signUpWithResolver = async (
|
|
11
|
-
_: any,
|
|
12
|
-
{
|
|
13
|
-
input,
|
|
14
|
-
}: {
|
|
15
|
-
input: SignUpWithInput
|
|
16
|
-
},
|
|
17
|
-
context: WabeContext<any>,
|
|
18
|
-
) => {
|
|
19
|
-
if (context.wabe.config.authentication?.disableSignUp) throw new Error('SignUp is disabled')
|
|
20
|
-
|
|
21
|
-
// Create object call the provider signUp
|
|
22
|
-
const res = await context.wabe.controllers.database.createObject({
|
|
23
|
-
className: 'User',
|
|
24
|
-
data: {
|
|
25
|
-
authentication: input.authentication,
|
|
26
|
-
},
|
|
27
|
-
context,
|
|
28
|
-
select: { id: true },
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const createdUserId = res?.id
|
|
32
|
-
|
|
33
|
-
const session = new Session<any>()
|
|
34
|
-
|
|
35
|
-
if (!createdUserId) throw new Error('User not created')
|
|
36
|
-
|
|
37
|
-
const { accessToken, refreshToken, csrfToken } = await session.create(createdUserId, context)
|
|
38
|
-
|
|
39
|
-
if (context.wabe.config.authentication?.session?.cookieSession) {
|
|
40
|
-
const sameSite = getSessionCookieSameSite(context.wabe.config)
|
|
41
|
-
|
|
42
|
-
context.response?.setCookie('refreshToken', refreshToken, {
|
|
43
|
-
httpOnly: true,
|
|
44
|
-
path: '/',
|
|
45
|
-
sameSite,
|
|
46
|
-
secure: true,
|
|
47
|
-
expires: session.getRefreshTokenExpireAt(context.wabe.config),
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
context.response?.setCookie('accessToken', accessToken, {
|
|
51
|
-
httpOnly: true,
|
|
52
|
-
path: '/',
|
|
53
|
-
sameSite,
|
|
54
|
-
secure: true,
|
|
55
|
-
expires: session.getAccessTokenExpireAt(context.wabe.config),
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
context.response?.setCookie('csrfToken', csrfToken, {
|
|
59
|
-
httpOnly: false, // OWASP specification specify that the csrfToken should not be httpOnly
|
|
60
|
-
path: '/',
|
|
61
|
-
sameSite,
|
|
62
|
-
secure: true,
|
|
63
|
-
expires: session.getAccessTokenExpireAt(context.wabe.config),
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return { accessToken, refreshToken, id: createdUserId }
|
|
68
|
-
}
|