wabe 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -32
- package/dev/index.ts +215 -0
- package/dist/authentication/OTP.d.ts +3 -0
- package/dist/authentication/Session.d.ts +5 -2
- package/dist/authentication/interface.d.ts +25 -3
- package/dist/authentication/utils.d.ts +0 -1
- package/dist/database/DatabaseController.d.ts +10 -10
- package/dist/database/interface.d.ts +5 -13
- package/dist/email/interface.d.ts +1 -1
- package/dist/graphql/resolvers.d.ts +4 -2
- package/dist/hooks/authentication.d.ts +2 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +9130 -9041
- package/dist/server/index.d.ts +4 -2
- package/dist/server/interface.d.ts +1 -0
- package/dist/utils/crypto.d.ts +18 -0
- package/dist/utils/export.d.ts +1 -0
- package/dist/utils/helper.d.ts +4 -1
- package/dist/utils/index.d.ts +16 -3
- package/generated/schema.graphql +18 -15
- package/generated/wabe.ts +6 -5
- package/package.json +52 -53
- package/src/authentication/OTP.test.ts +69 -0
- package/src/authentication/OTP.ts +66 -0
- package/src/authentication/Session.test.ts +665 -0
- package/src/authentication/Session.ts +529 -0
- package/src/authentication/defaultAuthentication.ts +214 -0
- package/src/authentication/index.ts +3 -0
- package/src/authentication/interface.ts +157 -0
- package/src/authentication/oauth/GitHub.test.ts +105 -0
- package/src/authentication/oauth/GitHub.ts +133 -0
- package/src/authentication/oauth/Google.test.ts +105 -0
- package/src/authentication/oauth/Google.ts +110 -0
- package/src/authentication/oauth/Oauth2Client.test.ts +225 -0
- package/src/authentication/oauth/Oauth2Client.ts +140 -0
- package/src/authentication/oauth/index.ts +2 -0
- package/src/authentication/oauth/utils.test.ts +35 -0
- package/src/authentication/oauth/utils.ts +28 -0
- package/src/authentication/providers/EmailOTP.test.ts +138 -0
- package/src/authentication/providers/EmailOTP.ts +93 -0
- package/src/authentication/providers/EmailPassword.test.ts +187 -0
- package/src/authentication/providers/EmailPassword.ts +130 -0
- package/src/authentication/providers/EmailPasswordSRP.test.ts +206 -0
- package/src/authentication/providers/EmailPasswordSRP.ts +184 -0
- package/src/authentication/providers/GitHub.ts +30 -0
- package/src/authentication/providers/Google.ts +30 -0
- package/src/authentication/providers/OAuth.test.ts +185 -0
- package/src/authentication/providers/OAuth.ts +112 -0
- package/src/authentication/providers/PhonePassword.test.ts +187 -0
- package/src/authentication/providers/PhonePassword.ts +129 -0
- package/src/authentication/providers/QRCodeOTP.test.ts +79 -0
- package/src/authentication/providers/QRCodeOTP.ts +65 -0
- package/src/authentication/providers/index.ts +6 -0
- package/src/authentication/resolvers/refreshResolver.test.ts +37 -0
- package/src/authentication/resolvers/refreshResolver.ts +20 -0
- package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
- package/src/authentication/resolvers/signInWithResolver.test.ts +307 -0
- package/src/authentication/resolvers/signInWithResolver.ts +102 -0
- package/src/authentication/resolvers/signOutResolver.test.ts +41 -0
- package/src/authentication/resolvers/signOutResolver.ts +22 -0
- package/src/authentication/resolvers/signUpWithResolver.test.ts +186 -0
- package/src/authentication/resolvers/signUpWithResolver.ts +69 -0
- package/src/authentication/resolvers/verifyChallenge.test.ts +136 -0
- package/src/authentication/resolvers/verifyChallenge.ts +69 -0
- package/src/authentication/roles.test.ts +59 -0
- package/src/authentication/roles.ts +40 -0
- package/src/authentication/utils.test.ts +99 -0
- package/src/authentication/utils.ts +43 -0
- package/src/cache/InMemoryCache.test.ts +62 -0
- package/src/cache/InMemoryCache.ts +45 -0
- package/src/cron/index.test.ts +17 -0
- package/src/cron/index.ts +46 -0
- package/src/database/DatabaseController.test.ts +625 -0
- package/src/database/DatabaseController.ts +983 -0
- package/src/database/index.test.ts +1230 -0
- package/src/database/index.ts +9 -0
- package/src/database/interface.ts +312 -0
- package/src/email/DevAdapter.ts +8 -0
- package/src/email/EmailController.test.ts +29 -0
- package/src/email/EmailController.ts +13 -0
- package/src/email/index.ts +2 -0
- package/src/email/interface.ts +36 -0
- package/src/email/templates/sendOtpCode.ts +120 -0
- package/src/file/FileController.ts +28 -0
- package/src/file/FileDevAdapter.ts +54 -0
- package/src/file/hookDeleteFile.ts +27 -0
- package/src/file/hookReadFile.ts +70 -0
- package/src/file/hookUploadFile.ts +53 -0
- package/src/file/index.test.ts +979 -0
- package/src/file/index.ts +2 -0
- package/src/file/interface.ts +42 -0
- package/src/graphql/GraphQLSchema.test.ts +4399 -0
- package/src/graphql/GraphQLSchema.ts +928 -0
- package/src/graphql/index.ts +2 -0
- package/src/graphql/parseGraphqlSchema.ts +94 -0
- package/src/graphql/parser.test.ts +217 -0
- package/src/graphql/parser.ts +566 -0
- package/src/graphql/pointerAndRelationFunction.ts +200 -0
- package/src/graphql/resolvers.ts +467 -0
- package/src/graphql/tests/aggregation.test.ts +1123 -0
- package/src/graphql/tests/e2e.test.ts +596 -0
- package/src/graphql/tests/scalars.test.ts +250 -0
- package/src/graphql/types.ts +219 -0
- package/src/hooks/HookObject.test.ts +122 -0
- package/src/hooks/HookObject.ts +168 -0
- package/src/hooks/authentication.ts +76 -0
- package/src/hooks/createUser.test.ts +77 -0
- package/src/hooks/createUser.ts +10 -0
- package/src/hooks/defaultFields.test.ts +187 -0
- package/src/hooks/defaultFields.ts +40 -0
- package/src/hooks/deleteSession.test.ts +181 -0
- package/src/hooks/deleteSession.ts +20 -0
- package/src/hooks/hashFieldHook.test.ts +163 -0
- package/src/hooks/hashFieldHook.ts +97 -0
- package/src/hooks/index.test.ts +207 -0
- package/src/hooks/index.ts +430 -0
- package/src/hooks/permissions.test.ts +424 -0
- package/src/hooks/permissions.ts +113 -0
- package/src/hooks/protected.test.ts +551 -0
- package/src/hooks/protected.ts +72 -0
- package/src/hooks/searchableFields.test.ts +166 -0
- package/src/hooks/searchableFields.ts +98 -0
- package/src/hooks/session.test.ts +138 -0
- package/src/hooks/session.ts +78 -0
- package/src/hooks/setEmail.test.ts +216 -0
- package/src/hooks/setEmail.ts +35 -0
- package/src/hooks/setupAcl.test.ts +589 -0
- package/src/hooks/setupAcl.ts +29 -0
- package/src/index.ts +9 -0
- package/src/schema/Schema.test.ts +484 -0
- package/src/schema/Schema.ts +795 -0
- package/src/schema/defaultResolvers.ts +94 -0
- package/src/schema/index.ts +1 -0
- package/src/schema/resolvers/meResolver.test.ts +62 -0
- package/src/schema/resolvers/meResolver.ts +14 -0
- package/src/schema/resolvers/newFile.ts +0 -0
- package/src/schema/resolvers/resetPassword.test.ts +345 -0
- package/src/schema/resolvers/resetPassword.ts +64 -0
- package/src/schema/resolvers/sendEmail.test.ts +118 -0
- package/src/schema/resolvers/sendEmail.ts +21 -0
- package/src/schema/resolvers/sendOtpCode.test.ts +153 -0
- package/src/schema/resolvers/sendOtpCode.ts +52 -0
- package/src/security.test.ts +3461 -0
- package/src/server/defaultSessionHandler.test.ts +66 -0
- package/src/server/defaultSessionHandler.ts +115 -0
- package/src/server/generateCodegen.ts +476 -0
- package/src/server/index.test.ts +552 -0
- package/src/server/index.ts +354 -0
- package/src/server/interface.ts +11 -0
- package/src/server/routes/authHandler.ts +187 -0
- package/src/server/routes/index.ts +40 -0
- package/src/utils/crypto.test.ts +41 -0
- package/src/utils/crypto.ts +121 -0
- package/src/utils/export.ts +13 -0
- package/src/utils/helper.ts +195 -0
- package/src/utils/index.test.ts +11 -0
- package/src/utils/index.ts +201 -0
- package/src/utils/preload.ts +8 -0
- package/src/utils/testHelper.ts +117 -0
- package/tsconfig.json +32 -0
- package/bunfig.toml +0 -4
- package/dist/ai/index.d.ts +0 -1
- package/dist/ai/interface.d.ts +0 -9
- /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
package/dist/server/index.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ import type { Context, CorsOptions, RateLimitOptions } from "wobe";
|
|
|
11
11
|
import type { WabeContext } from "./interface";
|
|
12
12
|
import type { EmailConfig } from "../email";
|
|
13
13
|
import { EmailController } from "../email/EmailController";
|
|
14
|
-
import type { AIConfig } from "../ai";
|
|
15
14
|
import { FileController } from "../file/FileController";
|
|
16
15
|
import type { CronConfig } from "../cron";
|
|
17
16
|
import type { FileConfig } from "../file";
|
|
@@ -19,9 +18,13 @@ type SecurityConfig = {
|
|
|
19
18
|
corsOptions?: CorsOptions;
|
|
20
19
|
rateLimit?: RateLimitOptions;
|
|
21
20
|
hideSensitiveErrorMessage?: boolean;
|
|
21
|
+
disableCSRFProtection?: boolean;
|
|
22
|
+
allowIntrospectionInProduction?: boolean;
|
|
23
|
+
maxGraphqlDepth?: number;
|
|
22
24
|
};
|
|
23
25
|
export * from "./interface";
|
|
24
26
|
export * from "./routes";
|
|
27
|
+
export declare const defaultRoles: unknown;
|
|
25
28
|
export interface WabeConfig<T extends WabeTypes> {
|
|
26
29
|
port: number;
|
|
27
30
|
isProduction: boolean;
|
|
@@ -41,7 +44,6 @@ export interface WabeConfig<T extends WabeTypes> {
|
|
|
41
44
|
rootKey: string;
|
|
42
45
|
hooks?: Hook<T, any>[];
|
|
43
46
|
email?: EmailConfig;
|
|
44
|
-
ai?: AIConfig;
|
|
45
47
|
file?: FileConfig<T>;
|
|
46
48
|
crons?: CronConfig<T>;
|
|
47
49
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hash a string with Argon2id and PHC format
|
|
3
|
+
* @return : Returns the PHC format of the hashed text
|
|
4
|
+
*/
|
|
5
|
+
export declare const hashArgon2: unknown;
|
|
6
|
+
/*
|
|
7
|
+
* Verify if a hash matchs with a string
|
|
8
|
+
* @return : Returns true if the password matchs with the hash, false otherwise
|
|
9
|
+
*/
|
|
10
|
+
export declare const verifyArgon2: unknown;
|
|
11
|
+
export declare const isArgon2Hash: (value: string) => boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Deterministic AES-256-GCM encryption for tokens.
|
|
14
|
+
* IV is derived via HMAC-SHA256(key, token) to allow equality checks without storing plaintext.
|
|
15
|
+
* Caller must provide a strong 32-byte key (already derived/hashed).
|
|
16
|
+
*/
|
|
17
|
+
export declare const encryptDeterministicToken: (token: string, key: Buffer) => string;
|
|
18
|
+
export declare const decryptDeterministicToken: (encryptedToken: string | undefined, key: Buffer) => string | null;
|
package/dist/utils/export.d.ts
CHANGED
package/dist/utils/helper.d.ts
CHANGED
|
@@ -10,7 +10,10 @@ export interface DevWabeTypes extends WabeTypes {
|
|
|
10
10
|
export declare const firstLetterUpperCase: (str: string) => string;
|
|
11
11
|
export declare const getGraphqlClient: (port: number) => GraphQLClient;
|
|
12
12
|
export declare const getAnonymousClient: (port: number) => GraphQLClient;
|
|
13
|
-
export declare const getUserClient: (port: number,
|
|
13
|
+
export declare const getUserClient: (port: number, options: {
|
|
14
|
+
accessToken?: string;
|
|
15
|
+
csrfToken?: string;
|
|
16
|
+
}) => GraphQLClient;
|
|
14
17
|
export declare const getAdminUserClient: (port: number, wabe: Wabe<DevWabeTypes>, { email, password }: {
|
|
15
18
|
email: string;
|
|
16
19
|
password: string;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { ClassInterface } from "../schema";
|
|
2
|
-
import type { WabeTypes, WabeConfig } from "../server";
|
|
3
|
-
export declare const
|
|
2
|
+
import type { WabeTypes, WabeConfig, WabeContext } from "../server";
|
|
3
|
+
export declare const contextWithoutGraphQLCall: (context: WabeContext<any>) => WabeContext<any>;
|
|
4
|
+
type Base32Variant = "RFC3548" | "RFC4648" | "RFC4648-HEX" | "Crockford";
|
|
5
|
+
interface Base32Options {
|
|
6
|
+
padding?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Convert supported input types to Uint8Array.
|
|
10
|
+
*/
|
|
11
|
+
export declare const toUint8Array: (data: string | ArrayBuffer | Uint8Array | Buffer) => Uint8Array;
|
|
12
|
+
/**
|
|
13
|
+
* Encode binary data to base32 using specified variant.
|
|
14
|
+
* Base on https://github.com/LinusU/base32-encode/blob/master/index.js
|
|
15
|
+
*/
|
|
16
|
+
export declare const base32Encode: (data: string | ArrayBuffer | Uint8Array | Buffer, variant: Base32Variant, options?: Base32Options) => string;
|
|
4
17
|
export declare const getNewObjectAfterUpdateNestedProperty: unknown;
|
|
5
18
|
export declare const getNestedProperty: unknown;
|
|
6
|
-
export declare const isArgon2Hash: (value: string) => boolean;
|
|
7
19
|
export declare const firstLetterInUpperCase: unknown;
|
|
8
20
|
export declare const firstLetterInLowerCase: unknown;
|
|
9
21
|
export declare const getClassFromClassName: <T extends WabeTypes>(className: string, config: WabeConfig<any>) => ClassInterface<T>;
|
|
@@ -20,3 +32,4 @@ export declare const getCookieInRequestHeaders: unknown;
|
|
|
20
32
|
* - trim
|
|
21
33
|
*/
|
|
22
34
|
export declare const tokenize: unknown;
|
|
35
|
+
export {};
|
package/generated/schema.graphql
CHANGED
|
@@ -13,6 +13,7 @@ enum AuthenticationProvider {
|
|
|
13
13
|
|
|
14
14
|
enum SecondaryFactor {
|
|
15
15
|
emailOTP
|
|
16
|
+
qrcodeOTP
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
"""Object containing information about the file"""
|
|
@@ -424,9 +425,9 @@ input PostRelationInput {
|
|
|
424
425
|
type _Session {
|
|
425
426
|
id: ID!
|
|
426
427
|
user: User
|
|
427
|
-
|
|
428
|
+
accessTokenEncrypted: String!
|
|
428
429
|
accessTokenExpiresAt: Date!
|
|
429
|
-
|
|
430
|
+
refreshTokenEncrypted: String!
|
|
430
431
|
refreshTokenExpiresAt: Date!
|
|
431
432
|
acl: _SessionACLObject
|
|
432
433
|
createdAt: Date
|
|
@@ -453,9 +454,9 @@ type _SessionACLObjectRolesACL {
|
|
|
453
454
|
|
|
454
455
|
input _SessionInput {
|
|
455
456
|
user: UserPointerInput
|
|
456
|
-
|
|
457
|
+
accessTokenEncrypted: String!
|
|
457
458
|
accessTokenExpiresAt: Date!
|
|
458
|
-
|
|
459
|
+
refreshTokenEncrypted: String!
|
|
459
460
|
refreshTokenExpiresAt: Date!
|
|
460
461
|
acl: _SessionACLObjectInput
|
|
461
462
|
createdAt: Date
|
|
@@ -489,9 +490,9 @@ input _SessionPointerInput {
|
|
|
489
490
|
|
|
490
491
|
input _SessionCreateFieldsInput {
|
|
491
492
|
user: UserPointerInput
|
|
492
|
-
|
|
493
|
+
accessTokenEncrypted: String
|
|
493
494
|
accessTokenExpiresAt: Date
|
|
494
|
-
|
|
495
|
+
refreshTokenEncrypted: String
|
|
495
496
|
refreshTokenExpiresAt: Date
|
|
496
497
|
acl: _SessionACLObjectCreateFieldsInput
|
|
497
498
|
createdAt: Date
|
|
@@ -936,9 +937,9 @@ input RoleACLObjectRolesACLWhereInput {
|
|
|
936
937
|
input _SessionWhereInput {
|
|
937
938
|
id: IdWhereInput
|
|
938
939
|
user: UserWhereInput
|
|
939
|
-
|
|
940
|
+
accessTokenEncrypted: StringWhereInput
|
|
940
941
|
accessTokenExpiresAt: DateWhereInput
|
|
941
|
-
|
|
942
|
+
refreshTokenEncrypted: StringWhereInput
|
|
942
943
|
refreshTokenExpiresAt: DateWhereInput
|
|
943
944
|
acl: _SessionACLObjectWhereInput
|
|
944
945
|
createdAt: DateWhereInput
|
|
@@ -1098,12 +1099,12 @@ enum PostOrder {
|
|
|
1098
1099
|
enum _SessionOrder {
|
|
1099
1100
|
user_ASC
|
|
1100
1101
|
user_DESC
|
|
1101
|
-
|
|
1102
|
-
|
|
1102
|
+
accessTokenEncrypted_ASC
|
|
1103
|
+
accessTokenEncrypted_DESC
|
|
1103
1104
|
accessTokenExpiresAt_ASC
|
|
1104
1105
|
accessTokenExpiresAt_DESC
|
|
1105
|
-
|
|
1106
|
-
|
|
1106
|
+
refreshTokenEncrypted_ASC
|
|
1107
|
+
refreshTokenEncrypted_DESC
|
|
1107
1108
|
refreshTokenExpiresAt_ASC
|
|
1108
1109
|
refreshTokenExpiresAt_DESC
|
|
1109
1110
|
acl_ASC
|
|
@@ -1506,9 +1507,9 @@ input Update_SessionInput {
|
|
|
1506
1507
|
|
|
1507
1508
|
input _SessionUpdateFieldsInput {
|
|
1508
1509
|
user: UserPointerInput
|
|
1509
|
-
|
|
1510
|
+
accessTokenEncrypted: String
|
|
1510
1511
|
accessTokenExpiresAt: Date
|
|
1511
|
-
|
|
1512
|
+
refreshTokenEncrypted: String
|
|
1512
1513
|
refreshTokenExpiresAt: Date
|
|
1513
1514
|
acl: _SessionACLObjectUpdateFieldsInput
|
|
1514
1515
|
createdAt: Date
|
|
@@ -1742,9 +1743,10 @@ input SendEmailInput {
|
|
|
1742
1743
|
}
|
|
1743
1744
|
|
|
1744
1745
|
type SignInWithOutput {
|
|
1745
|
-
|
|
1746
|
+
user: User
|
|
1746
1747
|
accessToken: String
|
|
1747
1748
|
refreshToken: String
|
|
1749
|
+
csrfToken: String
|
|
1748
1750
|
srp: SignInWithOutputSRPOutputSignInWith
|
|
1749
1751
|
}
|
|
1750
1752
|
|
|
@@ -1796,6 +1798,7 @@ type SignUpWithOutput {
|
|
|
1796
1798
|
id: String
|
|
1797
1799
|
accessToken: String!
|
|
1798
1800
|
refreshToken: String!
|
|
1801
|
+
csrfToken: String
|
|
1799
1802
|
}
|
|
1800
1803
|
|
|
1801
1804
|
input SignUpWithInput {
|
package/generated/wabe.ts
CHANGED
|
@@ -12,7 +12,8 @@ export enum AuthenticationProvider {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export enum SecondaryFactor {
|
|
15
|
-
emailOTP = "emailOTP"
|
|
15
|
+
emailOTP = "emailOTP",
|
|
16
|
+
qrcodeOTP = "qrcodeOTP"
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export type ACLObjectUsersACL = {
|
|
@@ -115,9 +116,9 @@ export type Post = {
|
|
|
115
116
|
export type _Session = {
|
|
116
117
|
id: string,
|
|
117
118
|
user: User,
|
|
118
|
-
|
|
119
|
+
accessTokenEncrypted: string,
|
|
119
120
|
accessTokenExpiresAt: string,
|
|
120
|
-
|
|
121
|
+
refreshTokenEncrypted: string,
|
|
121
122
|
refreshTokenExpiresAt: string,
|
|
122
123
|
acl?: ACLObject,
|
|
123
124
|
createdAt?: string,
|
|
@@ -180,9 +181,9 @@ export type WherePost = {
|
|
|
180
181
|
export type Where_Session = {
|
|
181
182
|
id: string,
|
|
182
183
|
user: User,
|
|
183
|
-
|
|
184
|
+
accessTokenEncrypted: string,
|
|
184
185
|
accessTokenExpiresAt: Date,
|
|
185
|
-
|
|
186
|
+
refreshTokenEncrypted: string,
|
|
186
187
|
refreshTokenExpiresAt: Date,
|
|
187
188
|
acl?: ACLObject,
|
|
188
189
|
createdAt?: Date,
|
package/package.json
CHANGED
|
@@ -1,55 +1,54 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
2
|
+
"name": "wabe",
|
|
3
|
+
"version": "0.6.10",
|
|
4
|
+
"description": "Your backend without vendor lock-in in Typescript",
|
|
5
|
+
"homepage": "https://palixir.github.io/wabe/",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "coratgerl",
|
|
8
|
+
"url": "https://github.com/coratgerl"
|
|
9
|
+
},
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"backend",
|
|
13
|
+
"wabe",
|
|
14
|
+
"graphql",
|
|
15
|
+
"baas"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/palixir/wabe.git"
|
|
20
|
+
},
|
|
21
|
+
"main": "dist/index.js",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "bun --filter wabe-build build:package $(pwd)",
|
|
24
|
+
"check": "tsc --project $(pwd)/tsconfig.json",
|
|
25
|
+
"lint": "biome lint . --no-errors-on-unmatched",
|
|
26
|
+
"ci": "bun generate:codegen && bun lint $(pwd) && bun check && bun test src",
|
|
27
|
+
"format": "biome format --write .",
|
|
28
|
+
"dev": "bun run --watch dev/index.ts",
|
|
29
|
+
"generate:codegen": "touch generated/wabe.ts && CODEGEN=true bun dev/index.ts"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"croner": "9.0.0",
|
|
33
|
+
"graphql": "16.12.0",
|
|
34
|
+
"js-srp6a": "1.0.2",
|
|
35
|
+
"jsonwebtoken": "9.0.2",
|
|
36
|
+
"libphonenumber-js": "1.11.18",
|
|
37
|
+
"otplib": "12.0.1",
|
|
38
|
+
"p-retry": "7.1.0",
|
|
39
|
+
"wobe": "1.1.14",
|
|
40
|
+
"wobe-graphql-yoga": "1.2.9"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/jsonwebtoken": "9.0.6",
|
|
44
|
+
"@types/uuid": "9.0.6",
|
|
45
|
+
"graphql-request": "6.1.0",
|
|
46
|
+
"get-port": "7.1.0",
|
|
47
|
+
"uuid": "13.0.0",
|
|
48
|
+
"wabe-mongodb-launcher": "0.5.2",
|
|
49
|
+
"wabe-pluralize": "0.0.1",
|
|
50
|
+
"wabe-build": "0.5.0",
|
|
51
|
+
"wabe-mongodb": "0.5.2",
|
|
52
|
+
"wabe": "0.6.9"
|
|
53
|
+
}
|
|
55
54
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test'
|
|
2
|
+
import { OTP } from './OTP'
|
|
3
|
+
|
|
4
|
+
describe('OTP', () => {
|
|
5
|
+
it('should generate a valid OTP code', () => {
|
|
6
|
+
const otp = new OTP('rootKey')
|
|
7
|
+
|
|
8
|
+
const otpValue = otp.generate('userId')
|
|
9
|
+
|
|
10
|
+
expect(otpValue.length).toBe(6)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should verify a valid OTP code', () => {
|
|
14
|
+
const otp = new OTP('rootKey')
|
|
15
|
+
|
|
16
|
+
const otpValue = otp.generate('userId')
|
|
17
|
+
|
|
18
|
+
expect(otpValue.length).toBe(6)
|
|
19
|
+
|
|
20
|
+
expect(otp.verify(otpValue, 'userId')).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should not verify an invalid OTP code', () => {
|
|
24
|
+
const otp = new OTP('rootKey')
|
|
25
|
+
|
|
26
|
+
const otpValue = otp.generate('userId')
|
|
27
|
+
|
|
28
|
+
expect(otpValue.length).toBe(6)
|
|
29
|
+
|
|
30
|
+
expect(otp.verify('invalidOtp', 'userId')).toBe(false)
|
|
31
|
+
|
|
32
|
+
const otpValue2 = otp.generate('invalidUserId')
|
|
33
|
+
|
|
34
|
+
expect(otpValue2.length).toBe(6)
|
|
35
|
+
|
|
36
|
+
expect(otp.verify(otpValue2, 'userId')).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should not verify an invalid OTP code (more than 5 minutes)', () => {
|
|
40
|
+
// Directly test the timeout is flaky we only test that the correct value is passed to totp
|
|
41
|
+
const otp = new OTP('rootKey')
|
|
42
|
+
|
|
43
|
+
expect(otp.internalTotp.options.window).toEqual([1, 0])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should generate a valid keyuri', () => {
|
|
47
|
+
const otp = new OTP('rootKey')
|
|
48
|
+
|
|
49
|
+
const keyuri = otp.generateKeyuri({
|
|
50
|
+
userId: 'userId',
|
|
51
|
+
emailOrUsername: 'email@test.fr',
|
|
52
|
+
applicationName: 'Wabe',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
expect(keyuri).toBe(
|
|
56
|
+
'otpauth://totp/Wabe:email%40test.fr?secret=O54OZDANWM2YFHJKJMMVMQSV7DUMUZFT3BWE4Z5NOQCAATGGHKYA&period=30&digits=6&algorithm=SHA1&issuer=Wabe',
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should verify an OTP generated from authenticator', () => {
|
|
61
|
+
const otp = new OTP('rootKey')
|
|
62
|
+
|
|
63
|
+
const code = otp.authenticatorGenerate('userId')
|
|
64
|
+
|
|
65
|
+
const isValid = otp.authenticatorVerify(code, 'userId')
|
|
66
|
+
|
|
67
|
+
expect(isValid).toBe(true)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { totp, authenticator } from 'otplib'
|
|
2
|
+
import type { TOTP } from 'otplib/core'
|
|
3
|
+
import { createHash } from 'node:crypto'
|
|
4
|
+
import { base32Encode } from 'src/utils'
|
|
5
|
+
|
|
6
|
+
const ONE_WINDOW = 1
|
|
7
|
+
|
|
8
|
+
export class OTP {
|
|
9
|
+
private secret: string
|
|
10
|
+
public internalTotp: TOTP
|
|
11
|
+
|
|
12
|
+
constructor(rootKey: string) {
|
|
13
|
+
this.secret = rootKey
|
|
14
|
+
this.internalTotp = totp.clone({
|
|
15
|
+
window: [ONE_WINDOW, 0],
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
deriveSecret(userId: string): string {
|
|
20
|
+
const hash = createHash('sha256')
|
|
21
|
+
.update(`${this.secret}:${userId}`)
|
|
22
|
+
.digest()
|
|
23
|
+
|
|
24
|
+
return base32Encode(hash, 'RFC4648', { padding: false })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
generate(userId: string): string {
|
|
28
|
+
const secret = this.deriveSecret(userId)
|
|
29
|
+
|
|
30
|
+
return this.internalTotp.generate(secret)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
verify(otp: string, userId: string): boolean {
|
|
34
|
+
const secret = this.deriveSecret(userId)
|
|
35
|
+
|
|
36
|
+
return this.internalTotp.verify({ secret, token: otp })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
authenticatorGenerate(userId: string): string {
|
|
40
|
+
const secret = this.deriveSecret(userId)
|
|
41
|
+
return authenticator.generate(secret)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
authenticatorVerify(otp: string, userId: string): boolean {
|
|
45
|
+
const secret = this.deriveSecret(userId)
|
|
46
|
+
|
|
47
|
+
return authenticator.verify({
|
|
48
|
+
secret,
|
|
49
|
+
token: otp,
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
generateKeyuri({
|
|
54
|
+
userId,
|
|
55
|
+
emailOrUsername,
|
|
56
|
+
applicationName,
|
|
57
|
+
}: {
|
|
58
|
+
userId: string
|
|
59
|
+
emailOrUsername: string
|
|
60
|
+
applicationName: string
|
|
61
|
+
}): string {
|
|
62
|
+
const secret = this.deriveSecret(userId)
|
|
63
|
+
|
|
64
|
+
return authenticator.keyuri(emailOrUsername, applicationName, secret)
|
|
65
|
+
}
|
|
66
|
+
}
|