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,517 +0,0 @@
|
|
|
1
|
-
import jwt, { verify, type SignOptions } from 'jsonwebtoken'
|
|
2
|
-
import crypto from 'node:crypto'
|
|
3
|
-
import type { WabeContext } from '../server/interface'
|
|
4
|
-
import type { WabeConfig, WabeTypes } from '../server'
|
|
5
|
-
import { contextWithRoot } from '../utils/export'
|
|
6
|
-
import { encryptDeterministicToken, decryptDeterministicToken } from '../utils/crypto'
|
|
7
|
-
|
|
8
|
-
const getJwtSecret = <T extends WabeTypes>(context: WabeContext<T>): string => {
|
|
9
|
-
const secret = context.wabe.config.authentication?.session?.jwtSecret
|
|
10
|
-
if (!secret) throw new Error('Authentication session requires jwtSecret')
|
|
11
|
-
return secret
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const JWT_ALGORITHM = 'HS256'
|
|
15
|
-
|
|
16
|
-
const safeVerify = (
|
|
17
|
-
token: string,
|
|
18
|
-
secret: string,
|
|
19
|
-
options: Pick<SignOptions, 'audience' | 'issuer'> = {},
|
|
20
|
-
) => {
|
|
21
|
-
try {
|
|
22
|
-
return !!verify(token, secret, {
|
|
23
|
-
...options,
|
|
24
|
-
algorithms: [JWT_ALGORITHM],
|
|
25
|
-
})
|
|
26
|
-
} catch {
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const getTokenSecret = <T extends WabeTypes>(context: WabeContext<T>): string =>
|
|
32
|
-
context.wabe.config.authentication?.session?.tokenSecret ?? getJwtSecret(context)
|
|
33
|
-
|
|
34
|
-
const getTokenEncryptionKey = <T extends WabeTypes>(context: WabeContext<T>) =>
|
|
35
|
-
crypto.createHash('sha256').update(getTokenSecret(context)).digest()
|
|
36
|
-
|
|
37
|
-
const getJwtVerifyOptions = <T extends WabeTypes>(context: WabeContext<T>) => {
|
|
38
|
-
const opts: Pick<SignOptions, 'audience' | 'issuer'> = {}
|
|
39
|
-
const audience = context.wabe.config.authentication?.session?.jwtAudience
|
|
40
|
-
const issuer = context.wabe.config.authentication?.session?.jwtIssuer
|
|
41
|
-
if (audience) opts.audience = audience
|
|
42
|
-
if (issuer) opts.issuer = issuer
|
|
43
|
-
return opts
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class Session<T extends WabeTypes> {
|
|
47
|
-
private accessToken: string | undefined = undefined
|
|
48
|
-
private refreshToken: string | undefined = undefined
|
|
49
|
-
|
|
50
|
-
getAccessTokenExpireAt(config: WabeConfig<T>) {
|
|
51
|
-
const customExpiresInMs = config?.authentication?.session?.accessTokenExpiresInMs
|
|
52
|
-
|
|
53
|
-
if (!customExpiresInMs) return new Date(Date.now() + 1000 * 60 * 15) // 15 minutes in ms
|
|
54
|
-
|
|
55
|
-
return new Date(Date.now() + customExpiresInMs)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
_getRefreshTokenExpiresInMs(config: WabeConfig<T>) {
|
|
59
|
-
const customExpiresInMs = config?.authentication?.session?.refreshTokenExpiresInMs
|
|
60
|
-
|
|
61
|
-
if (!customExpiresInMs) return 1000 * 60 * 60 * 24 * 7 // 7 days in ms
|
|
62
|
-
|
|
63
|
-
return customExpiresInMs
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
getRefreshTokenExpireAt(config: WabeConfig<T>) {
|
|
67
|
-
const expiresInMs = this._getRefreshTokenExpiresInMs(config)
|
|
68
|
-
|
|
69
|
-
return new Date(Date.now() + expiresInMs)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async meFromAccessToken(
|
|
73
|
-
{ accessToken, csrfToken }: { accessToken: string; csrfToken: string },
|
|
74
|
-
context: WabeContext<T>,
|
|
75
|
-
): Promise<{
|
|
76
|
-
sessionId: string | null
|
|
77
|
-
user: T['types']['User'] | null
|
|
78
|
-
accessToken: string | null
|
|
79
|
-
refreshToken?: string | null
|
|
80
|
-
}> {
|
|
81
|
-
const verifyOptions = getJwtVerifyOptions(context)
|
|
82
|
-
if (!safeVerify(accessToken, getJwtSecret(context), verifyOptions)) {
|
|
83
|
-
return {
|
|
84
|
-
sessionId: null,
|
|
85
|
-
user: null,
|
|
86
|
-
accessToken: null,
|
|
87
|
-
refreshToken: null,
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const encryptedAccessToken = encryptDeterministicToken(
|
|
92
|
-
accessToken,
|
|
93
|
-
getTokenEncryptionKey(context),
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
const sessions = await context.wabe.controllers.database.getObjects({
|
|
97
|
-
className: '_Session',
|
|
98
|
-
// @ts-expect-error
|
|
99
|
-
where: {
|
|
100
|
-
accessTokenEncrypted: { equalTo: encryptedAccessToken },
|
|
101
|
-
OR: [
|
|
102
|
-
{
|
|
103
|
-
accessTokenExpiresAt: {
|
|
104
|
-
greaterThanOrEqualTo: new Date(),
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
refreshTokenExpiresAt: {
|
|
109
|
-
greaterThanOrEqualTo: new Date(),
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
},
|
|
114
|
-
select: {
|
|
115
|
-
// @ts-expect-error
|
|
116
|
-
id: true,
|
|
117
|
-
// @ts-expect-error Generic
|
|
118
|
-
user: true,
|
|
119
|
-
// @ts-expect-error Generic
|
|
120
|
-
accessTokenExpiresAt: true,
|
|
121
|
-
// @ts-expect-error Generic
|
|
122
|
-
refreshTokenExpiresAt: true,
|
|
123
|
-
// @ts-expect-error Generic
|
|
124
|
-
refreshTokenEncrypted: true,
|
|
125
|
-
},
|
|
126
|
-
first: 1,
|
|
127
|
-
context,
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
if (sessions.length === 0)
|
|
131
|
-
return {
|
|
132
|
-
sessionId: null,
|
|
133
|
-
user: null,
|
|
134
|
-
accessToken: null,
|
|
135
|
-
refreshToken: null,
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const session = sessions[0]
|
|
139
|
-
|
|
140
|
-
if (!session || !session?.user)
|
|
141
|
-
return {
|
|
142
|
-
sessionId: null,
|
|
143
|
-
user: null,
|
|
144
|
-
accessToken: null,
|
|
145
|
-
refreshToken: null,
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// CSRF check only for cookie-based sessions (enabled by default unless explicitly disabled)
|
|
149
|
-
if (
|
|
150
|
-
context.wabe.config.authentication?.session?.cookieSession &&
|
|
151
|
-
context.wabe.config.security?.disableCSRFProtection !== true
|
|
152
|
-
) {
|
|
153
|
-
const [receivedHmacHex, receivedRandomValue] = csrfToken.split('.')
|
|
154
|
-
|
|
155
|
-
if (!receivedHmacHex || !receivedRandomValue)
|
|
156
|
-
return {
|
|
157
|
-
sessionId: null,
|
|
158
|
-
user: null,
|
|
159
|
-
accessToken: null,
|
|
160
|
-
refreshToken: null,
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const currentSessionId = session.id
|
|
164
|
-
|
|
165
|
-
const message = `${currentSessionId.length}!${currentSessionId}!${receivedRandomValue?.length}!${receivedRandomValue}`
|
|
166
|
-
|
|
167
|
-
const csrfSecret =
|
|
168
|
-
context.wabe.config.authentication?.session?.csrfSecret || getJwtSecret(context)
|
|
169
|
-
|
|
170
|
-
const expectedHmac = crypto.createHmac('sha256', csrfSecret).update(message).digest('hex')
|
|
171
|
-
|
|
172
|
-
const isValid = crypto.timingSafeEqual(
|
|
173
|
-
Buffer.from(receivedHmacHex || '', 'hex'),
|
|
174
|
-
Buffer.from(expectedHmac, 'hex'),
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
if (!isValid)
|
|
178
|
-
return {
|
|
179
|
-
sessionId: null,
|
|
180
|
-
user: null,
|
|
181
|
-
accessToken: null,
|
|
182
|
-
refreshToken: null,
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// User check
|
|
187
|
-
|
|
188
|
-
const user = session.user
|
|
189
|
-
|
|
190
|
-
const userWithRole = await context.wabe.controllers.database.getObject({
|
|
191
|
-
className: 'User',
|
|
192
|
-
select: {
|
|
193
|
-
// @ts-expect-error
|
|
194
|
-
role: true,
|
|
195
|
-
},
|
|
196
|
-
context,
|
|
197
|
-
id: user.id,
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
// If access token is expired and refresh token is not expired
|
|
201
|
-
if (
|
|
202
|
-
new Date(session.accessTokenExpiresAt) < new Date() &&
|
|
203
|
-
new Date(session.refreshTokenExpiresAt) >= new Date() &&
|
|
204
|
-
session.refreshTokenEncrypted
|
|
205
|
-
) {
|
|
206
|
-
const decryptedRefreshToken = decryptDeterministicToken(
|
|
207
|
-
session.refreshTokenEncrypted as string,
|
|
208
|
-
getTokenEncryptionKey(context),
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
if (!decryptedRefreshToken)
|
|
212
|
-
return {
|
|
213
|
-
sessionId: null,
|
|
214
|
-
user: null,
|
|
215
|
-
accessToken: null,
|
|
216
|
-
refreshToken: null,
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const { accessToken: newAccessToken, refreshToken: newRefreshToken } = await this.refresh(
|
|
220
|
-
accessToken,
|
|
221
|
-
decryptedRefreshToken,
|
|
222
|
-
context,
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
sessionId: session.id,
|
|
227
|
-
user: {
|
|
228
|
-
...user,
|
|
229
|
-
role: userWithRole?.role,
|
|
230
|
-
},
|
|
231
|
-
accessToken: newAccessToken,
|
|
232
|
-
refreshToken: newRefreshToken,
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
sessionId: session.id,
|
|
238
|
-
user: {
|
|
239
|
-
...user,
|
|
240
|
-
role: userWithRole?.role,
|
|
241
|
-
},
|
|
242
|
-
accessToken,
|
|
243
|
-
refreshToken: decryptDeterministicToken(
|
|
244
|
-
session.refreshTokenEncrypted as string,
|
|
245
|
-
getTokenEncryptionKey(context),
|
|
246
|
-
),
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async create(userId: string, context: WabeContext<T>) {
|
|
251
|
-
const jwtTokenFields = context.wabe.config.authentication?.session?.jwtTokenFields
|
|
252
|
-
|
|
253
|
-
const nowSeconds = Math.floor(Date.now() / 1000)
|
|
254
|
-
|
|
255
|
-
const result = jwtTokenFields
|
|
256
|
-
? await context.wabe.controllers.database.getObject({
|
|
257
|
-
className: 'User',
|
|
258
|
-
select: jwtTokenFields,
|
|
259
|
-
context,
|
|
260
|
-
id: userId,
|
|
261
|
-
})
|
|
262
|
-
: undefined
|
|
263
|
-
|
|
264
|
-
const secretKey = getJwtSecret(context)
|
|
265
|
-
|
|
266
|
-
const signOptions: SignOptions = {
|
|
267
|
-
jwtid: crypto.randomUUID(),
|
|
268
|
-
algorithm: JWT_ALGORITHM,
|
|
269
|
-
}
|
|
270
|
-
const audience = context.wabe.config.authentication?.session?.jwtAudience
|
|
271
|
-
const issuer = context.wabe.config.authentication?.session?.jwtIssuer
|
|
272
|
-
if (audience) signOptions.audience = audience
|
|
273
|
-
if (issuer) signOptions.issuer = issuer
|
|
274
|
-
|
|
275
|
-
this.accessToken = jwt.sign(
|
|
276
|
-
{
|
|
277
|
-
userId,
|
|
278
|
-
user: result,
|
|
279
|
-
iat: nowSeconds,
|
|
280
|
-
exp: Math.floor(this.getAccessTokenExpireAt(context.wabe.config).getTime() / 1000),
|
|
281
|
-
},
|
|
282
|
-
secretKey,
|
|
283
|
-
{ ...signOptions, algorithm: JWT_ALGORITHM },
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
this.refreshToken = jwt.sign(
|
|
287
|
-
{
|
|
288
|
-
userId,
|
|
289
|
-
user: result,
|
|
290
|
-
iat: nowSeconds,
|
|
291
|
-
exp: Math.floor(this.getRefreshTokenExpireAt(context.wabe.config).getTime() / 1000),
|
|
292
|
-
},
|
|
293
|
-
secretKey,
|
|
294
|
-
{ ...signOptions, algorithm: JWT_ALGORITHM },
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
const accessTokenEncrypted = encryptDeterministicToken(
|
|
298
|
-
this.accessToken,
|
|
299
|
-
getTokenEncryptionKey(context),
|
|
300
|
-
)
|
|
301
|
-
const refreshTokenEncrypted = encryptDeterministicToken(
|
|
302
|
-
this.refreshToken,
|
|
303
|
-
getTokenEncryptionKey(context),
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
const res = await context.wabe.controllers.database.createObject({
|
|
307
|
-
className: '_Session',
|
|
308
|
-
context: contextWithRoot(context),
|
|
309
|
-
data: {
|
|
310
|
-
accessTokenEncrypted,
|
|
311
|
-
accessTokenExpiresAt: this.getAccessTokenExpireAt(context.wabe.config),
|
|
312
|
-
refreshTokenEncrypted,
|
|
313
|
-
refreshTokenExpiresAt: this.getRefreshTokenExpireAt(context.wabe.config),
|
|
314
|
-
user: userId,
|
|
315
|
-
},
|
|
316
|
-
// @ts-expect-error
|
|
317
|
-
select: { id: true },
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
if (!res) throw new Error('Session not created')
|
|
321
|
-
|
|
322
|
-
const sessionId = res.id
|
|
323
|
-
const randomValue = crypto.randomBytes(16).toString('hex')
|
|
324
|
-
const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`
|
|
325
|
-
|
|
326
|
-
const csrfSecret = context.wabe.config.authentication?.session?.csrfSecret || secretKey
|
|
327
|
-
|
|
328
|
-
const hmac = crypto.createHmac('sha256', csrfSecret).update(message).digest('hex')
|
|
329
|
-
|
|
330
|
-
const csrfToken = `${hmac}.${randomValue}`
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
accessToken: this.accessToken,
|
|
334
|
-
refreshToken: this.refreshToken,
|
|
335
|
-
csrfToken,
|
|
336
|
-
sessionId: res.id,
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
async refresh(accessToken: string, refreshToken: string, context: WabeContext<T>) {
|
|
341
|
-
const secretKey = getJwtSecret(context)
|
|
342
|
-
|
|
343
|
-
const verifyOptions = getJwtVerifyOptions(context)
|
|
344
|
-
|
|
345
|
-
if (!safeVerify(accessToken, secretKey, verifyOptions))
|
|
346
|
-
return {
|
|
347
|
-
accessToken: null,
|
|
348
|
-
refreshToken: null,
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (!safeVerify(refreshToken, secretKey, verifyOptions))
|
|
352
|
-
return {
|
|
353
|
-
accessToken: null,
|
|
354
|
-
refreshToken: null,
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const accessTokenEncrypted = encryptDeterministicToken(
|
|
358
|
-
accessToken,
|
|
359
|
-
getTokenEncryptionKey(context),
|
|
360
|
-
)
|
|
361
|
-
const incomingRefreshTokenEncrypted = encryptDeterministicToken(
|
|
362
|
-
refreshToken,
|
|
363
|
-
getTokenEncryptionKey(context),
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
const session = await context.wabe.controllers.database.getObjects({
|
|
367
|
-
className: '_Session',
|
|
368
|
-
// @ts-expect-error
|
|
369
|
-
where: {
|
|
370
|
-
accessTokenEncrypted: { equalTo: accessTokenEncrypted },
|
|
371
|
-
refreshTokenEncrypted: {
|
|
372
|
-
equalTo: incomingRefreshTokenEncrypted,
|
|
373
|
-
},
|
|
374
|
-
},
|
|
375
|
-
select: {
|
|
376
|
-
// @ts-expect-error
|
|
377
|
-
id: true,
|
|
378
|
-
// @ts-expect-error
|
|
379
|
-
user: {
|
|
380
|
-
id: true,
|
|
381
|
-
role: {
|
|
382
|
-
id: true,
|
|
383
|
-
name: true,
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
// @ts-expect-error
|
|
387
|
-
refreshTokenEncrypted: true,
|
|
388
|
-
// @ts-expect-error
|
|
389
|
-
refreshTokenExpiresAt: true,
|
|
390
|
-
},
|
|
391
|
-
context: contextWithRoot(context),
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
if (!session.length)
|
|
395
|
-
return {
|
|
396
|
-
accessToken: null,
|
|
397
|
-
refreshToken: null,
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (!session[0]) throw new Error('Session not found')
|
|
401
|
-
|
|
402
|
-
const {
|
|
403
|
-
refreshTokenExpiresAt,
|
|
404
|
-
user,
|
|
405
|
-
refreshTokenEncrypted: storedRefreshTokenEncrypted,
|
|
406
|
-
id,
|
|
407
|
-
} = session[0]
|
|
408
|
-
|
|
409
|
-
if (new Date(refreshTokenExpiresAt) < new Date(Date.now()))
|
|
410
|
-
throw new Error('Refresh token expired')
|
|
411
|
-
|
|
412
|
-
const decryptedRefreshToken =
|
|
413
|
-
decryptDeterministicToken(storedRefreshTokenEncrypted, getTokenEncryptionKey(context)) ||
|
|
414
|
-
refreshToken
|
|
415
|
-
|
|
416
|
-
if (!decryptedRefreshToken || decryptedRefreshToken !== refreshToken)
|
|
417
|
-
throw new Error('Invalid refresh token')
|
|
418
|
-
|
|
419
|
-
// Always rotate tokens on refresh
|
|
420
|
-
const userId = user?.id
|
|
421
|
-
|
|
422
|
-
if (!userId)
|
|
423
|
-
return {
|
|
424
|
-
accessToken: null,
|
|
425
|
-
refreshToken: null,
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const jwtTokenFields = context.wabe.config.authentication?.session?.jwtTokenFields
|
|
429
|
-
|
|
430
|
-
const result = jwtTokenFields
|
|
431
|
-
? await context.wabe.controllers.database.getObject({
|
|
432
|
-
className: 'User',
|
|
433
|
-
select: jwtTokenFields,
|
|
434
|
-
context,
|
|
435
|
-
id: userId,
|
|
436
|
-
})
|
|
437
|
-
: undefined
|
|
438
|
-
|
|
439
|
-
const nowSeconds = Math.floor(Date.now() / 1000)
|
|
440
|
-
|
|
441
|
-
const signOptions: SignOptions = {
|
|
442
|
-
jwtid: crypto.randomUUID(),
|
|
443
|
-
algorithm: JWT_ALGORITHM,
|
|
444
|
-
}
|
|
445
|
-
const audience = context.wabe.config.authentication?.session?.jwtAudience
|
|
446
|
-
const issuer = context.wabe.config.authentication?.session?.jwtIssuer
|
|
447
|
-
if (audience) signOptions.audience = audience
|
|
448
|
-
if (issuer) signOptions.issuer = issuer
|
|
449
|
-
|
|
450
|
-
const newAccessToken = jwt.sign(
|
|
451
|
-
{
|
|
452
|
-
userId,
|
|
453
|
-
user: result,
|
|
454
|
-
iat: nowSeconds,
|
|
455
|
-
exp: Math.floor(this.getAccessTokenExpireAt(context.wabe.config).getTime() / 1000),
|
|
456
|
-
},
|
|
457
|
-
secretKey,
|
|
458
|
-
{ ...signOptions, algorithm: JWT_ALGORITHM },
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
const newRefreshToken = jwt.sign(
|
|
462
|
-
{
|
|
463
|
-
userId,
|
|
464
|
-
user: result,
|
|
465
|
-
iat: nowSeconds,
|
|
466
|
-
exp: Math.floor(this.getRefreshTokenExpireAt(context.wabe.config).getTime() / 1000),
|
|
467
|
-
},
|
|
468
|
-
secretKey,
|
|
469
|
-
{ ...signOptions, algorithm: JWT_ALGORITHM },
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
const newAccessTokenEncrypted = encryptDeterministicToken(
|
|
473
|
-
newAccessToken,
|
|
474
|
-
getTokenEncryptionKey(context),
|
|
475
|
-
)
|
|
476
|
-
const newRefreshTokenEncrypted = encryptDeterministicToken(
|
|
477
|
-
newRefreshToken,
|
|
478
|
-
getTokenEncryptionKey(context),
|
|
479
|
-
)
|
|
480
|
-
|
|
481
|
-
await context.wabe.controllers.database.updateObject({
|
|
482
|
-
className: '_Session',
|
|
483
|
-
context: contextWithRoot(context),
|
|
484
|
-
id,
|
|
485
|
-
data: {
|
|
486
|
-
accessTokenEncrypted: newAccessTokenEncrypted,
|
|
487
|
-
accessTokenExpiresAt: this.getAccessTokenExpireAt(context.wabe.config),
|
|
488
|
-
refreshTokenEncrypted: newRefreshTokenEncrypted,
|
|
489
|
-
refreshTokenExpiresAt: this.getRefreshTokenExpireAt(context.wabe.config),
|
|
490
|
-
},
|
|
491
|
-
select: {},
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
return {
|
|
495
|
-
accessToken: newAccessToken,
|
|
496
|
-
refreshToken: newRefreshToken,
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
async delete(context: WabeContext<T>) {
|
|
501
|
-
if (!context.sessionId) return
|
|
502
|
-
|
|
503
|
-
await context.wabe.controllers.database.deleteObject({
|
|
504
|
-
className: '_Session',
|
|
505
|
-
context: contextWithRoot(context),
|
|
506
|
-
id: context.sessionId,
|
|
507
|
-
select: {},
|
|
508
|
-
})
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
_isRefreshTokenExpired(userRefreshTokenExpiresAt: Date, refreshTokenAgeInMs: number) {
|
|
512
|
-
const refreshTokenEmittedAt = userRefreshTokenExpiresAt.getTime() - refreshTokenAgeInMs
|
|
513
|
-
const numberOfMsSinceRefreshTokenEmitted = Date.now() - refreshTokenEmittedAt
|
|
514
|
-
|
|
515
|
-
return numberOfMsSinceRefreshTokenEmitted >= 0.75 * refreshTokenAgeInMs
|
|
516
|
-
}
|
|
517
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { WabeConfig, WabeTypes } from '../server'
|
|
2
|
-
|
|
3
|
-
export const getSessionCookieSameSite = <T extends WabeTypes>(config: WabeConfig<T>) => {
|
|
4
|
-
const frontDomain = config.authentication?.frontDomain
|
|
5
|
-
const backDomain = config.authentication?.backDomain
|
|
6
|
-
|
|
7
|
-
if (frontDomain && backDomain && frontDomain !== backDomain) return 'None'
|
|
8
|
-
|
|
9
|
-
return 'Strict'
|
|
10
|
-
}
|