wabe 0.6.9 → 0.6.11

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.
Files changed (162) hide show
  1. package/README.md +156 -50
  2. package/bucket/b.txt +1 -0
  3. package/dev/index.ts +215 -0
  4. package/dist/authentication/Session.d.ts +4 -1
  5. package/dist/authentication/interface.d.ts +16 -0
  6. package/dist/cron/index.d.ts +0 -1
  7. package/dist/database/DatabaseController.d.ts +41 -13
  8. package/dist/database/interface.d.ts +1 -0
  9. package/dist/email/DevAdapter.d.ts +0 -1
  10. package/dist/email/interface.d.ts +1 -1
  11. package/dist/graphql/resolvers.d.ts +4 -2
  12. package/dist/hooks/index.d.ts +8 -2
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.js +32144 -32058
  15. package/dist/schema/Schema.d.ts +2 -1
  16. package/dist/server/index.d.ts +4 -2
  17. package/dist/utils/crypto.d.ts +7 -0
  18. package/dist/utils/helper.d.ts +5 -1
  19. package/generated/schema.graphql +22 -14
  20. package/generated/wabe.ts +4 -4
  21. package/package.json +23 -23
  22. package/src/authentication/OTP.test.ts +69 -0
  23. package/src/authentication/OTP.ts +64 -0
  24. package/src/authentication/Session.test.ts +629 -0
  25. package/src/authentication/Session.ts +493 -0
  26. package/src/authentication/defaultAuthentication.ts +209 -0
  27. package/src/authentication/index.ts +3 -0
  28. package/src/authentication/interface.ts +155 -0
  29. package/src/authentication/oauth/GitHub.test.ts +91 -0
  30. package/src/authentication/oauth/GitHub.ts +121 -0
  31. package/src/authentication/oauth/Google.test.ts +91 -0
  32. package/src/authentication/oauth/Google.ts +101 -0
  33. package/src/authentication/oauth/Oauth2Client.test.ts +219 -0
  34. package/src/authentication/oauth/Oauth2Client.ts +135 -0
  35. package/src/authentication/oauth/index.ts +2 -0
  36. package/src/authentication/oauth/utils.test.ts +33 -0
  37. package/src/authentication/oauth/utils.ts +27 -0
  38. package/src/authentication/providers/EmailOTP.test.ts +127 -0
  39. package/src/authentication/providers/EmailOTP.ts +84 -0
  40. package/src/authentication/providers/EmailPassword.test.ts +176 -0
  41. package/src/authentication/providers/EmailPassword.ts +116 -0
  42. package/src/authentication/providers/EmailPasswordSRP.test.ts +208 -0
  43. package/src/authentication/providers/EmailPasswordSRP.ts +179 -0
  44. package/src/authentication/providers/GitHub.ts +24 -0
  45. package/src/authentication/providers/Google.ts +24 -0
  46. package/src/authentication/providers/OAuth.test.ts +185 -0
  47. package/src/authentication/providers/OAuth.ts +106 -0
  48. package/src/authentication/providers/PhonePassword.test.ts +176 -0
  49. package/src/authentication/providers/PhonePassword.ts +115 -0
  50. package/src/authentication/providers/QRCodeOTP.test.ts +77 -0
  51. package/src/authentication/providers/QRCodeOTP.ts +58 -0
  52. package/src/authentication/providers/index.ts +6 -0
  53. package/src/authentication/resolvers/refreshResolver.test.ts +30 -0
  54. package/src/authentication/resolvers/refreshResolver.ts +19 -0
  55. package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
  56. package/src/authentication/resolvers/signInWithResolver.test.ts +293 -0
  57. package/src/authentication/resolvers/signInWithResolver.ts +92 -0
  58. package/src/authentication/resolvers/signOutResolver.test.ts +38 -0
  59. package/src/authentication/resolvers/signOutResolver.ts +18 -0
  60. package/src/authentication/resolvers/signUpWithResolver.test.ts +180 -0
  61. package/src/authentication/resolvers/signUpWithResolver.ts +65 -0
  62. package/src/authentication/resolvers/verifyChallenge.test.ts +133 -0
  63. package/src/authentication/resolvers/verifyChallenge.ts +62 -0
  64. package/src/authentication/roles.test.ts +49 -0
  65. package/src/authentication/roles.ts +40 -0
  66. package/src/authentication/utils.test.ts +97 -0
  67. package/src/authentication/utils.ts +39 -0
  68. package/src/cache/InMemoryCache.test.ts +62 -0
  69. package/src/cache/InMemoryCache.ts +45 -0
  70. package/src/cron/index.test.ts +17 -0
  71. package/src/cron/index.ts +43 -0
  72. package/src/database/DatabaseController.test.ts +613 -0
  73. package/src/database/DatabaseController.ts +1007 -0
  74. package/src/database/index.test.ts +1372 -0
  75. package/src/database/index.ts +9 -0
  76. package/src/database/interface.ts +302 -0
  77. package/src/email/DevAdapter.ts +7 -0
  78. package/src/email/EmailController.test.ts +29 -0
  79. package/src/email/EmailController.ts +13 -0
  80. package/src/email/index.ts +2 -0
  81. package/src/email/interface.ts +36 -0
  82. package/src/email/templates/sendOtpCode.ts +120 -0
  83. package/src/file/FileController.ts +28 -0
  84. package/src/file/FileDevAdapter.ts +51 -0
  85. package/src/file/hookDeleteFile.ts +25 -0
  86. package/src/file/hookReadFile.ts +66 -0
  87. package/src/file/hookUploadFile.ts +50 -0
  88. package/src/file/index.test.ts +932 -0
  89. package/src/file/index.ts +2 -0
  90. package/src/file/interface.ts +39 -0
  91. package/src/graphql/GraphQLSchema.test.ts +4408 -0
  92. package/src/graphql/GraphQLSchema.ts +880 -0
  93. package/src/graphql/index.ts +2 -0
  94. package/src/graphql/parseGraphqlSchema.ts +85 -0
  95. package/src/graphql/parser.test.ts +203 -0
  96. package/src/graphql/parser.ts +542 -0
  97. package/src/graphql/pointerAndRelationFunction.ts +191 -0
  98. package/src/graphql/resolvers.ts +442 -0
  99. package/src/graphql/tests/aggregation.test.ts +1115 -0
  100. package/src/graphql/tests/e2e.test.ts +590 -0
  101. package/src/graphql/tests/scalars.test.ts +250 -0
  102. package/src/graphql/types.ts +227 -0
  103. package/src/hooks/HookObject.test.ts +122 -0
  104. package/src/hooks/HookObject.ts +165 -0
  105. package/src/hooks/authentication.ts +67 -0
  106. package/src/hooks/createUser.test.ts +77 -0
  107. package/src/hooks/createUser.ts +10 -0
  108. package/src/hooks/defaultFields.test.ts +176 -0
  109. package/src/hooks/defaultFields.ts +32 -0
  110. package/src/hooks/deleteSession.test.ts +181 -0
  111. package/src/hooks/deleteSession.ts +20 -0
  112. package/src/hooks/hashFieldHook.test.ts +152 -0
  113. package/src/hooks/hashFieldHook.ts +89 -0
  114. package/src/hooks/index.test.ts +258 -0
  115. package/src/hooks/index.ts +414 -0
  116. package/src/hooks/permissions.test.ts +412 -0
  117. package/src/hooks/permissions.ts +93 -0
  118. package/src/hooks/protected.test.ts +551 -0
  119. package/src/hooks/protected.ts +60 -0
  120. package/src/hooks/searchableFields.test.ts +147 -0
  121. package/src/hooks/searchableFields.ts +86 -0
  122. package/src/hooks/session.test.ts +134 -0
  123. package/src/hooks/session.ts +76 -0
  124. package/src/hooks/setEmail.test.ts +216 -0
  125. package/src/hooks/setEmail.ts +33 -0
  126. package/src/hooks/setupAcl.test.ts +618 -0
  127. package/src/hooks/setupAcl.ts +25 -0
  128. package/src/index.ts +9 -0
  129. package/src/schema/Schema.test.ts +482 -0
  130. package/src/schema/Schema.ts +757 -0
  131. package/src/schema/defaultResolvers.ts +93 -0
  132. package/src/schema/index.ts +1 -0
  133. package/src/schema/resolvers/meResolver.test.ts +62 -0
  134. package/src/schema/resolvers/meResolver.ts +10 -0
  135. package/src/schema/resolvers/resetPassword.test.ts +341 -0
  136. package/src/schema/resolvers/resetPassword.ts +63 -0
  137. package/src/schema/resolvers/sendEmail.test.ts +118 -0
  138. package/src/schema/resolvers/sendEmail.ts +21 -0
  139. package/src/schema/resolvers/sendOtpCode.test.ts +141 -0
  140. package/src/schema/resolvers/sendOtpCode.ts +52 -0
  141. package/src/security.test.ts +3434 -0
  142. package/src/server/defaultSessionHandler.test.ts +62 -0
  143. package/src/server/defaultSessionHandler.ts +105 -0
  144. package/src/server/generateCodegen.ts +433 -0
  145. package/src/server/index.test.ts +532 -0
  146. package/src/server/index.ts +334 -0
  147. package/src/server/interface.ts +11 -0
  148. package/src/server/routes/authHandler.ts +169 -0
  149. package/src/server/routes/index.ts +39 -0
  150. package/src/utils/crypto.test.ts +41 -0
  151. package/src/utils/crypto.ts +105 -0
  152. package/src/utils/export.ts +11 -0
  153. package/src/utils/helper.ts +204 -0
  154. package/src/utils/index.test.ts +11 -0
  155. package/src/utils/index.ts +189 -0
  156. package/src/utils/preload.ts +8 -0
  157. package/src/utils/testHelper.ts +116 -0
  158. package/tsconfig.json +32 -0
  159. package/bunfig.toml +0 -4
  160. package/dist/ai/index.d.ts +0 -1
  161. package/dist/ai/interface.d.ts +0 -9
  162. /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
@@ -0,0 +1,58 @@
1
+ import { contextWithRoot } from '../..'
2
+ import type { DevWabeTypes } from '../../utils/helper'
3
+ import type { OnVerifyChallengeOptions, SecondaryProviderInterface } from '../interface'
4
+ import { OTP } from '../OTP'
5
+
6
+ const DUMMY_USER_ID = '00000000-0000-0000-0000-000000000000'
7
+
8
+ type QRCodeOTPInterface = {
9
+ email: string
10
+ otp: string
11
+ }
12
+
13
+ export class QRCodeOTP implements SecondaryProviderInterface<DevWabeTypes, QRCodeOTPInterface> {
14
+ async onSendChallenge() {
15
+ // The user should check the application and get the OTP code
16
+ }
17
+
18
+ async onVerifyChallenge({
19
+ context,
20
+ input,
21
+ }: OnVerifyChallengeOptions<DevWabeTypes, QRCodeOTPInterface>) {
22
+ const users = await context.wabe.controllers.database.getObjects({
23
+ className: 'User',
24
+ where: {
25
+ email: {
26
+ equalTo: input.email,
27
+ },
28
+ },
29
+ select: {
30
+ authentication: true,
31
+ role: true,
32
+ secondFA: true,
33
+ email: true,
34
+ id: true,
35
+ provider: true,
36
+ isOauth: true,
37
+ createdAt: true,
38
+ updatedAt: true,
39
+ },
40
+ first: 1,
41
+ context: contextWithRoot(context),
42
+ })
43
+
44
+ const realUser = users.length > 0 ? users[0] : null
45
+ const userId = realUser?.id ?? DUMMY_USER_ID
46
+
47
+ const isDevBypass =
48
+ !context.wabe.config.isProduction && input.otp === '000000' && realUser !== null
49
+
50
+ const otpClass = new OTP(context.wabe.config.rootKey)
51
+
52
+ const isOtpValid = otpClass.authenticatorVerify(input.otp, userId)
53
+
54
+ if (realUser && (isOtpValid || isDevBypass)) return { userId: realUser.id }
55
+
56
+ return null
57
+ }
58
+ }
@@ -0,0 +1,6 @@
1
+ export * from './EmailPassword'
2
+ export * from './Google'
3
+ export * from './GitHub'
4
+ export * from './PhonePassword'
5
+ export * from './EmailOTP'
6
+ export * from './QRCodeOTP'
@@ -0,0 +1,30 @@
1
+ import { describe, expect, it, spyOn } from 'bun:test'
2
+ import { refreshResolver } from './refreshResolver'
3
+ import type { WabeContext } from '../../server/interface'
4
+ import { Session } from '../Session'
5
+
6
+ const context: WabeContext<any> = {
7
+ sessionId: 'sessionId',
8
+ user: {} as any,
9
+ isRoot: false,
10
+ } as WabeContext<any>
11
+
12
+ describe('refreshResolver', () => {
13
+ it('should refresh the session', async () => {
14
+ const spyRefreshSession = spyOn(Session.prototype, 'refresh').mockResolvedValue({} as any)
15
+
16
+ await refreshResolver(
17
+ null,
18
+ {
19
+ input: {
20
+ accessToken: 'accessToken',
21
+ refreshToken: 'refreshToken',
22
+ },
23
+ },
24
+ context,
25
+ )
26
+
27
+ expect(spyRefreshSession).toHaveBeenCalledTimes(1)
28
+ expect(spyRefreshSession).toHaveBeenCalledWith('accessToken', 'refreshToken', context)
29
+ })
30
+ })
@@ -0,0 +1,19 @@
1
+ import type { WabeContext } from '../../server/interface'
2
+ import type { DevWabeTypes } from '../../utils/helper'
3
+ import { Session } from '../Session'
4
+
5
+ export const refreshResolver = async (_: any, args: any, context: WabeContext<DevWabeTypes>) => {
6
+ const {
7
+ input: { refreshToken, accessToken },
8
+ } = args
9
+
10
+ const session = new Session()
11
+
12
+ const { accessToken: newAccessToken, refreshToken: newRefreshToken } = await session.refresh(
13
+ accessToken,
14
+ refreshToken,
15
+ context,
16
+ )
17
+
18
+ return { accessToken: newAccessToken, refreshToken: newRefreshToken }
19
+ }
@@ -0,0 +1,59 @@
1
+ import { describe, it, beforeAll, afterAll, expect } from 'bun:test'
2
+ import { gql } from 'graphql-request'
3
+ import type { Wabe } from 'src'
4
+ import { getAdminUserClient, type DevWabeTypes } from 'src/utils/helper'
5
+ import { setupTests, closeTests } from 'src/utils/testHelper'
6
+
7
+ describe('signInWithResolver integration test', () => {
8
+ let wabe: Wabe<DevWabeTypes>
9
+
10
+ beforeAll(async () => {
11
+ const setup = await setupTests()
12
+ wabe = setup.wabe
13
+ })
14
+
15
+ afterAll(async () => {
16
+ await closeTests(wabe)
17
+ })
18
+
19
+ it('should return all fields of the user on signInWith resolver', async () => {
20
+ const adminClient = await getAdminUserClient(wabe.config.port, wabe, {
21
+ email: 'admin@wabe.dev',
22
+ password: 'admin',
23
+ })
24
+ const res = await adminClient.request<any>(
25
+ gql`
26
+ mutation signInWith($input: SignInWithInput!) {
27
+ signInWith(input: $input) {
28
+ user {
29
+ id
30
+ authentication {
31
+ emailPassword {
32
+ email
33
+ }
34
+ }
35
+ role {
36
+ name
37
+ }
38
+ }
39
+ }
40
+ }
41
+ `,
42
+ {
43
+ input: {
44
+ authentication: {
45
+ emailPassword: {
46
+ email: 'admin@wabe.dev',
47
+ password: 'admin',
48
+ },
49
+ },
50
+ },
51
+ },
52
+ )
53
+
54
+ expect(res.signInWith.user.role.name).toBe('Admin')
55
+ expect(res.signInWith.user.authentication.emailPassword).toEqual({
56
+ email: 'admin@wabe.dev',
57
+ })
58
+ })
59
+ })
@@ -0,0 +1,293 @@
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
+
24
+ const mockOnSendChallenge = mock(() => Promise.resolve())
25
+ const mockOnVerifyChallenge = mock(() => Promise.resolve(true))
26
+
27
+ const context = {
28
+ wabe: {
29
+ config: {
30
+ authentication: {
31
+ session: {
32
+ cookieSession: true,
33
+ },
34
+ customAuthenticationMethods: [
35
+ {
36
+ name: 'emailPassword',
37
+ input: {
38
+ email: { type: 'Email', required: true },
39
+ password: { type: 'String', required: true },
40
+ },
41
+ provider: {
42
+ onSignUp: mockOnSignUp,
43
+ onSignIn: mockOnLogin,
44
+ },
45
+ },
46
+ {
47
+ name: 'emailOTP',
48
+ input: {
49
+ email: {
50
+ type: 'Email',
51
+ required: true,
52
+ },
53
+ code: {
54
+ type: 'String',
55
+ required: true,
56
+ },
57
+ },
58
+ provider: {
59
+ onSendChallenge: mockOnSendChallenge,
60
+ onVerifyChallenge: mockOnVerifyChallenge,
61
+ },
62
+ },
63
+ ],
64
+ },
65
+ },
66
+ },
67
+ }
68
+
69
+ afterEach(() => {
70
+ mockCreateObject.mockClear()
71
+ mockOnLogin.mockClear()
72
+ mockOnSignUp.mockClear()
73
+ })
74
+
75
+ it('should call the secondary factor authentication on signIn', async () => {
76
+ mockOnLogin.mockResolvedValueOnce({
77
+ user: {
78
+ id: 'id',
79
+ // @ts-expect-error
80
+ email: 'email@test.fr',
81
+ secondFA: {
82
+ enabled: true,
83
+ provider: SecondaryFactor.EmailOTP,
84
+ },
85
+ },
86
+ })
87
+
88
+ const res = await signInWithResolver(
89
+ {},
90
+ {
91
+ input: {
92
+ authentication: {
93
+ emailPassword: {
94
+ email: 'email@test.fr',
95
+ password: 'password',
96
+ },
97
+ },
98
+ },
99
+ },
100
+ // @ts-expect-error
101
+ context,
102
+ )
103
+
104
+ expect(mockOnLogin).toHaveBeenCalledTimes(1)
105
+ expect(mockOnLogin).toHaveBeenCalledWith({
106
+ input: {
107
+ email: 'email@test.fr',
108
+ password: 'password',
109
+ },
110
+ context: expect.any(Object),
111
+ })
112
+
113
+ expect(mockOnSendChallenge).toHaveBeenCalledTimes(1)
114
+ expect(mockOnSendChallenge).toHaveBeenCalledWith({
115
+ context: expect.any(Object),
116
+ user: expect.objectContaining({
117
+ email: 'email@test.fr',
118
+ }),
119
+ })
120
+
121
+ expect(res).toEqual({
122
+ accessToken: null,
123
+ refreshToken: null,
124
+ user: {
125
+ id: 'id',
126
+ email: 'email@test.fr',
127
+ secondFA: {
128
+ enabled: true,
129
+ // @ts-expect-error
130
+ provider: SecondaryFactor.EmailOTP,
131
+ },
132
+ },
133
+ })
134
+ })
135
+
136
+ it('should throw an error if no custom authentication configuration is provided', () => {
137
+ expect(
138
+ signInWithResolver(
139
+ {},
140
+ {
141
+ input: {
142
+ authentication: {
143
+ emailPassword: {
144
+ email: 'email@test.fr',
145
+ password: 'password',
146
+ },
147
+ },
148
+ },
149
+ },
150
+ { wabe: { config: { authentication: undefined } } } as any,
151
+ ),
152
+ ).rejects.toThrow('No custom authentication methods found')
153
+ })
154
+
155
+ it('should throw an error if a custom authentication is provided but not in the custom authentication config', () => {
156
+ expect(
157
+ signInWithResolver(
158
+ {},
159
+ {
160
+ input: {
161
+ authentication: {
162
+ emailPassword: {
163
+ email: 'email@test.fr',
164
+ password: 'password',
165
+ },
166
+ },
167
+ },
168
+ },
169
+ {
170
+ wabe: {
171
+ config: {
172
+ authentication: {
173
+ customAuthenticationMethods: [
174
+ {
175
+ name: 'phonePassword',
176
+ input: {
177
+ email: {
178
+ type: 'Email',
179
+ required: true,
180
+ },
181
+ password: {
182
+ type: 'String',
183
+ required: true,
184
+ },
185
+ },
186
+ provider: {
187
+ onSignUp: mockOnSignUp,
188
+ onSignIn: mockOnLogin,
189
+ },
190
+ },
191
+ ],
192
+ },
193
+ },
194
+ },
195
+ } as any,
196
+ ),
197
+ ).rejects.toThrow('No available custom authentication methods found')
198
+ })
199
+
200
+ it('should signInWith email and password when the user already exist (on cookieSession)', async () => {
201
+ const mockCreateSession = spyOn(Session.prototype, 'create').mockResolvedValue({
202
+ refreshToken: 'refreshToken',
203
+ accessToken: 'accessToken',
204
+ csrfToken: 'csrfToken',
205
+ } as any)
206
+
207
+ const mockSetCookie = mock(() => {})
208
+
209
+ const mockResponse = {
210
+ setCookie: mockSetCookie,
211
+ }
212
+
213
+ const res = await signInWithResolver(
214
+ {},
215
+ {
216
+ input: {
217
+ authentication: {
218
+ emailPassword: {
219
+ email: 'email@test.fr',
220
+ password: 'password',
221
+ },
222
+ },
223
+ },
224
+ },
225
+ {
226
+ ...context,
227
+ response: mockResponse,
228
+ } as any,
229
+ )
230
+
231
+ expect(res).toEqual({
232
+ accessToken: 'accessToken',
233
+ refreshToken: 'refreshToken',
234
+ user: {
235
+ id: 'id',
236
+ },
237
+ srp: undefined,
238
+ })
239
+ expect(mockOnLogin).toHaveBeenCalledTimes(1)
240
+ expect(mockOnLogin).toHaveBeenCalledWith({
241
+ input: {
242
+ email: 'email@test.fr',
243
+ password: 'password',
244
+ },
245
+ context: expect.any(Object),
246
+ })
247
+
248
+ expect(mockSetCookie).toHaveBeenCalledTimes(3)
249
+ expect(mockSetCookie).toHaveBeenNthCalledWith(1, 'refreshToken', 'refreshToken', {
250
+ httpOnly: true,
251
+ path: '/',
252
+ secure: true,
253
+ sameSite: 'Strict',
254
+ expires: expect.any(Date),
255
+ })
256
+
257
+ expect(mockSetCookie).toHaveBeenNthCalledWith(2, 'accessToken', 'accessToken', {
258
+ httpOnly: true,
259
+ path: '/',
260
+ secure: true,
261
+ sameSite: 'Strict',
262
+ expires: expect.any(Date),
263
+ })
264
+
265
+ expect(mockSetCookie).toHaveBeenNthCalledWith(3, 'csrfToken', 'csrfToken', {
266
+ httpOnly: true,
267
+ path: '/',
268
+ secure: true,
269
+ sameSite: 'Strict',
270
+ expires: expect.any(Date),
271
+ })
272
+
273
+ // @ts-expect-error
274
+ const refreshTokenExpiresIn = mockSetCookie.mock.calls[0][2].expires
275
+ // @ts-expect-error
276
+ const accessTokenExpiresIn = mockSetCookie.mock.calls[1][2].expires
277
+
278
+ // - 1000 to avoid flaky
279
+ expect(refreshTokenExpiresIn.getTime() - Date.now()).toBeGreaterThanOrEqual(
280
+ 1000 * 7 * 24 * 60 * 60 - 1000,
281
+ )
282
+ expect(accessTokenExpiresIn.getTime() - Date.now()).toBeGreaterThanOrEqual(
283
+ 1000 * 15 * 60 - 1000,
284
+ )
285
+
286
+ expect(mockCreateSession).toHaveBeenCalledTimes(1)
287
+ expect(mockCreateSession).toHaveBeenCalledWith('id', expect.anything())
288
+
289
+ expect(mockOnSignUp).toHaveBeenCalledTimes(0)
290
+
291
+ mockCreateSession.mockRestore()
292
+ })
293
+ })
@@ -0,0 +1,92 @@
1
+ import type { SignInWithInput } from '../../../generated/wabe'
2
+ import type { WabeContext } from '../../server/interface'
3
+ import type { DevWabeTypes } from '../../utils/helper'
4
+ import { Session } from '../Session'
5
+ import type { ProviderInterface, SecondaryProviderInterface } from '../interface'
6
+ import { getAuthenticationMethod } from '../utils'
7
+
8
+ // 0 - Get the authentication method
9
+ // 1 - We check if the signIn is possible (call onSign)
10
+ // 2 - If secondaryFactor is present, we call the onSendChallenge method of the provider
11
+ // 3 - We create session
12
+ export const signInWithResolver = async (
13
+ _: any,
14
+ {
15
+ input,
16
+ }: {
17
+ input: SignInWithInput
18
+ },
19
+ context: WabeContext<DevWabeTypes>,
20
+ ) => {
21
+ const { provider, name } = getAuthenticationMethod<DevWabeTypes, ProviderInterface<DevWabeTypes>>(
22
+ Object.keys(input.authentication || {}),
23
+ context,
24
+ )
25
+
26
+ const inputOfTheGoodAuthenticationMethod =
27
+ // @ts-expect-error
28
+ input.authentication[name]
29
+
30
+ // 1 - We call the onSignIn method of the provider
31
+ const { user, srp } = await provider.onSignIn({
32
+ input: inputOfTheGoodAuthenticationMethod,
33
+ context,
34
+ })
35
+
36
+ const userId = user.id
37
+
38
+ if (!userId) throw new Error('Authentication failed')
39
+
40
+ const secondFAObject = user.secondFA
41
+
42
+ // 2 - We call the onSendChallenge method of the provider
43
+ if (secondFAObject?.enabled) {
44
+ const secondaryProvider = getAuthenticationMethod<
45
+ DevWabeTypes,
46
+ SecondaryProviderInterface<DevWabeTypes>
47
+ >([secondFAObject.provider], context)
48
+
49
+ await secondaryProvider.provider.onSendChallenge?.({
50
+ context,
51
+ // @ts-expect-error
52
+ user,
53
+ })
54
+
55
+ return { accessToken: null, refreshToken: null, user }
56
+ }
57
+
58
+ const session = new Session()
59
+
60
+ const { refreshToken, accessToken, csrfToken } = await session.create(userId, context)
61
+
62
+ if (context.wabe.config.authentication?.session?.cookieSession) {
63
+ const accessTokenExpiresAt = session.getAccessTokenExpireAt(context.wabe.config)
64
+ const refreshTokenExpiresAt = session.getRefreshTokenExpireAt(context.wabe.config)
65
+
66
+ context.response?.setCookie('refreshToken', refreshToken, {
67
+ httpOnly: true,
68
+ path: '/',
69
+ sameSite: 'Strict',
70
+ secure: true,
71
+ expires: refreshTokenExpiresAt,
72
+ })
73
+
74
+ context.response?.setCookie('accessToken', accessToken, {
75
+ httpOnly: true,
76
+ path: '/',
77
+ sameSite: 'Strict',
78
+ secure: true,
79
+ expires: accessTokenExpiresAt,
80
+ })
81
+
82
+ context.response?.setCookie('csrfToken', csrfToken, {
83
+ httpOnly: true,
84
+ path: '/',
85
+ sameSite: 'Strict',
86
+ secure: true,
87
+ expires: accessTokenExpiresAt,
88
+ })
89
+ }
90
+
91
+ return { accessToken, refreshToken, user, srp }
92
+ }
@@ -0,0 +1,38 @@
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
+ })
@@ -0,0 +1,18 @@
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()
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
+ }