@appconda/nextjs 1.0.86 → 1.0.88
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/dist/actions/actionClient.js +1 -1
- package/dist/actions/auth.d.ts +1 -1
- package/dist/actions/auth.js +1 -1
- package/dist/actions/authOptions.d.ts +1 -2
- package/dist/actions/authOptions.js +211 -203
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/crypto.d.ts +23 -0
- package/dist/lib/crypto.js +76 -0
- package/dist/lib/env.js +6 -2
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/jwt.d.ts +12 -0
- package/dist/lib/jwt.js +102 -0
- package/package.json +2 -1
- package/src/actions/actionClient.ts +1 -1
- package/src/actions/auth.ts +1 -1
- package/src/actions/authOptions.ts +218 -209
- package/src/index.ts +1 -0
- package/src/lib/crypto.ts +103 -0
- package/src/lib/env.ts +6 -2
- package/src/lib/index.ts +1 -0
- package/src/lib/jwt.ts +113 -0
@@ -21,7 +21,7 @@ export const actionClient = createSafeActionClient({
|
|
21
21
|
},
|
22
22
|
});
|
23
23
|
export const authenticatedActionClient = actionClient.use(async ({ next }) => {
|
24
|
-
const session = await getServerSession(authOptions);
|
24
|
+
const session = await getServerSession(authOptions());
|
25
25
|
if (!session?.user) {
|
26
26
|
throw new AuthenticationError("Not authenticated");
|
27
27
|
}
|
package/dist/actions/auth.d.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export declare const auth: () => Promise<
|
1
|
+
export declare const auth: () => Promise<unknown>;
|
package/dist/actions/auth.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
import type { NextAuthOptions } from "next-auth";
|
2
1
|
export declare function signIn({ userName, password }: {
|
3
2
|
userName: string;
|
4
3
|
password: string;
|
5
4
|
}): Promise<import("../modules/account/types").Session>;
|
6
|
-
export declare const authOptions:
|
5
|
+
export declare const authOptions: () => any;
|
@@ -2,17 +2,17 @@ import CredentialsProvider from "next-auth/providers/credentials";
|
|
2
2
|
import { cookies } from "next/headers";
|
3
3
|
import { getAppcondaClient } from "../getAppcondaClient";
|
4
4
|
import { Account } from "../modules/account/service";
|
5
|
-
|
5
|
+
import { getEnv } from "../lib/env";
|
6
6
|
import { getSDKForCurrentUser } from "../getSDKForCurrentUser";
|
7
7
|
import { Query } from "../query";
|
8
|
-
const getEnv = () => {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
};
|
8
|
+
/* const getEnv = () => {
|
9
|
+
return {
|
10
|
+
APPCONDA_ENDPOINT: 'process.env.APPCONDA_ENDPOINT',
|
11
|
+
APPCONDA_CLIENT_ENDPOINT: 'process.env.APPCONDA_CLIENT_ENDPOINT',
|
12
|
+
_SERVICE_TOKEN: 'process.env._SERVICE_TOKEN',
|
13
|
+
ENTERPRISE_LICENSE_KEY: 'process.env.ENTERPRISE_LICENSE_KEY',
|
14
|
+
};
|
15
|
+
}; */
|
16
16
|
export async function signIn({ userName, password }) {
|
17
17
|
const adminClient = await getAppcondaClient();
|
18
18
|
const account = new Account(adminClient);
|
@@ -26,200 +26,208 @@ export async function signIn({ userName, password }) {
|
|
26
26
|
});
|
27
27
|
return session;
|
28
28
|
}
|
29
|
-
export const authOptions = {
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
29
|
+
export const authOptions = (() => {
|
30
|
+
let options = null;
|
31
|
+
return () => {
|
32
|
+
if (options == null) {
|
33
|
+
options = {
|
34
|
+
providers: [
|
35
|
+
CredentialsProvider({
|
36
|
+
id: "credentials",
|
37
|
+
// The name to display on the sign in form (e.g. "Sign in with...")
|
38
|
+
name: "Credentials",
|
39
|
+
// The credentials is used to generate a suitable form on the sign in page.
|
40
|
+
// You can specify whatever fields you are expecting to be submitted.
|
41
|
+
// e.g. domain, username, password, 2FA token, etc.
|
42
|
+
// You can pass any HTML attribute to the <input> tag through the object.
|
43
|
+
credentials: {
|
44
|
+
email: {
|
45
|
+
label: "Email Address",
|
46
|
+
type: "email",
|
47
|
+
placeholder: "Your email address",
|
48
|
+
},
|
49
|
+
password: {
|
50
|
+
label: "Password",
|
51
|
+
type: "password",
|
52
|
+
placeholder: "Your password",
|
53
|
+
},
|
54
|
+
totpCode: { label: "Two-factor Code", type: "input", placeholder: "Code from authenticator app" },
|
55
|
+
backupCode: { label: "Backup Code", type: "input", placeholder: "Two-factor backup code" },
|
56
|
+
},
|
57
|
+
async authorize(credentials, _req) {
|
58
|
+
let user;
|
59
|
+
const appcondaSession = await signIn({ userName: credentials?.email, password: credentials?.password });
|
60
|
+
console.log(credentials);
|
61
|
+
/* try {
|
62
|
+
user = await prisma.user.findUnique({
|
63
|
+
where: {
|
64
|
+
email: credentials?.email,
|
65
|
+
},
|
66
|
+
});
|
67
|
+
} catch (e) {
|
68
|
+
console.error(e);
|
69
|
+
throw Error("Internal server error. Please try again later");
|
70
|
+
}
|
71
|
+
if (!user || !credentials) {
|
72
|
+
throw new Error("Invalid credentials");
|
73
|
+
}
|
74
|
+
if (!user.password) {
|
75
|
+
throw new Error("Invalid credentials");
|
76
|
+
}
|
77
|
+
|
78
|
+
const isValid = await verifyPassword(credentials.password, user.password);
|
79
|
+
|
80
|
+
if (!isValid) {
|
81
|
+
throw new Error("Invalid credentials");
|
82
|
+
}
|
83
|
+
|
84
|
+
if (user.twoFactorEnabled && credentials.backupCode) {
|
85
|
+
if (!ENCRYPTION_KEY) {
|
86
|
+
console.error("Missing encryption key; cannot proceed with backup code login.");
|
87
|
+
throw new Error("Internal Server Error");
|
88
|
+
}
|
89
|
+
|
90
|
+
if (!user.backupCodes) throw new Error("No backup codes found");
|
91
|
+
|
92
|
+
const backupCodes = JSON.parse(symmetricDecrypt(user.backupCodes, ENCRYPTION_KEY));
|
93
|
+
|
94
|
+
// check if user-supplied code matches one
|
95
|
+
const index = backupCodes.indexOf(credentials.backupCode.replaceAll("-", ""));
|
96
|
+
if (index === -1) throw new Error("Invalid backup code");
|
97
|
+
|
98
|
+
// delete verified backup code and re-encrypt remaining
|
99
|
+
backupCodes[index] = null;
|
100
|
+
await prisma.user.update({
|
101
|
+
where: {
|
102
|
+
id: user.id,
|
103
|
+
},
|
104
|
+
data: {
|
105
|
+
backupCodes: symmetricEncrypt(JSON.stringify(backupCodes), ENCRYPTION_KEY),
|
106
|
+
},
|
107
|
+
});
|
108
|
+
} else if (user.twoFactorEnabled) {
|
109
|
+
if (!credentials.totpCode) {
|
110
|
+
throw new Error("second factor required");
|
111
|
+
}
|
112
|
+
|
113
|
+
if (!user.twoFactorSecret) {
|
114
|
+
throw new Error("Internal Server Error");
|
115
|
+
}
|
116
|
+
|
117
|
+
if (!ENCRYPTION_KEY) {
|
118
|
+
throw new Error("Internal Server Error");
|
119
|
+
}
|
120
|
+
|
121
|
+
const secret = symmetricDecrypt(user.twoFactorSecret, ENCRYPTION_KEY);
|
122
|
+
if (secret.length !== 32) {
|
123
|
+
throw new Error("Internal Server Error");
|
124
|
+
}
|
125
|
+
|
126
|
+
const isValidToken = (await import("./totp")).totpAuthenticatorCheck(credentials.totpCode, secret);
|
127
|
+
if (!isValidToken) {
|
128
|
+
throw new Error("Invalid second factor code");
|
129
|
+
}
|
130
|
+
} */
|
131
|
+
console.log("asafdf");
|
132
|
+
return {
|
133
|
+
id: appcondaSession.userId,
|
134
|
+
email: appcondaSession.providerUid,
|
135
|
+
emailVerified: true,
|
136
|
+
imageUrl: "",
|
137
|
+
};
|
138
|
+
},
|
139
|
+
}),
|
140
|
+
CredentialsProvider({
|
141
|
+
id: "token",
|
142
|
+
// The name to display on the sign in form (e.g. "Sign in with...")
|
143
|
+
name: "Token",
|
144
|
+
// The credentials is used to generate a suitable form on the sign in page.
|
145
|
+
// You can specify whatever fields you are expecting to be submitted.
|
146
|
+
// e.g. domain, username, password, 2FA token, etc.
|
147
|
+
// You can pass any HTML attribute to the <input> tag through the object.
|
148
|
+
credentials: {
|
149
|
+
token: {
|
150
|
+
label: "Verification Token",
|
151
|
+
type: "string",
|
152
|
+
},
|
153
|
+
},
|
154
|
+
async authorize(credentials, _req) {
|
155
|
+
let user;
|
156
|
+
/* try {
|
157
|
+
if (!credentials?.token) {
|
158
|
+
throw new Error("Token not found");
|
159
|
+
}
|
160
|
+
const { id } = await verifyToken(credentials?.token);
|
161
|
+
user = await prisma.user.findUnique({
|
162
|
+
where: {
|
163
|
+
id: id,
|
164
|
+
},
|
165
|
+
});
|
166
|
+
} catch (e) {
|
167
|
+
console.error(e);
|
168
|
+
throw new Error("Either a user does not match the provided token or the token is invalid");
|
169
|
+
}
|
170
|
+
|
171
|
+
if (!user) {
|
172
|
+
throw new Error("Either a user does not match the provided token or the token is invalid");
|
173
|
+
}
|
174
|
+
|
175
|
+
if (user.emailVerified) {
|
176
|
+
throw new Error("Email already verified");
|
177
|
+
}
|
178
|
+
|
179
|
+
user = await updateUser(user.id, { emailVerified: new Date() }); */
|
180
|
+
return user || null;
|
181
|
+
},
|
182
|
+
}),
|
183
|
+
// Conditionally add enterprise SSO providers
|
184
|
+
...(getEnv().ENTERPRISE_LICENSE_KEY ? [] : []),
|
185
|
+
],
|
186
|
+
callbacks: {
|
187
|
+
async jwt({ token }) {
|
188
|
+
const { users } = await getSDKForCurrentUser();
|
189
|
+
const userList = await users.list([Query.equal("email", token.email)]);
|
190
|
+
const user = userList.users[0] ?? {};
|
191
|
+
/* const existingUser = await getUserByEmail(token?.email!);
|
192
|
+
|
193
|
+
if (!existingUser) {
|
194
|
+
return token;
|
195
|
+
} */
|
196
|
+
return {
|
197
|
+
...token,
|
198
|
+
//@ts-ignore
|
199
|
+
profile: { id: user.$id, ...user },
|
200
|
+
};
|
201
|
+
},
|
202
|
+
async session({ session, token }) {
|
203
|
+
//@ts-ignore
|
204
|
+
session.user.id = token?.id;
|
205
|
+
//@ts-ignore
|
206
|
+
session.user = token.profile;
|
207
|
+
return session;
|
208
|
+
},
|
209
|
+
//@ts-ignore
|
210
|
+
async signIn({ user, account }) {
|
211
|
+
/* if (account?.provider === "credentials" || account?.provider === "token") {
|
212
|
+
// check if user's email is verified or not
|
213
|
+
if (!user.emailVerified && !EMAIL_VERIFICATION_DISABLED) {
|
214
|
+
throw new Error("Email Verification is Pending");
|
215
|
+
}
|
216
|
+
return true;
|
217
|
+
}
|
218
|
+
if (ENTERPRISE_LICENSE_KEY) {
|
219
|
+
return handleSSOCallback({ user, account });
|
220
|
+
} */
|
221
|
+
return true;
|
222
|
+
},
|
49
223
|
},
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
let user;
|
55
|
-
const appcondaSession = await signIn({ userName: credentials?.email, password: credentials?.password });
|
56
|
-
console.log(credentials);
|
57
|
-
/* try {
|
58
|
-
user = await prisma.user.findUnique({
|
59
|
-
where: {
|
60
|
-
email: credentials?.email,
|
61
|
-
},
|
62
|
-
});
|
63
|
-
} catch (e) {
|
64
|
-
console.error(e);
|
65
|
-
throw Error("Internal server error. Please try again later");
|
66
|
-
}
|
67
|
-
if (!user || !credentials) {
|
68
|
-
throw new Error("Invalid credentials");
|
69
|
-
}
|
70
|
-
if (!user.password) {
|
71
|
-
throw new Error("Invalid credentials");
|
72
|
-
}
|
73
|
-
|
74
|
-
const isValid = await verifyPassword(credentials.password, user.password);
|
75
|
-
|
76
|
-
if (!isValid) {
|
77
|
-
throw new Error("Invalid credentials");
|
78
|
-
}
|
79
|
-
|
80
|
-
if (user.twoFactorEnabled && credentials.backupCode) {
|
81
|
-
if (!ENCRYPTION_KEY) {
|
82
|
-
console.error("Missing encryption key; cannot proceed with backup code login.");
|
83
|
-
throw new Error("Internal Server Error");
|
84
|
-
}
|
85
|
-
|
86
|
-
if (!user.backupCodes) throw new Error("No backup codes found");
|
87
|
-
|
88
|
-
const backupCodes = JSON.parse(symmetricDecrypt(user.backupCodes, ENCRYPTION_KEY));
|
89
|
-
|
90
|
-
// check if user-supplied code matches one
|
91
|
-
const index = backupCodes.indexOf(credentials.backupCode.replaceAll("-", ""));
|
92
|
-
if (index === -1) throw new Error("Invalid backup code");
|
93
|
-
|
94
|
-
// delete verified backup code and re-encrypt remaining
|
95
|
-
backupCodes[index] = null;
|
96
|
-
await prisma.user.update({
|
97
|
-
where: {
|
98
|
-
id: user.id,
|
99
|
-
},
|
100
|
-
data: {
|
101
|
-
backupCodes: symmetricEncrypt(JSON.stringify(backupCodes), ENCRYPTION_KEY),
|
102
|
-
},
|
103
|
-
});
|
104
|
-
} else if (user.twoFactorEnabled) {
|
105
|
-
if (!credentials.totpCode) {
|
106
|
-
throw new Error("second factor required");
|
107
|
-
}
|
108
|
-
|
109
|
-
if (!user.twoFactorSecret) {
|
110
|
-
throw new Error("Internal Server Error");
|
111
|
-
}
|
112
|
-
|
113
|
-
if (!ENCRYPTION_KEY) {
|
114
|
-
throw new Error("Internal Server Error");
|
115
|
-
}
|
116
|
-
|
117
|
-
const secret = symmetricDecrypt(user.twoFactorSecret, ENCRYPTION_KEY);
|
118
|
-
if (secret.length !== 32) {
|
119
|
-
throw new Error("Internal Server Error");
|
120
|
-
}
|
121
|
-
|
122
|
-
const isValidToken = (await import("./totp")).totpAuthenticatorCheck(credentials.totpCode, secret);
|
123
|
-
if (!isValidToken) {
|
124
|
-
throw new Error("Invalid second factor code");
|
125
|
-
}
|
126
|
-
} */
|
127
|
-
console.log("asafdf");
|
128
|
-
return {
|
129
|
-
id: appcondaSession.userId,
|
130
|
-
email: appcondaSession.providerUid,
|
131
|
-
emailVerified: true,
|
132
|
-
imageUrl: "",
|
133
|
-
};
|
134
|
-
},
|
135
|
-
}),
|
136
|
-
CredentialsProvider({
|
137
|
-
id: "token",
|
138
|
-
// The name to display on the sign in form (e.g. "Sign in with...")
|
139
|
-
name: "Token",
|
140
|
-
// The credentials is used to generate a suitable form on the sign in page.
|
141
|
-
// You can specify whatever fields you are expecting to be submitted.
|
142
|
-
// e.g. domain, username, password, 2FA token, etc.
|
143
|
-
// You can pass any HTML attribute to the <input> tag through the object.
|
144
|
-
credentials: {
|
145
|
-
token: {
|
146
|
-
label: "Verification Token",
|
147
|
-
type: "string",
|
224
|
+
pages: {
|
225
|
+
signIn: "/auth/login",
|
226
|
+
signOut: "/auth/logout",
|
227
|
+
error: "/auth/login", // Error code passed in query string as ?error=
|
148
228
|
},
|
149
|
-
},
|
150
|
-
async authorize(credentials, _req) {
|
151
|
-
let user;
|
152
|
-
/* try {
|
153
|
-
if (!credentials?.token) {
|
154
|
-
throw new Error("Token not found");
|
155
|
-
}
|
156
|
-
const { id } = await verifyToken(credentials?.token);
|
157
|
-
user = await prisma.user.findUnique({
|
158
|
-
where: {
|
159
|
-
id: id,
|
160
|
-
},
|
161
|
-
});
|
162
|
-
} catch (e) {
|
163
|
-
console.error(e);
|
164
|
-
throw new Error("Either a user does not match the provided token or the token is invalid");
|
165
|
-
}
|
166
|
-
|
167
|
-
if (!user) {
|
168
|
-
throw new Error("Either a user does not match the provided token or the token is invalid");
|
169
|
-
}
|
170
|
-
|
171
|
-
if (user.emailVerified) {
|
172
|
-
throw new Error("Email already verified");
|
173
|
-
}
|
174
|
-
|
175
|
-
user = await updateUser(user.id, { emailVerified: new Date() }); */
|
176
|
-
return user || null;
|
177
|
-
},
|
178
|
-
}),
|
179
|
-
// Conditionally add enterprise SSO providers
|
180
|
-
...(getEnv().ENTERPRISE_LICENSE_KEY ? [] : []),
|
181
|
-
],
|
182
|
-
callbacks: {
|
183
|
-
async jwt({ token }) {
|
184
|
-
const { users } = await getSDKForCurrentUser();
|
185
|
-
const userList = await users.list([Query.equal("email", token.email)]);
|
186
|
-
const user = userList.users[0] ?? {};
|
187
|
-
/* const existingUser = await getUserByEmail(token?.email!);
|
188
|
-
|
189
|
-
if (!existingUser) {
|
190
|
-
return token;
|
191
|
-
} */
|
192
|
-
return {
|
193
|
-
...token,
|
194
|
-
//@ts-ignore
|
195
|
-
profile: { id: user.$id, ...user },
|
196
229
|
};
|
197
|
-
}
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
//@ts-ignore
|
202
|
-
session.user = token.profile;
|
203
|
-
return session;
|
204
|
-
},
|
205
|
-
//@ts-ignore
|
206
|
-
async signIn({ user, account }) {
|
207
|
-
/* if (account?.provider === "credentials" || account?.provider === "token") {
|
208
|
-
// check if user's email is verified or not
|
209
|
-
if (!user.emailVerified && !EMAIL_VERIFICATION_DISABLED) {
|
210
|
-
throw new Error("Email Verification is Pending");
|
211
|
-
}
|
212
|
-
return true;
|
213
|
-
}
|
214
|
-
if (ENTERPRISE_LICENSE_KEY) {
|
215
|
-
return handleSSOCallback({ user, account });
|
216
|
-
} */
|
217
|
-
return true;
|
218
|
-
},
|
219
|
-
},
|
220
|
-
pages: {
|
221
|
-
signIn: "/auth/login",
|
222
|
-
signOut: "/auth/logout",
|
223
|
-
error: "/auth/login", // Error code passed in query string as ?error=
|
224
|
-
},
|
225
|
-
};
|
230
|
+
}
|
231
|
+
return options;
|
232
|
+
};
|
233
|
+
})();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
/**
|
2
|
+
*
|
3
|
+
* @param text Value to be encrypted
|
4
|
+
* @param key Key used to encrypt value must be 32 bytes for AES256 encryption algorithm
|
5
|
+
*
|
6
|
+
* @returns Encrypted value using key
|
7
|
+
*/
|
8
|
+
export declare const symmetricEncrypt: (text: string, key: string) => string;
|
9
|
+
/**
|
10
|
+
*
|
11
|
+
* @param text Value to decrypt
|
12
|
+
* @param key Key used to decrypt value must be 32 bytes for AES256 encryption algorithm
|
13
|
+
*/
|
14
|
+
export declare const symmetricDecrypt: (text: string, key: string) => string;
|
15
|
+
export declare const getHash: (key: string) => string;
|
16
|
+
export declare const encryptAES128: (encryptionKey: string, data: string) => string;
|
17
|
+
export declare const decryptAES128: (encryptionKey: string, data: string) => string;
|
18
|
+
export declare const generateLocalSignedUrl: (fileName: string, environmentId: string, fileType: string) => {
|
19
|
+
signature: string;
|
20
|
+
uuid: string;
|
21
|
+
timestamp: number;
|
22
|
+
};
|
23
|
+
export declare const validateLocalSignedUrl: (uuid: string, fileName: string, environmentId: string, fileType: string, timestamp: number, signature: string, secret: string) => boolean;
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import crypto from "crypto";
|
2
|
+
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from "crypto";
|
3
|
+
import { getEnv } from "./env";
|
4
|
+
const ALGORITHM = "aes256";
|
5
|
+
const INPUT_ENCODING = "utf8";
|
6
|
+
const OUTPUT_ENCODING = "hex";
|
7
|
+
const BUFFER_ENCODING = getEnv().ENCRYPTION_KEY.length === 32 ? "latin1" : "hex";
|
8
|
+
const IV_LENGTH = 16; // AES blocksize
|
9
|
+
/**
|
10
|
+
*
|
11
|
+
* @param text Value to be encrypted
|
12
|
+
* @param key Key used to encrypt value must be 32 bytes for AES256 encryption algorithm
|
13
|
+
*
|
14
|
+
* @returns Encrypted value using key
|
15
|
+
*/
|
16
|
+
export const symmetricEncrypt = (text, key) => {
|
17
|
+
const _key = Buffer.from(key, BUFFER_ENCODING);
|
18
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
19
|
+
// @ts-ignore -- the package needs to be built
|
20
|
+
const cipher = crypto.createCipheriv(ALGORITHM, _key, iv);
|
21
|
+
let ciphered = cipher.update(text, INPUT_ENCODING, OUTPUT_ENCODING);
|
22
|
+
ciphered += cipher.final(OUTPUT_ENCODING);
|
23
|
+
const ciphertext = iv.toString(OUTPUT_ENCODING) + ":" + ciphered;
|
24
|
+
return ciphertext;
|
25
|
+
};
|
26
|
+
/**
|
27
|
+
*
|
28
|
+
* @param text Value to decrypt
|
29
|
+
* @param key Key used to decrypt value must be 32 bytes for AES256 encryption algorithm
|
30
|
+
*/
|
31
|
+
export const symmetricDecrypt = (text, key) => {
|
32
|
+
const _key = Buffer.from(key, BUFFER_ENCODING);
|
33
|
+
const components = text.split(":");
|
34
|
+
const iv_from_ciphertext = Buffer.from(components.shift() || "", OUTPUT_ENCODING);
|
35
|
+
// @ts-ignore -- the package needs to be built
|
36
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, _key, iv_from_ciphertext);
|
37
|
+
let deciphered = decipher.update(components.join(":"), OUTPUT_ENCODING, INPUT_ENCODING);
|
38
|
+
deciphered += decipher.final(INPUT_ENCODING);
|
39
|
+
return deciphered;
|
40
|
+
};
|
41
|
+
export const getHash = (key) => createHash("sha256").update(key).digest("hex");
|
42
|
+
// create an aes128 encryption function
|
43
|
+
export const encryptAES128 = (encryptionKey, data) => {
|
44
|
+
// @ts-ignore -- the package needs to be built
|
45
|
+
const cipher = createCipheriv("aes-128-ecb", Buffer.from(encryptionKey, "base64"), "");
|
46
|
+
let encrypted = cipher.update(data, "utf-8", "hex");
|
47
|
+
encrypted += cipher.final("hex");
|
48
|
+
return encrypted;
|
49
|
+
};
|
50
|
+
// create an aes128 decryption function
|
51
|
+
export const decryptAES128 = (encryptionKey, data) => {
|
52
|
+
// @ts-ignore -- the package needs to be built
|
53
|
+
const cipher = createDecipheriv("aes-128-ecb", Buffer.from(encryptionKey, "base64"), "");
|
54
|
+
let decrypted = cipher.update(data, "hex", "utf-8");
|
55
|
+
decrypted += cipher.final("utf-8");
|
56
|
+
return decrypted;
|
57
|
+
};
|
58
|
+
export const generateLocalSignedUrl = (fileName, environmentId, fileType) => {
|
59
|
+
const uuid = randomBytes(16).toString("hex");
|
60
|
+
const timestamp = Date.now();
|
61
|
+
const data = `${uuid}:${fileName}:${environmentId}:${fileType}:${timestamp}`;
|
62
|
+
const signature = createHmac("sha256", getEnv().ENCRYPTION_KEY).update(data).digest("hex");
|
63
|
+
return { signature, uuid, timestamp };
|
64
|
+
};
|
65
|
+
export const validateLocalSignedUrl = (uuid, fileName, environmentId, fileType, timestamp, signature, secret) => {
|
66
|
+
const data = `${uuid}:${fileName}:${environmentId}:${fileType}:${timestamp}`;
|
67
|
+
const expectedSignature = createHmac("sha256", secret).update(data).digest("hex");
|
68
|
+
if (expectedSignature !== signature) {
|
69
|
+
return false;
|
70
|
+
}
|
71
|
+
// valid for 5 minutes
|
72
|
+
if (Date.now() - timestamp > 1000 * 60 * 5) {
|
73
|
+
return false;
|
74
|
+
}
|
75
|
+
return true;
|
76
|
+
};
|
package/dist/lib/env.js
CHANGED
@@ -14,6 +14,8 @@ export const getEnv = (() => {
|
|
14
14
|
APPCONDA_CLIENT_ENDPOINT: z.string(),
|
15
15
|
_SERVICE_TOKEN: z.string(),
|
16
16
|
ENTERPRISE_LICENSE_KEY: z.string(),
|
17
|
+
NEXTAUTH_SECRET: z.string().min(1),
|
18
|
+
ENCRYPTION_KEY: z.string().length(64).or(z.string().length(32)),
|
17
19
|
/* AI_AZURE_LLM_API_KEY: z.string().optional(),
|
18
20
|
AI_AZURE_EMBEDDINGS_DEPLOYMENT_ID: z.string().optional(),
|
19
21
|
AI_AZURE_LLM_DEPLOYMENT_ID: z.string().optional(),
|
@@ -33,7 +35,7 @@ export const getEnv = (() => {
|
|
33
35
|
E2E_TESTING: z.enum(["1", "0"]).optional(),
|
34
36
|
EMAIL_AUTH_DISABLED: z.enum(["1", "0"]).optional(),
|
35
37
|
EMAIL_VERIFICATION_DISABLED: z.enum(["1", "0"]).optional(),
|
36
|
-
|
38
|
+
|
37
39
|
ENTERPRISE_LICENSE_KEY: z.string().optional(),
|
38
40
|
FORMBRICKS_ENCRYPTION_KEY: z.string().length(24).or(z.string().length(0)).optional(),
|
39
41
|
GITHUB_ID: z.string().optional(),
|
@@ -55,7 +57,7 @@ export const getEnv = (() => {
|
|
55
57
|
INTERCOM_SECRET_KEY: z.string().optional(),
|
56
58
|
IS_FORMBRICKS_CLOUD: z.enum(["1", "0"]).optional(),
|
57
59
|
MAIL_FROM: z.string().email().optional(),
|
58
|
-
|
60
|
+
|
59
61
|
NOTION_OAUTH_CLIENT_ID: z.string().optional(),
|
60
62
|
NOTION_OAUTH_CLIENT_SECRET: z.string().optional(),
|
61
63
|
OIDC_CLIENT_ID: z.string().optional(),
|
@@ -119,6 +121,8 @@ export const getEnv = (() => {
|
|
119
121
|
APPCONDA_CLIENT_ENDPOINT: process.env.APPCONDA_CLIENT_ENDPOINT,
|
120
122
|
_SERVICE_TOKEN: process.env._SERVICE_TOKEN,
|
121
123
|
ENTERPRISE_LICENSE_KEY: process.env.ENTERPRISE_LICENSE_KEY,
|
124
|
+
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
125
|
+
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
|
122
126
|
},
|
123
127
|
});
|
124
128
|
console.log(env);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./jwt";
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./jwt";
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { JwtPayload } from "jsonwebtoken";
|
2
|
+
export declare const createToken: (userId: string, userEmail: string, options?: {}) => string;
|
3
|
+
export declare const createTokenForLinkSurvey: (surveyId: string, userEmail: string) => string;
|
4
|
+
export declare const createEmailToken: (email: string) => string;
|
5
|
+
export declare const getEmailFromEmailToken: (token: string) => string;
|
6
|
+
export declare const createInviteToken: (inviteId: string, email: string, options?: {}) => string;
|
7
|
+
export declare const verifyTokenForLinkSurvey: (token: string, surveyId: string) => string | null;
|
8
|
+
export declare const verifyToken: (token: string) => Promise<JwtPayload>;
|
9
|
+
export declare const verifyInviteToken: (token: string) => {
|
10
|
+
inviteId: string;
|
11
|
+
email: string;
|
12
|
+
};
|