faire-auth 1.0.3 → 1.1.0
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/{adapter-factory-lKIOf2OR.mjs → adapter-factory-qPVW8x9-.mjs} +34 -36
- package/dist/{adapter-factory-D1I7joki.cjs → adapter-factory-u8r3oTez.cjs} +66 -68
- package/dist/adapters/drizzle-adapter/index.cjs +34 -40
- package/dist/adapters/drizzle-adapter/index.d.cts +4 -9
- package/dist/adapters/drizzle-adapter/index.d.mts +4 -9
- package/dist/adapters/drizzle-adapter/index.mjs +20 -26
- package/dist/adapters/index.cjs +11 -16
- package/dist/adapters/index.d.cts +11 -37
- package/dist/adapters/index.d.mts +11 -37
- package/dist/adapters/index.mjs +4 -13
- package/dist/adapters/kysely-adapter/index.cjs +270 -11
- package/dist/adapters/kysely-adapter/index.d.cts +9 -19
- package/dist/adapters/kysely-adapter/index.d.mts +9 -19
- package/dist/adapters/kysely-adapter/index.mjs +267 -9
- package/dist/adapters/memory-adapter/index.cjs +146 -9
- package/dist/adapters/memory-adapter/index.d.cts +4 -10
- package/dist/adapters/memory-adapter/index.d.mts +4 -10
- package/dist/adapters/memory-adapter/index.mjs +145 -9
- package/dist/adapters/mongodb-adapter/index.cjs +13 -21
- package/dist/adapters/mongodb-adapter/index.d.cts +3 -8
- package/dist/adapters/mongodb-adapter/index.d.mts +4 -9
- package/dist/adapters/mongodb-adapter/index.mjs +12 -20
- package/dist/adapters/prisma-adapter/index.cjs +12 -20
- package/dist/adapters/prisma-adapter/index.d.cts +4 -8
- package/dist/adapters/prisma-adapter/index.d.mts +4 -8
- package/dist/adapters/prisma-adapter/index.mjs +5 -13
- package/dist/adapters/test.cjs +86 -69
- package/dist/adapters/test.d.cts +7 -11
- package/dist/adapters/test.d.mts +7 -11
- package/dist/adapters/test.mjs +83 -66
- package/dist/api/index.cjs +3 -20
- package/dist/api/index.d.cts +2 -3
- package/dist/api/index.d.mts +2 -3
- package/dist/api/index.mjs +1 -20
- package/dist/api/middleware/index.cjs +4 -11
- package/dist/api/middleware/index.d.cts +3 -9
- package/dist/api/middleware/index.d.mts +3 -9
- package/dist/api/middleware/index.mjs +3 -11
- package/dist/{api-B4RDwBty.mjs → api-0f-F-Fi2.mjs} +292 -189
- package/dist/{api-CaRC1Gio.cjs → api-LjfH6hQi.cjs} +557 -452
- package/dist/{test-utils/bun-sqlite-dialect-BZBvHchL.cjs → bun-sqlite-dialect-BHkarnxN.cjs} +5 -9
- package/dist/{test-utils/bun-sqlite-dialect-BSWhhKNs.mjs → bun-sqlite-dialect-BiZBuEF6.mjs} +6 -9
- package/dist/client/index.cjs +8 -15
- package/dist/client/index.d.cts +9 -14
- package/dist/client/index.d.mts +9 -14
- package/dist/client/index.mjs +7 -15
- package/dist/client/lynx/index.cjs +10 -18
- package/dist/client/lynx/index.d.cts +402 -71
- package/dist/client/lynx/index.d.mts +402 -71
- package/dist/client/lynx/index.mjs +5 -13
- package/dist/client/plugins/index.cjs +3 -5
- package/dist/client/plugins/index.d.cts +5 -10
- package/dist/client/plugins/index.d.mts +5 -10
- package/dist/client/plugins/index.mjs +2 -4
- package/dist/client/react/index.cjs +6 -14
- package/dist/client/react/index.d.cts +402 -71
- package/dist/client/react/index.d.mts +402 -71
- package/dist/client/react/index.mjs +5 -13
- package/dist/client/solid/index.cjs +6 -14
- package/dist/client/solid/index.d.cts +393 -57
- package/dist/client/solid/index.d.mts +393 -57
- package/dist/client/solid/index.mjs +5 -13
- package/dist/client/svelte/index.cjs +5 -10
- package/dist/client/svelte/index.d.cts +400 -67
- package/dist/client/svelte/index.d.mts +400 -67
- package/dist/client/svelte/index.mjs +4 -10
- package/dist/client/vue/index.cjs +6 -14
- package/dist/client/vue/index.d.cts +401 -68
- package/dist/client/vue/index.d.mts +401 -68
- package/dist/client/vue/index.mjs +5 -13
- package/dist/{cookies-BTs1o992.mjs → cookies-D9aY_gJh.mjs} +25 -21
- package/dist/{cookies-BTl72THv.cjs → cookies-DQ52rcOy.cjs} +119 -115
- package/dist/crypto/index.cjs +10 -8
- package/dist/crypto/index.d.cts +5 -13
- package/dist/crypto/index.d.mts +5 -13
- package/dist/crypto/index.mjs +2 -5
- package/dist/{crypto-DH2Aa7jN.mjs → crypto-BH-hD0lJ.mjs} +7 -10
- package/dist/{crypto-DpSCnrTp.cjs → crypto-BITfWVwD.cjs} +24 -27
- package/dist/db/index.cjs +1135 -29
- package/dist/db/index.d.cts +1 -2
- package/dist/db/index.d.mts +1 -2
- package/dist/db/index.mjs +1115 -16
- package/dist/defu-D2h4S3X-.mjs +87 -0
- package/dist/defu-DWS5RPWt.cjs +134 -0
- package/dist/{email-verification-DI_0H4JC.cjs → email-verification-DOPnSgbI.cjs} +65 -71
- package/dist/{email-verification-C_lBu_JY.mjs → email-verification-YSR5n5lb.mjs} +23 -29
- package/dist/{hono-CO-zsqI-.mjs → hono-BAV2wQkK.mjs} +16 -33
- package/dist/hono-SVQuc5Ru.mjs +4 -0
- package/dist/hono-pgK4H_U1.cjs +9 -0
- package/dist/{hono-DnW5crl1.cjs → hono-tyFFJFdS.cjs} +42 -60
- package/dist/index-B86Cy0dE.d.cts +2 -0
- package/dist/index-B86Cy0dE.d.mts +2 -0
- package/dist/index.cjs +115 -150
- package/dist/index.d.cts +4 -5
- package/dist/index.d.mts +4 -5
- package/dist/index.mjs +46 -81
- package/dist/integrations/next-js.cjs +5 -9
- package/dist/integrations/next-js.d.cts +6 -14
- package/dist/integrations/next-js.d.mts +6 -14
- package/dist/integrations/next-js.mjs +4 -9
- package/dist/integrations/node.cjs +16 -14
- package/dist/integrations/node.d.cts +6 -14
- package/dist/integrations/node.d.mts +6 -14
- package/dist/integrations/node.mjs +15 -13
- package/dist/integrations/react-start.cjs +7 -11
- package/dist/integrations/react-start.d.cts +5 -13
- package/dist/integrations/react-start.d.mts +5 -13
- package/dist/integrations/react-start.mjs +6 -11
- package/dist/integrations/solid-start.cjs +3 -5
- package/dist/integrations/solid-start.d.cts +2 -5
- package/dist/integrations/solid-start.d.mts +2 -5
- package/dist/integrations/solid-start.mjs +2 -4
- package/dist/integrations/svelte-kit.cjs +6 -10
- package/dist/integrations/svelte-kit.d.cts +11 -23
- package/dist/integrations/svelte-kit.d.mts +11 -23
- package/dist/integrations/svelte-kit.mjs +5 -10
- package/dist/{ip-BjNYspCH.mjs → ip-6Lv2o42m.mjs} +4 -9
- package/dist/{ip-C17Qdq-G.cjs → ip-DPydExzV.cjs} +21 -27
- package/dist/{json--tl7Grvs.cjs → json-D3aq-nH2.cjs} +9 -13
- package/dist/{json-B1v2qRTQ.mjs → json-DooGMtnJ.mjs} +2 -5
- package/dist/{logged-CyGfp8_H.mjs → logged-B83SzMnJ.mjs} +6 -10
- package/dist/{logged-Dv3st5qc.cjs → logged-BIpCpnVY.cjs} +13 -18
- package/dist/{middleware-CE6OhDkq.mjs → middleware-Cakg4e1D.mjs} +2 -4
- package/dist/{middleware-CWvRH4EW.cjs → middleware-Cd-VkNxB.cjs} +17 -20
- package/dist/{node-sqlite-dialect-w4DAg0PH.mjs → node-sqlite-dialect-BMVOjEMh.mjs} +6 -9
- package/dist/{test-utils/node-sqlite-dialect-BgJqjBOu.cjs → node-sqlite-dialect-D3QPFROH.cjs} +5 -9
- package/dist/oauth2/index.cjs +166 -22
- package/dist/oauth2/index.d.cts +5 -17
- package/dist/oauth2/index.d.mts +5 -17
- package/dist/oauth2/index.mjs +156 -16
- package/dist/{origin-check-DjLTuZQ8.cjs → origin-check-BflGhEEN.cjs} +34 -38
- package/dist/{origin-check-8Gajklvh.mjs → origin-check-CRgs1M7k.mjs} +13 -16
- package/dist/{parser-B25ylT5u.mjs → parser-CqwvpF6l.cjs} +12 -10
- package/dist/{parser-B_wxvaAr.cjs → parser-DcKDjjHO.mjs} +7 -22
- package/dist/plugins/access/index.cjs +38 -4
- package/dist/plugins/access/index.d.cts +36 -2
- package/dist/plugins/access/index.d.mts +36 -2
- package/dist/plugins/access/index.mjs +36 -3
- package/dist/plugins/bearer/index.cjs +52 -7
- package/dist/plugins/bearer/index.d.cts +27 -4
- package/dist/plugins/bearer/index.d.mts +27 -4
- package/dist/plugins/bearer/index.mjs +51 -7
- package/dist/plugins/index.cjs +1385 -842
- package/dist/plugins/index.d.cts +489 -7566
- package/dist/plugins/index.d.mts +489 -7566
- package/dist/plugins/index.mjs +1118 -576
- package/dist/plugins/passkey/index.cjs +551 -15
- package/dist/plugins/passkey/index.d.cts +123 -4
- package/dist/plugins/passkey/index.d.mts +123 -4
- package/dist/plugins/passkey/index.mjs +547 -15
- package/dist/{chunk-BFnQceUY.mjs → rolldown-runtime-BKpuH2hG.mjs} +6 -7
- package/dist/{chunk-fPmz2rbM.cjs → rolldown-runtime-C_Pgydhf.cjs} +22 -24
- package/dist/{schema-DTIwLLmf.mjs → schema-CFCpv0tK.mjs} +3 -6
- package/dist/{schema-CirKflVs.cjs → schema-b-lKhMkG.cjs} +51 -55
- package/dist/{session-CM3rmOYF.mjs → session-BGjJgihS.mjs} +51 -74
- package/dist/{session-Cwrj4GfV.cjs → session-CzZSMmQC.cjs} +197 -219
- package/dist/shared.d.cts +1581 -0
- package/dist/shared.d.mts +1581 -0
- package/dist/social-providers/index.cjs +9 -9
- package/dist/social-providers/index.d.cts +1 -1
- package/dist/social-providers/index.d.mts +1 -1
- package/dist/social-providers/index.mjs +3 -5
- package/dist/social-providers-CjPww-qf.mjs +6 -0
- package/dist/social-providers-D7w_7Ym8.cjs +10 -0
- package/dist/{state-CvSihlVT.cjs → state-2OrNjOO-.cjs} +24 -24
- package/dist/{state-CQghgH9I.mjs → state-Dooeisfq.mjs} +17 -17
- package/dist/{bun-sqlite-dialect-DPgTzc0w.cjs → test-utils/bun-sqlite-dialect-BHkarnxN.cjs} +5 -9
- package/dist/{bun-sqlite-dialect-6NqeV3px.mjs → test-utils/bun-sqlite-dialect-BiZBuEF6.mjs} +6 -9
- package/dist/test-utils/index.cjs +8831 -7941
- package/dist/test-utils/index.d.cts +310 -12440
- package/dist/test-utils/index.d.mts +313 -12440
- package/dist/test-utils/index.mjs +8591 -7701
- package/dist/test-utils/{node-sqlite-dialect-DAQXiobH.mjs → node-sqlite-dialect-BMVOjEMh.mjs} +6 -9
- package/dist/{node-sqlite-dialect-DP11CB0y.cjs → test-utils/node-sqlite-dialect-D3QPFROH.cjs} +5 -9
- package/dist/types/index.d.cts +2 -3
- package/dist/types/index.d.mts +2 -3
- package/dist/types/index.mjs +1 -1
- package/dist/{url-BoUuoIwZ.mjs → url-CJtdrrUu.mjs} +12 -13
- package/dist/{url-Ce7CVaQD.cjs → url-DjCmCUHS.cjs} +42 -44
- package/dist/utils/cookies.cjs +29 -30
- package/dist/utils/cookies.d.cts +21 -25
- package/dist/utils/cookies.d.mts +20 -24
- package/dist/utils/cookies.mjs +2 -7
- package/dist/utils-C7OHyfHa.mjs +4 -0
- package/dist/utils-iX0Vy_UL.cjs +3 -0
- package/package.json +40 -30
- package/dist/access-BZ7-AbTM.cjs +0 -51
- package/dist/access-D0DRqUKC.mjs +0 -39
- package/dist/bearer-DDzrp5M7.cjs +0 -60
- package/dist/bearer-WZcs7gxL.mjs +0 -54
- package/dist/db-4tUsQJXd.mjs +0 -1119
- package/dist/db-ChMbb_U-.cjs +0 -1180
- package/dist/defu-BmOYq4PU.cjs +0 -34
- package/dist/defu-D9ZTXqlo.mjs +0 -28
- package/dist/hono-C14rpOEa.cjs +0 -13
- package/dist/hono-CoKYWnXA.mjs +0 -7
- package/dist/hono-D0VqxVy1.d.mts +0 -18
- package/dist/hono-DOM31WQz.d.cts +0 -18
- package/dist/index-BCTFWP_o.d.mts +0 -992
- package/dist/index-BDhhaE6Y.d.cts +0 -3
- package/dist/index-BVRfQy6L.d.mts +0 -992
- package/dist/index-Baarsq5J.d.mts +0 -33
- package/dist/index-By364WbF.d.cts +0 -992
- package/dist/index-BzWwMgxr.d.cts +0 -992
- package/dist/index-CF9iq55a.d.mts +0 -3
- package/dist/index-CJD2yB-W.d.cts +0 -33
- package/dist/index-CRt_fppq.d.mts +0 -41
- package/dist/index-CdLyn7qP.d.cts +0 -41
- package/dist/index-CsWF3f7U.d.cts +0 -13350
- package/dist/index-CzidsAyA.d.mts +0 -13350
- package/dist/index-DvW-GCcI.d.cts +0 -33
- package/dist/index-f-5OJ9yV.d.cts +0 -13350
- package/dist/index-rFv8Hxld.d.mts +0 -13350
- package/dist/index-ztVZPQdV.d.mts +0 -33
- package/dist/kysely-adapter-CtIdoHa8.mjs +0 -270
- package/dist/kysely-adapter-QZTx077T.cjs +0 -288
- package/dist/memory-adapter-17RZd5df.cjs +0 -154
- package/dist/memory-adapter-Dp4XdCZK.mjs +0 -148
- package/dist/oauth2-5RL4cVNS.cjs +0 -177
- package/dist/oauth2-C56wz-n9.mjs +0 -159
- package/dist/passkey-BTT-v5c-.cjs +0 -512
- package/dist/passkey-CzY_W2us.mjs +0 -505
- package/dist/request-1NL_1lyJ.mjs +0 -58
- package/dist/request-CPNlp-DK.cjs +0 -100
- package/dist/social-providers-B8RK5yqI.mjs +0 -9
- package/dist/social-providers-BmFOIIPk.cjs +0 -13
- package/dist/utils-44aDDP8f.cjs +0 -2
- package/dist/utils-D4vZd1-9.mjs +0 -4
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,36 +1,27 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
6
|
-
import "../
|
|
7
|
-
import { r as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import "
|
|
13
|
-
import "../
|
|
14
|
-
import {
|
|
15
|
-
import { t as
|
|
16
|
-
import "../kysely-adapter-CtIdoHa8.mjs";
|
|
17
|
-
import "../adapter-factory-lKIOf2OR.mjs";
|
|
18
|
-
import { i as toZodSchema } from "../db-4tUsQJXd.mjs";
|
|
19
|
-
import "../memory-adapter-Dp4XdCZK.mjs";
|
|
20
|
-
import { n as parser_default } from "../parser-B25ylT5u.mjs";
|
|
21
|
-
import { n as role, t as createAccessControl } from "../access-D0DRqUKC.mjs";
|
|
22
|
-
import { t as bearer } from "../bearer-WZcs7gxL.mjs";
|
|
23
|
-
import { t as passkey } from "../passkey-CzY_W2us.mjs";
|
|
1
|
+
import { createAccessControl, role } from "./access/index.mjs";
|
|
2
|
+
import { bearer } from "./bearer/index.mjs";
|
|
3
|
+
import { a as requestOnlySessionMiddleware, f as createEndpoint, r as getSessionFromCtx, u as sessionMiddleware } from "../session-BGjJgihS.mjs";
|
|
4
|
+
import { n as originCheck } from "../origin-check-CRgs1M7k.mjs";
|
|
5
|
+
import { n as crypto_exports, r as validatePassword } from "../crypto-BH-hD0lJ.mjs";
|
|
6
|
+
import { n as cookies_exports, o as deleteSessionCookie, y as setSessionCookie } from "../cookies-D9aY_gJh.mjs";
|
|
7
|
+
import { n as createHook, r as createMiddleware } from "../middleware-Cakg4e1D.mjs";
|
|
8
|
+
import { r as getOrigin } from "../url-CJtdrrUu.mjs";
|
|
9
|
+
import { t as parseJSON } from "../parser-DcKDjjHO.mjs";
|
|
10
|
+
import { toZodSchema } from "../db/index.mjs";
|
|
11
|
+
import { i as generateRandomString$2, r as generateId } from "../utils-C7OHyfHa.mjs";
|
|
12
|
+
import { passkey } from "./passkey/index.mjs";
|
|
13
|
+
import { t as mergeSchema } from "../schema-CFCpv0tK.mjs";
|
|
14
|
+
import { t as safeJSONParse } from "../json-DooGMtnJ.mjs";
|
|
15
|
+
import { t as createEmailVerificationToken } from "../email-verification-YSR5n5lb.mjs";
|
|
24
16
|
import { env, logger } from "@faire-auth/core/env";
|
|
25
17
|
import { APIError, BASE_ERROR_CODES, FaireAuthError } from "@faire-auth/core/error";
|
|
26
18
|
import { callbackURLSchema, createRoute, createRoute as createRoute$1, dateOrIsoStringSchema, emailSchema, nameSchema, req, res } from "@faire-auth/core/factory";
|
|
27
19
|
import { Definitions, False, SCHEMAS, True } from "@faire-auth/core/static";
|
|
28
20
|
import { getDate, toSuccess } from "@faire-auth/core/utils";
|
|
29
21
|
import { base64Url, createHMAC, createHash, createOTP } from "@faire-auth/core/datatypes";
|
|
30
|
-
import * as z
|
|
22
|
+
import * as z from "zod";
|
|
31
23
|
import { looseUserSchema } from "@faire-auth/core/db";
|
|
32
|
-
|
|
33
|
-
//#region src/plugins/magic-link/index.ts
|
|
24
|
+
// src/plugins/magic-link/index.ts
|
|
34
25
|
const magicLink = (options) => ({
|
|
35
26
|
id: "magic-link",
|
|
36
27
|
routes: {
|
|
@@ -39,7 +30,7 @@ const magicLink = (options) => ({
|
|
|
39
30
|
method: "post",
|
|
40
31
|
path: "/sign-in/magic-link",
|
|
41
32
|
description: "Sign in with magic link",
|
|
42
|
-
request: req().bdy(z
|
|
33
|
+
request: req().bdy(z.object({
|
|
43
34
|
email: emailSchema.openapi({ description: "Email address to send the magic link" }),
|
|
44
35
|
name: nameSchema,
|
|
45
36
|
callbackURL: callbackURLSchema(true).openapi({ description: "URL to redirect after magic link verification" })
|
|
@@ -73,8 +64,8 @@ const magicLink = (options) => ({
|
|
|
73
64
|
path: "/magic-link/verify",
|
|
74
65
|
description: "Verify magic link",
|
|
75
66
|
middleware: [originCheck((ctx) => ctx.req.query("callbackURL"))],
|
|
76
|
-
request: req().qry(z
|
|
77
|
-
token: z
|
|
67
|
+
request: req().qry(z.object({
|
|
68
|
+
token: z.string().openapi({
|
|
78
69
|
param: {
|
|
79
70
|
in: "query",
|
|
80
71
|
name: "token"
|
|
@@ -101,15 +92,17 @@ const magicLink = (options) => ({
|
|
|
101
92
|
}
|
|
102
93
|
await ctx.get("context").internalAdapter.deleteVerificationValue(tokenValue.id);
|
|
103
94
|
const { email, name } = JSON.parse(tokenValue.value);
|
|
104
|
-
let user = await ctx.get("context").internalAdapter.findUserByEmail(email).then((res
|
|
105
|
-
if (!user)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
95
|
+
let user = await ctx.get("context").internalAdapter.findUserByEmail(email).then((res) => res?.user);
|
|
96
|
+
if (!user) {
|
|
97
|
+
if (!(options.disableSignUp ?? false)) {
|
|
98
|
+
user = await ctx.get("context").internalAdapter.createUser({
|
|
99
|
+
email,
|
|
100
|
+
emailVerified: true,
|
|
101
|
+
...name != null && { name }
|
|
102
|
+
});
|
|
103
|
+
if (user == null) return ctx.redirect(`${toRedirectTo}?error=failed_to_create_user`, 302);
|
|
104
|
+
} else return ctx.redirect(`${toRedirectTo}?error=failed_to_create_user`, 302);
|
|
105
|
+
}
|
|
113
106
|
if (!user.emailVerified) await ctx.get("context").internalAdapter.updateUser(user.id, { emailVerified: true });
|
|
114
107
|
const session = await ctx.get("context").internalAdapter.createSession(user.id);
|
|
115
108
|
if (session == null) return ctx.redirect(`${toRedirectTo}?error=failed_to_create_session`, 302);
|
|
@@ -130,9 +123,7 @@ const magicLink = (options) => ({
|
|
|
130
123
|
max: options.rateLimit?.max ?? 5
|
|
131
124
|
}]
|
|
132
125
|
});
|
|
133
|
-
|
|
134
|
-
//#endregion
|
|
135
|
-
//#region src/plugins/oauth-proxy/index.ts
|
|
126
|
+
// src/plugins/oauth-proxy/index.ts
|
|
136
127
|
/**
|
|
137
128
|
* A proxy plugin, that allows you to proxy OAuth requests.
|
|
138
129
|
* Useful for development and preview deployments where
|
|
@@ -160,9 +151,9 @@ const oAuthProxy = (opts) => {
|
|
|
160
151
|
path: "/oauth-proxy-callback",
|
|
161
152
|
isAction: false,
|
|
162
153
|
middleware: [originCheck((ctx) => ctx.req.query("callbackURL"))],
|
|
163
|
-
request: req().qry(z
|
|
154
|
+
request: req().qry(z.object({
|
|
164
155
|
callbackURL: callbackURLSchema().openapi({ description: "The URL to redirect to after the proxy" }),
|
|
165
|
-
cookies: z
|
|
156
|
+
cookies: z.string().openapi({ description: "The cookies to set after the proxy" })
|
|
166
157
|
})).bld(),
|
|
167
158
|
responses: res().rdr().bld()
|
|
168
159
|
}), (options) => async (ctx) => {
|
|
@@ -220,51 +211,7 @@ const oAuthProxy = (opts) => {
|
|
|
220
211
|
}] }
|
|
221
212
|
};
|
|
222
213
|
};
|
|
223
|
-
|
|
224
|
-
//#endregion
|
|
225
|
-
//#region src/plugins/organization/access/statement.ts
|
|
226
|
-
const defaultStatements = {
|
|
227
|
-
organization: ["update", "delete"],
|
|
228
|
-
member: [
|
|
229
|
-
"create",
|
|
230
|
-
"update",
|
|
231
|
-
"delete"
|
|
232
|
-
],
|
|
233
|
-
invitation: ["create", "cancel"],
|
|
234
|
-
team: [
|
|
235
|
-
"create",
|
|
236
|
-
"update",
|
|
237
|
-
"delete"
|
|
238
|
-
],
|
|
239
|
-
ac: [
|
|
240
|
-
"create",
|
|
241
|
-
"read",
|
|
242
|
-
"update",
|
|
243
|
-
"delete"
|
|
244
|
-
]
|
|
245
|
-
};
|
|
246
|
-
const defaultAc = createAccessControl(defaultStatements);
|
|
247
|
-
const adminAc = defaultAc.newRole({
|
|
248
|
-
organization: ["update"],
|
|
249
|
-
invitation: ["create", "cancel"],
|
|
250
|
-
member: [
|
|
251
|
-
"create",
|
|
252
|
-
"update",
|
|
253
|
-
"delete"
|
|
254
|
-
],
|
|
255
|
-
team: [
|
|
256
|
-
"create",
|
|
257
|
-
"update",
|
|
258
|
-
"delete"
|
|
259
|
-
],
|
|
260
|
-
ac: [
|
|
261
|
-
"create",
|
|
262
|
-
"read",
|
|
263
|
-
"update",
|
|
264
|
-
"delete"
|
|
265
|
-
]
|
|
266
|
-
});
|
|
267
|
-
const ownerAc = defaultAc.newRole({
|
|
214
|
+
const defaultAc = createAccessControl({
|
|
268
215
|
organization: ["update", "delete"],
|
|
269
216
|
member: [
|
|
270
217
|
"create",
|
|
@@ -284,21 +231,56 @@ const ownerAc = defaultAc.newRole({
|
|
|
284
231
|
"delete"
|
|
285
232
|
]
|
|
286
233
|
});
|
|
287
|
-
const memberAc = defaultAc.newRole({
|
|
288
|
-
organization: [],
|
|
289
|
-
member: [],
|
|
290
|
-
invitation: [],
|
|
291
|
-
team: [],
|
|
292
|
-
ac: ["read"]
|
|
293
|
-
});
|
|
294
234
|
const defaultRoles$1 = {
|
|
295
|
-
admin:
|
|
296
|
-
|
|
297
|
-
|
|
235
|
+
admin: defaultAc.newRole({
|
|
236
|
+
organization: ["update"],
|
|
237
|
+
invitation: ["create", "cancel"],
|
|
238
|
+
member: [
|
|
239
|
+
"create",
|
|
240
|
+
"update",
|
|
241
|
+
"delete"
|
|
242
|
+
],
|
|
243
|
+
team: [
|
|
244
|
+
"create",
|
|
245
|
+
"update",
|
|
246
|
+
"delete"
|
|
247
|
+
],
|
|
248
|
+
ac: [
|
|
249
|
+
"create",
|
|
250
|
+
"read",
|
|
251
|
+
"update",
|
|
252
|
+
"delete"
|
|
253
|
+
]
|
|
254
|
+
}),
|
|
255
|
+
owner: defaultAc.newRole({
|
|
256
|
+
organization: ["update", "delete"],
|
|
257
|
+
member: [
|
|
258
|
+
"create",
|
|
259
|
+
"update",
|
|
260
|
+
"delete"
|
|
261
|
+
],
|
|
262
|
+
invitation: ["create", "cancel"],
|
|
263
|
+
team: [
|
|
264
|
+
"create",
|
|
265
|
+
"update",
|
|
266
|
+
"delete"
|
|
267
|
+
],
|
|
268
|
+
ac: [
|
|
269
|
+
"create",
|
|
270
|
+
"read",
|
|
271
|
+
"update",
|
|
272
|
+
"delete"
|
|
273
|
+
]
|
|
274
|
+
}),
|
|
275
|
+
member: defaultAc.newRole({
|
|
276
|
+
organization: [],
|
|
277
|
+
member: [],
|
|
278
|
+
invitation: [],
|
|
279
|
+
team: [],
|
|
280
|
+
ac: ["read"]
|
|
281
|
+
})
|
|
298
282
|
};
|
|
299
|
-
|
|
300
|
-
//#endregion
|
|
301
|
-
//#region src/plugins/organization/adapter.ts
|
|
283
|
+
// src/plugins/organization/adapter.ts
|
|
302
284
|
const getOrgAdapter = (context, options) => {
|
|
303
285
|
const adapter = context.adapter;
|
|
304
286
|
return {
|
|
@@ -312,7 +294,7 @@ const getOrgAdapter = (context, options) => {
|
|
|
312
294
|
});
|
|
313
295
|
},
|
|
314
296
|
createOrganization: async (data) => {
|
|
315
|
-
const organization
|
|
297
|
+
const organization = await adapter.create({
|
|
316
298
|
model: "organization",
|
|
317
299
|
data: {
|
|
318
300
|
...data.organization,
|
|
@@ -320,8 +302,8 @@ const getOrgAdapter = (context, options) => {
|
|
|
320
302
|
}
|
|
321
303
|
});
|
|
322
304
|
return {
|
|
323
|
-
...organization
|
|
324
|
-
...organization
|
|
305
|
+
...organization,
|
|
306
|
+
...organization["metadata"] && typeof organization["metadata"] === "string" && { metadata: JSON.parse(organization["metadata"]) }
|
|
325
307
|
};
|
|
326
308
|
},
|
|
327
309
|
findMemberByEmail: async (data) => {
|
|
@@ -390,7 +372,7 @@ const getOrgAdapter = (context, options) => {
|
|
|
390
372
|
});
|
|
391
373
|
return {
|
|
392
374
|
members: members[0].map((member) => {
|
|
393
|
-
const user = users.find((user
|
|
375
|
+
const user = users.find((user) => user.id === member.userId);
|
|
394
376
|
if (!user) throw new FaireAuthError("Unexpected error: User not found for member");
|
|
395
377
|
return {
|
|
396
378
|
...member,
|
|
@@ -469,14 +451,14 @@ const getOrgAdapter = (context, options) => {
|
|
|
469
451
|
}
|
|
470
452
|
});
|
|
471
453
|
},
|
|
472
|
-
updateMember: async (memberId, role
|
|
454
|
+
updateMember: async (memberId, role) => {
|
|
473
455
|
return await adapter.update({
|
|
474
456
|
model: "member",
|
|
475
457
|
where: [{
|
|
476
458
|
field: "id",
|
|
477
459
|
value: memberId
|
|
478
460
|
}],
|
|
479
|
-
update: { role
|
|
461
|
+
update: { role }
|
|
480
462
|
});
|
|
481
463
|
},
|
|
482
464
|
deleteMember: async (memberId) => {
|
|
@@ -489,7 +471,7 @@ const getOrgAdapter = (context, options) => {
|
|
|
489
471
|
});
|
|
490
472
|
},
|
|
491
473
|
updateOrganization: async (organizationId, data) => {
|
|
492
|
-
const organization
|
|
474
|
+
const organization = await adapter.update({
|
|
493
475
|
model: "organization",
|
|
494
476
|
where: [{
|
|
495
477
|
field: "id",
|
|
@@ -500,10 +482,10 @@ const getOrgAdapter = (context, options) => {
|
|
|
500
482
|
metadata: typeof data["metadata"] === "object" ? JSON.stringify(data["metadata"]) : data["metadata"]
|
|
501
483
|
}
|
|
502
484
|
});
|
|
503
|
-
if (!organization
|
|
485
|
+
if (!organization) return null;
|
|
504
486
|
return {
|
|
505
|
-
...organization
|
|
506
|
-
metadata: organization
|
|
487
|
+
...organization,
|
|
488
|
+
metadata: organization["metadata"] ? parseJSON(organization["metadata"]) : void 0
|
|
507
489
|
};
|
|
508
490
|
},
|
|
509
491
|
deleteOrganization: async (organizationId) => {
|
|
@@ -554,6 +536,9 @@ const getOrgAdapter = (context, options) => {
|
|
|
554
536
|
}]
|
|
555
537
|
});
|
|
556
538
|
},
|
|
539
|
+
/**
|
|
540
|
+
* @requires db
|
|
541
|
+
*/
|
|
557
542
|
findFullOrganization: async ({ organizationId, isSlug, includeTeams, membersLimit }) => {
|
|
558
543
|
const org = await adapter.findOne({
|
|
559
544
|
model: "organization",
|
|
@@ -709,13 +694,13 @@ const getOrgAdapter = (context, options) => {
|
|
|
709
694
|
}]
|
|
710
695
|
});
|
|
711
696
|
},
|
|
712
|
-
createTeamInvitation: async ({ email, role
|
|
697
|
+
createTeamInvitation: async ({ email, role, teamId, organizationId, inviterId, expiresIn = 1728e5 }) => {
|
|
713
698
|
const expiresAt = getDate(expiresIn);
|
|
714
699
|
return await adapter.create({
|
|
715
700
|
model: "invitation",
|
|
716
701
|
data: {
|
|
717
702
|
email,
|
|
718
|
-
role
|
|
703
|
+
role,
|
|
719
704
|
organizationId,
|
|
720
705
|
teamId,
|
|
721
706
|
inviterId,
|
|
@@ -835,7 +820,7 @@ const getOrgAdapter = (context, options) => {
|
|
|
835
820
|
});
|
|
836
821
|
},
|
|
837
822
|
createInvitation: async ({ invitation, user }) => {
|
|
838
|
-
const expiresAt = getDate(options?.invitationExpiresIn ||
|
|
823
|
+
const expiresAt = getDate(options?.invitationExpiresIn || 172800, "sec");
|
|
839
824
|
return await adapter.create({
|
|
840
825
|
model: "invitation",
|
|
841
826
|
data: {
|
|
@@ -908,9 +893,7 @@ const getOrgAdapter = (context, options) => {
|
|
|
908
893
|
}
|
|
909
894
|
};
|
|
910
895
|
};
|
|
911
|
-
|
|
912
|
-
//#endregion
|
|
913
|
-
//#region src/plugins/organization/call.ts
|
|
896
|
+
// src/plugins/organization/call.ts
|
|
914
897
|
/**
|
|
915
898
|
* the orgMiddleware type-asserts and adds org options, roles, and a getSession function.
|
|
916
899
|
*/
|
|
@@ -920,16 +903,14 @@ const orgMiddleware = (orgOptions) => createMiddleware()(async (ctx, next) => {
|
|
|
920
903
|
...defaultRoles$1,
|
|
921
904
|
...orgOptions?.roles
|
|
922
905
|
});
|
|
923
|
-
ctx.set("getSession", async (ctx
|
|
906
|
+
ctx.set("getSession", async (ctx) => await getSessionFromCtx(ctx).then((s) => {
|
|
924
907
|
if (s instanceof Response) return null;
|
|
925
908
|
return s;
|
|
926
909
|
}));
|
|
927
910
|
return await next();
|
|
928
911
|
});
|
|
929
912
|
const orgSessionMiddleware = sessionMiddleware();
|
|
930
|
-
|
|
931
|
-
//#endregion
|
|
932
|
-
//#region src/plugins/organization/error-codes.ts
|
|
913
|
+
// src/plugins/organization/error-codes.ts
|
|
933
914
|
const ORGANIZATION_ERROR_CODES = {
|
|
934
915
|
YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION: "You are not allowed to create a new organization",
|
|
935
916
|
YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS: "You have reached the maximum number of organizations",
|
|
@@ -987,9 +968,7 @@ const ORGANIZATION_ERROR_CODES = {
|
|
|
987
968
|
ROLE_NAME_IS_ALREADY_TAKEN: "That role name is already taken",
|
|
988
969
|
CANNOT_DELETE_A_PRE_DEFINED_ROLE: "Cannot delete a pre-defined role"
|
|
989
970
|
};
|
|
990
|
-
|
|
991
|
-
//#endregion
|
|
992
|
-
//#region src/plugins/organization/permission.ts
|
|
971
|
+
// src/plugins/organization/permission.ts
|
|
993
972
|
const hasPermissionFn = (input, acRoles) => {
|
|
994
973
|
if (!input.permissions && !input.permission) return false;
|
|
995
974
|
const roles = input.role.split(",");
|
|
@@ -997,13 +976,11 @@ const hasPermissionFn = (input, acRoles) => {
|
|
|
997
976
|
const isCreator = roles.includes(creatorRole);
|
|
998
977
|
const allowCreatorsAllPermissions = input.allowCreatorAllPermissions || false;
|
|
999
978
|
if (isCreator && allowCreatorsAllPermissions) return true;
|
|
1000
|
-
for (const role
|
|
979
|
+
for (const role of roles) if ((acRoles[role]?.authorize(input.permissions ?? input.permission))?.success) return true;
|
|
1001
980
|
return false;
|
|
1002
981
|
};
|
|
1003
982
|
let cacheAllRoles = /* @__PURE__ */ new Map();
|
|
1004
|
-
|
|
1005
|
-
//#endregion
|
|
1006
|
-
//#region src/plugins/organization/has-permission.ts
|
|
983
|
+
// src/plugins/organization/has-permission.ts
|
|
1007
984
|
const hasPermission = async (input, ctx) => {
|
|
1008
985
|
let acRoles = { ...input.options.roles || defaultRoles$1 };
|
|
1009
986
|
if (ctx && input.organizationId && input.options.dynamicAccessControl?.enabled && input.options.ac && !input.useMemoryCache) {
|
|
@@ -1014,25 +991,23 @@ const hasPermission = async (input, ctx) => {
|
|
|
1014
991
|
value: input.organizationId
|
|
1015
992
|
}]
|
|
1016
993
|
});
|
|
1017
|
-
for (const { role
|
|
1018
|
-
if (role
|
|
1019
|
-
const result = z
|
|
994
|
+
for (const { role, permission: permissionsString } of roles) {
|
|
995
|
+
if (role in acRoles) continue;
|
|
996
|
+
const result = z.record(z.string(), z.array(z.string())).safeParse(JSON.parse(permissionsString));
|
|
1020
997
|
if (!result.success) {
|
|
1021
|
-
ctx.get("context").logger.error("[hasPermission] Invalid permissions for role " + role
|
|
1022
|
-
throw new APIError("INTERNAL_SERVER_ERROR", { message: "Invalid permissions for role " + role
|
|
998
|
+
ctx.get("context").logger.error("[hasPermission] Invalid permissions for role " + role, { permissions: JSON.parse(permissionsString) });
|
|
999
|
+
throw new APIError("INTERNAL_SERVER_ERROR", { message: "Invalid permissions for role " + role });
|
|
1023
1000
|
}
|
|
1024
|
-
acRoles[role
|
|
1001
|
+
acRoles[role] = input.options.ac.newRole(result.data);
|
|
1025
1002
|
}
|
|
1026
1003
|
}
|
|
1027
1004
|
if (input.useMemoryCache) acRoles = cacheAllRoles.get(input.organizationId) || acRoles;
|
|
1028
1005
|
cacheAllRoles.set(input.organizationId, acRoles);
|
|
1029
1006
|
return hasPermissionFn(input, acRoles);
|
|
1030
1007
|
};
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
const role$1 = z$1.string().openapi({ description: "Role identifier for organization members (e.g., admin, member, owner)" });
|
|
1035
|
-
const invitationStatus = z$1.enum([
|
|
1008
|
+
// src/plugins/organization/schema/base.ts
|
|
1009
|
+
const role$1 = z.string().openapi({ description: "Role identifier for organization members (e.g., admin, member, owner)" });
|
|
1010
|
+
const invitationStatus = z.enum([
|
|
1036
1011
|
"pending",
|
|
1037
1012
|
"accepted",
|
|
1038
1013
|
"rejected",
|
|
@@ -1043,57 +1018,55 @@ const defaultRoles = [
|
|
|
1043
1018
|
"member",
|
|
1044
1019
|
"owner"
|
|
1045
1020
|
];
|
|
1046
|
-
|
|
1047
|
-
const organizationSchema = z
|
|
1048
|
-
id: z
|
|
1049
|
-
name: z
|
|
1050
|
-
slug: z
|
|
1051
|
-
logo: z
|
|
1052
|
-
metadata: z
|
|
1021
|
+
z.union([z.enum(defaultRoles), z.array(z.enum(defaultRoles))]).openapi({ description: "Default role options for organization members" });
|
|
1022
|
+
const organizationSchema = z.looseObject({
|
|
1023
|
+
id: z.string().default(generateId).openapi({ description: "Unique identifier for the organization" }),
|
|
1024
|
+
name: z.string().openapi({ description: "Display name of the organization" }),
|
|
1025
|
+
slug: z.string().openapi({ description: "URL-friendly slug for the organization" }),
|
|
1026
|
+
logo: z.string().nullish().openapi({ description: "URL or path to the organization logo image" }),
|
|
1027
|
+
metadata: z.record(z.string(), z.unknown()).or(z.string().transform((v) => JSON.parse(v))).nullish().openapi({ description: "Additional metadata for the organization as key-value pairs" }),
|
|
1053
1028
|
createdAt: dateOrIsoStringSchema.openapi({ description: "Timestamp when the organization was created" })
|
|
1054
1029
|
});
|
|
1055
|
-
const organizationRoleSchema = z
|
|
1056
|
-
id: z
|
|
1057
|
-
organizationId: z
|
|
1058
|
-
role: z
|
|
1059
|
-
permission: z
|
|
1030
|
+
const organizationRoleSchema = z.looseObject({
|
|
1031
|
+
id: z.string().default(generateId),
|
|
1032
|
+
organizationId: z.string(),
|
|
1033
|
+
role: z.string(),
|
|
1034
|
+
permission: z.record(z.string(), z.array(z.string())),
|
|
1060
1035
|
createdAt: dateOrIsoStringSchema.default(() => /* @__PURE__ */ new Date()),
|
|
1061
1036
|
updatedAt: dateOrIsoStringSchema.optional()
|
|
1062
1037
|
});
|
|
1063
|
-
const memberSchema = z
|
|
1064
|
-
id: z
|
|
1065
|
-
organizationId: z
|
|
1066
|
-
userId: z
|
|
1038
|
+
const memberSchema = z.looseObject({
|
|
1039
|
+
id: z.string().default(generateId).openapi({ description: "Unique identifier for the organization member" }),
|
|
1040
|
+
organizationId: z.string().openapi({ description: "ID of the organization this member belongs to" }),
|
|
1041
|
+
userId: z.coerce.string().openapi({ description: "ID of the user who is a member of the organization" }),
|
|
1067
1042
|
role: role$1,
|
|
1068
1043
|
createdAt: dateOrIsoStringSchema.default(() => /* @__PURE__ */ new Date()).openapi({ description: "Timestamp when the member was added to the organization" })
|
|
1069
1044
|
});
|
|
1070
|
-
const invitationSchema = z
|
|
1071
|
-
id: z
|
|
1072
|
-
organizationId: z
|
|
1045
|
+
const invitationSchema = z.looseObject({
|
|
1046
|
+
id: z.string().default(generateId).openapi({ description: "Unique identifier for the invitation" }),
|
|
1047
|
+
organizationId: z.string().openapi({ description: "The organization ID to invite the user to" }),
|
|
1073
1048
|
email: emailSchema.openapi({ description: "The email address of the user to invite" }),
|
|
1074
1049
|
role: role$1.openapi({ description: "The role(s) to assign to the user. It can be `admin`, `member`, or `guest`. Eg: \"member\"" }),
|
|
1075
1050
|
status: invitationStatus,
|
|
1076
|
-
teamId: z
|
|
1077
|
-
inviterId: z
|
|
1051
|
+
teamId: z.string().optional().openapi({ description: "The team ID to invite the user to" }),
|
|
1052
|
+
inviterId: z.string().openapi({ description: "ID of the user who sent the invitation" }),
|
|
1078
1053
|
expiresAt: dateOrIsoStringSchema.openapi({ description: "Timestamp when the invitation expires" })
|
|
1079
1054
|
});
|
|
1080
|
-
const teamSchema = z
|
|
1081
|
-
id: z
|
|
1082
|
-
name: z
|
|
1083
|
-
organizationId: z
|
|
1055
|
+
const teamSchema = z.looseObject({
|
|
1056
|
+
id: z.string().default(generateId).openapi({ description: "Unique identifier of the team" }),
|
|
1057
|
+
name: z.string().min(1).openapi({ description: "Name of the team" }),
|
|
1058
|
+
organizationId: z.string().openapi({ description: "ID of the organization the team belongs to" }),
|
|
1084
1059
|
createdAt: dateOrIsoStringSchema.openapi({ description: "Timestamp when the team was created" }),
|
|
1085
1060
|
updatedAt: dateOrIsoStringSchema.optional().openapi({ description: "Timestamp when the team was last updated" })
|
|
1086
1061
|
});
|
|
1087
|
-
const teamMemberSchema = z
|
|
1088
|
-
id: z
|
|
1089
|
-
teamId: z
|
|
1090
|
-
userId: z
|
|
1062
|
+
const teamMemberSchema = z.looseObject({
|
|
1063
|
+
id: z.string().default(generateId).openapi({ description: "Unique identifier for the team member" }),
|
|
1064
|
+
teamId: z.string().openapi({ description: "ID of the team this member belongs to" }),
|
|
1065
|
+
userId: z.string().openapi({ description: "ID of the user who is a member of the team" }),
|
|
1091
1066
|
createdAt: dateOrIsoStringSchema.default(() => /* @__PURE__ */ new Date()).openapi({ description: "Timestamp when the member was added to the team" })
|
|
1092
1067
|
});
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
//#region src/plugins/organization/routes/crud-access-control.ts
|
|
1096
|
-
const normalizeRoleName = (role$2) => role$2.toLowerCase();
|
|
1068
|
+
// src/plugins/organization/routes/crud-access-control.ts
|
|
1069
|
+
const normalizeRoleName = (role) => role.toLowerCase();
|
|
1097
1070
|
const DEFAULT_MAXIMUM_ROLES_PER_ORGANIZATION = Number.POSITIVE_INFINITY;
|
|
1098
1071
|
const getAdditionalFields = (options, shouldBePartial = false) => {
|
|
1099
1072
|
let additionalFields = options?.schema?.organizationRole?.additionalFields || {};
|
|
@@ -1109,15 +1082,15 @@ const getAdditionalFields = (options, shouldBePartial = false) => {
|
|
|
1109
1082
|
};
|
|
1110
1083
|
const createOrgRole = (options) => {
|
|
1111
1084
|
const { additionalFieldsSchema, $AdditionalFields, $ReturnAdditionalFields } = getAdditionalFields(options, false);
|
|
1112
|
-
const createOrgRoleRequestSchema = z
|
|
1113
|
-
organizationId: z
|
|
1114
|
-
role: z
|
|
1115
|
-
permission: z
|
|
1116
|
-
additionalFields: z
|
|
1085
|
+
const createOrgRoleRequestSchema = z.object({
|
|
1086
|
+
organizationId: z.string().optional().openapi({ description: "The id of the organization to create the role in. If not provided, the user's active organization will be used." }),
|
|
1087
|
+
role: z.string().openapi({ description: "The name of the role to create" }),
|
|
1088
|
+
permission: z.record(z.string(), z.array(z.string())).openapi({ description: "The permission to assign to the role" }),
|
|
1089
|
+
additionalFields: z.object(additionalFieldsSchema.shape).optional()
|
|
1117
1090
|
});
|
|
1118
|
-
const createOrgRoleResponseSchema = z
|
|
1091
|
+
const createOrgRoleResponseSchema = z.object({
|
|
1119
1092
|
roleData: organizationRoleSchema.extend(additionalFieldsSchema.shape),
|
|
1120
|
-
statements: z
|
|
1093
|
+
statements: z.record(z.string(), z.array(z.string()))
|
|
1121
1094
|
}).transform(toSuccess);
|
|
1122
1095
|
return createEndpoint(createRoute$1({
|
|
1123
1096
|
operationId: "createOrgRole",
|
|
@@ -1125,10 +1098,10 @@ const createOrgRole = (options) => {
|
|
|
1125
1098
|
path: "/organization/create-role",
|
|
1126
1099
|
middleware: [orgSessionMiddleware, orgMiddleware(options)],
|
|
1127
1100
|
request: req().bdy(createOrgRoleRequestSchema).bld(),
|
|
1128
|
-
responses: res(createOrgRoleResponseSchema).err(400).err(403, "Not a member or not allowed to create a role", SCHEMAS[Definitions.ERROR].default.extend({ missingPermissions: z
|
|
1101
|
+
responses: res(createOrgRoleResponseSchema).err(400).err(403, "Not a member or not allowed to create a role", SCHEMAS[Definitions.ERROR].default.extend({ missingPermissions: z.array(z.custom((v) => typeof v === "string" && v.includes(":"))).optional() })).err(501).bld()
|
|
1129
1102
|
}), (_authOptions) => async (ctx) => {
|
|
1130
1103
|
const { session, user } = ctx.get("session");
|
|
1131
|
-
const { organizationId = session.activeOrganizationId, role
|
|
1104
|
+
const { organizationId = session.activeOrganizationId, role, permission, additionalFields } = ctx.req.valid("json");
|
|
1132
1105
|
const ac = options.ac;
|
|
1133
1106
|
if (!ac) {
|
|
1134
1107
|
ctx.get("context").logger.error(`[Dynamic Access Control] The organization plugin is missing a pre-defined ac instance.`, `\nPlease refer to the documentation here: https://faire-auth.com/docs/plugins/organization#dynamic-access-control`);
|
|
@@ -1144,7 +1117,7 @@ const createOrgRole = (options) => {
|
|
|
1144
1117
|
message: ORGANIZATION_ERROR_CODES.YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE
|
|
1145
1118
|
}, 400);
|
|
1146
1119
|
}
|
|
1147
|
-
const roleName = normalizeRoleName(role
|
|
1120
|
+
const roleName = normalizeRoleName(role);
|
|
1148
1121
|
const takenRes = await checkIfRoleNameIsTakenByPreDefinedRole({
|
|
1149
1122
|
role: roleName,
|
|
1150
1123
|
organizationId,
|
|
@@ -1257,8 +1230,8 @@ const createOrgRole = (options) => {
|
|
|
1257
1230
|
});
|
|
1258
1231
|
};
|
|
1259
1232
|
const deleteOrgRole = (options) => {
|
|
1260
|
-
const deleteOrgRoleRequestSchema = z
|
|
1261
|
-
const deleteOrgRoleResponseSchema = z
|
|
1233
|
+
const deleteOrgRoleRequestSchema = z.object({ organizationId: z.string().optional().openapi({ description: "The id of the organization to create the role in. If not provided, the user's active organization will be used." }) }).and(z.union([z.object({ roleName: z.string().openapi({ description: "The name of the role to delete" }) }), z.object({ roleId: z.string().openapi({ description: "The id of the role to delete" }) })]));
|
|
1234
|
+
const deleteOrgRoleResponseSchema = z.object({ success: z.literal(true) });
|
|
1262
1235
|
return createEndpoint(createRoute$1({
|
|
1263
1236
|
operationId: "deleteOrgRole",
|
|
1264
1237
|
method: "post",
|
|
@@ -1320,16 +1293,16 @@ const deleteOrgRole = (options) => {
|
|
|
1320
1293
|
}, 403);
|
|
1321
1294
|
}
|
|
1322
1295
|
if (roleName) {
|
|
1323
|
-
const defaultRoles
|
|
1296
|
+
const defaultRoles = options.roles ? Object.keys(options.roles) : [
|
|
1324
1297
|
"owner",
|
|
1325
1298
|
"admin",
|
|
1326
1299
|
"member"
|
|
1327
1300
|
];
|
|
1328
|
-
if (defaultRoles
|
|
1301
|
+
if (defaultRoles.includes(roleName)) {
|
|
1329
1302
|
ctx.get("context").logger.error(`[Dynamic Access Control] Cannot delete a pre-defined role.`, {
|
|
1330
1303
|
roleName,
|
|
1331
1304
|
organizationId,
|
|
1332
|
-
defaultRoles
|
|
1305
|
+
defaultRoles
|
|
1333
1306
|
});
|
|
1334
1307
|
return ctx.render({
|
|
1335
1308
|
success: False,
|
|
@@ -1384,9 +1357,9 @@ const deleteOrgRole = (options) => {
|
|
|
1384
1357
|
};
|
|
1385
1358
|
const listOrgRoles = (options) => {
|
|
1386
1359
|
const { $ReturnAdditionalFields } = getAdditionalFields(options, false);
|
|
1387
|
-
const listOrgRolesRequestSchema = z
|
|
1360
|
+
const listOrgRolesRequestSchema = z.object({ organizationId: z.string().optional().openapi({ description: "The id of the organization to list roles for. If not provided, the user's active organization will be used." }) });
|
|
1388
1361
|
const { additionalFieldsSchema } = getAdditionalFields(options, false);
|
|
1389
|
-
const listOrgRolesResponseSchema = z
|
|
1362
|
+
const listOrgRolesResponseSchema = z.array(organizationRoleSchema.extend(additionalFieldsSchema.shape)).transform(toSuccess);
|
|
1390
1363
|
return createEndpoint(createRoute$1({
|
|
1391
1364
|
operationId: "listOrgRoles",
|
|
1392
1365
|
method: "get",
|
|
@@ -1462,10 +1435,10 @@ const listOrgRoles = (options) => {
|
|
|
1462
1435
|
};
|
|
1463
1436
|
const getOrgRole = (options) => {
|
|
1464
1437
|
const { $ReturnAdditionalFields } = getAdditionalFields(options, false);
|
|
1465
|
-
const getOrgRoleRequestSchema = z
|
|
1466
|
-
organizationId: z
|
|
1467
|
-
roleName: z
|
|
1468
|
-
roleId: z
|
|
1438
|
+
const getOrgRoleRequestSchema = z.object({
|
|
1439
|
+
organizationId: z.string().optional().openapi({ description: "The id of the organization to read a role for. If not provided, the user's active organization will be used." }),
|
|
1440
|
+
roleName: z.string().optional().openapi({ description: "The name of the role to read" }),
|
|
1441
|
+
roleId: z.string().optional().openapi({ description: "The id of the role to read" })
|
|
1469
1442
|
});
|
|
1470
1443
|
const { additionalFieldsSchema } = getAdditionalFields(options, false);
|
|
1471
1444
|
const getOrgRoleResponseSchema = organizationRoleSchema.extend(additionalFieldsSchema.shape).transform(toSuccess);
|
|
@@ -1539,7 +1512,7 @@ const getOrgRole = (options) => {
|
|
|
1539
1512
|
operator: "eq",
|
|
1540
1513
|
connector: "AND"
|
|
1541
1514
|
};
|
|
1542
|
-
let role
|
|
1515
|
+
let role = await ctx.get("context").adapter.findOne({
|
|
1543
1516
|
model: "organizationRole",
|
|
1544
1517
|
where: [{
|
|
1545
1518
|
field: "organizationId",
|
|
@@ -1548,7 +1521,7 @@ const getOrgRole = (options) => {
|
|
|
1548
1521
|
connector: "AND"
|
|
1549
1522
|
}, condition]
|
|
1550
1523
|
});
|
|
1551
|
-
if (!role
|
|
1524
|
+
if (!role) {
|
|
1552
1525
|
ctx.get("context").logger.error(`[Dynamic Access Control] The role name/id does not exist in the database.`, {
|
|
1553
1526
|
...roleName ? { roleName } : { roleId },
|
|
1554
1527
|
organizationId
|
|
@@ -1558,19 +1531,19 @@ const getOrgRole = (options) => {
|
|
|
1558
1531
|
message: ORGANIZATION_ERROR_CODES.ROLE_NOT_FOUND
|
|
1559
1532
|
}, 400);
|
|
1560
1533
|
}
|
|
1561
|
-
role
|
|
1562
|
-
return ctx.render(role
|
|
1534
|
+
role.permission = JSON.parse(role.permission);
|
|
1535
|
+
return ctx.render(role, 200);
|
|
1563
1536
|
});
|
|
1564
1537
|
};
|
|
1565
1538
|
const updateOrgRole = (options) => {
|
|
1566
1539
|
const { additionalFieldsSchema, $AdditionalFields, $ReturnAdditionalFields } = getAdditionalFields(options, true);
|
|
1567
|
-
const updateOrgRoleRequestSchema = z
|
|
1568
|
-
organizationId: z
|
|
1569
|
-
data: z
|
|
1570
|
-
permission: z
|
|
1571
|
-
roleName: z
|
|
1540
|
+
const updateOrgRoleRequestSchema = z.object({
|
|
1541
|
+
organizationId: z.string().optional().openapi({ description: "The id of the organization to update the role in. If not provided, the user's active organization will be used." }),
|
|
1542
|
+
data: z.object({
|
|
1543
|
+
permission: z.record(z.string(), z.array(z.string())).optional().openapi({ description: "The permission to update the role with" }),
|
|
1544
|
+
roleName: z.string().optional().openapi({ description: "The name of the role to update" })
|
|
1572
1545
|
}).extend(additionalFieldsSchema.shape)
|
|
1573
|
-
}).and(z
|
|
1546
|
+
}).and(z.union([z.object({ roleName: z.string().openapi({ description: "The name of the role to update" }) }), z.object({ roleId: z.string().openapi({ description: "The id of the role to update" }) })]));
|
|
1574
1547
|
const updateOrgRoleResponseSchema = organizationRoleSchema.extend(additionalFieldsSchema.shape).transform(toSuccess);
|
|
1575
1548
|
return createEndpoint(createRoute$1({
|
|
1576
1549
|
operationId: "updateOrgRole",
|
|
@@ -1649,7 +1622,7 @@ const updateOrgRole = (options) => {
|
|
|
1649
1622
|
operator: "eq",
|
|
1650
1623
|
connector: "AND"
|
|
1651
1624
|
};
|
|
1652
|
-
let role
|
|
1625
|
+
let role = await ctx.get("context").adapter.findOne({
|
|
1653
1626
|
model: "organizationRole",
|
|
1654
1627
|
where: [{
|
|
1655
1628
|
field: "organizationId",
|
|
@@ -1658,7 +1631,7 @@ const updateOrgRole = (options) => {
|
|
|
1658
1631
|
connector: "AND"
|
|
1659
1632
|
}, condition]
|
|
1660
1633
|
});
|
|
1661
|
-
if (!role
|
|
1634
|
+
if (!role) {
|
|
1662
1635
|
ctx.get("context").logger.error(`[Dynamic Access Control] The role name/id does not exist in the database.`, {
|
|
1663
1636
|
...roleName ? { roleName } : { roleId },
|
|
1664
1637
|
organizationId
|
|
@@ -1668,8 +1641,8 @@ const updateOrgRole = (options) => {
|
|
|
1668
1641
|
message: ORGANIZATION_ERROR_CODES.ROLE_NOT_FOUND
|
|
1669
1642
|
}, 400);
|
|
1670
1643
|
}
|
|
1671
|
-
role
|
|
1672
|
-
const { permission: _, roleName: __
|
|
1644
|
+
role.permission = role.permission ? JSON.parse(role.permission) : void 0;
|
|
1645
|
+
const { permission: _, roleName: __, ...additionalFields } = data;
|
|
1673
1646
|
let updateData = { ...additionalFields };
|
|
1674
1647
|
if (data.permission) {
|
|
1675
1648
|
let newPermission = data.permission;
|
|
@@ -1724,9 +1697,9 @@ const updateOrgRole = (options) => {
|
|
|
1724
1697
|
update
|
|
1725
1698
|
});
|
|
1726
1699
|
return ctx.render({
|
|
1727
|
-
...role
|
|
1700
|
+
...role,
|
|
1728
1701
|
...update,
|
|
1729
|
-
permission: updateData.permission || role
|
|
1702
|
+
permission: updateData.permission || role.permission || null
|
|
1730
1703
|
}, 200);
|
|
1731
1704
|
});
|
|
1732
1705
|
};
|
|
@@ -1782,17 +1755,17 @@ async function checkIfMemberHasPermission({ ctx, permissionRequired: permission,
|
|
|
1782
1755
|
}, 403);
|
|
1783
1756
|
}
|
|
1784
1757
|
}
|
|
1785
|
-
async function checkIfRoleNameIsTakenByPreDefinedRole({ options, organizationId, role
|
|
1786
|
-
const defaultRoles
|
|
1758
|
+
async function checkIfRoleNameIsTakenByPreDefinedRole({ options, organizationId, role, ctx }) {
|
|
1759
|
+
const defaultRoles = options.roles ? Object.keys(options.roles) : [
|
|
1787
1760
|
"owner",
|
|
1788
1761
|
"admin",
|
|
1789
1762
|
"member"
|
|
1790
1763
|
];
|
|
1791
|
-
if (defaultRoles
|
|
1792
|
-
ctx.get("context").logger.error(`[Dynamic Access Control] The role name "${role
|
|
1793
|
-
role
|
|
1764
|
+
if (defaultRoles.includes(role)) {
|
|
1765
|
+
ctx.get("context").logger.error(`[Dynamic Access Control] The role name "${role}" is already taken by a pre-defined role.`, {
|
|
1766
|
+
role,
|
|
1794
1767
|
organizationId,
|
|
1795
|
-
defaultRoles
|
|
1768
|
+
defaultRoles
|
|
1796
1769
|
});
|
|
1797
1770
|
return ctx.render({
|
|
1798
1771
|
success: False,
|
|
@@ -1800,7 +1773,7 @@ async function checkIfRoleNameIsTakenByPreDefinedRole({ options, organizationId,
|
|
|
1800
1773
|
}, 400);
|
|
1801
1774
|
}
|
|
1802
1775
|
}
|
|
1803
|
-
async function checkIfRoleNameIsTakenByRoleInDB({ organizationId, role
|
|
1776
|
+
async function checkIfRoleNameIsTakenByRoleInDB({ organizationId, role, ctx }) {
|
|
1804
1777
|
if (await ctx.get("context").adapter.findOne({
|
|
1805
1778
|
model: "organizationRole",
|
|
1806
1779
|
where: [{
|
|
@@ -1810,13 +1783,13 @@ async function checkIfRoleNameIsTakenByRoleInDB({ organizationId, role: role$2,
|
|
|
1810
1783
|
connector: "AND"
|
|
1811
1784
|
}, {
|
|
1812
1785
|
field: "role",
|
|
1813
|
-
value: role
|
|
1786
|
+
value: role,
|
|
1814
1787
|
operator: "eq",
|
|
1815
1788
|
connector: "AND"
|
|
1816
1789
|
}]
|
|
1817
1790
|
})) {
|
|
1818
|
-
ctx.get("context").logger.error(`[Dynamic Access Control] The role name "${role
|
|
1819
|
-
role
|
|
1791
|
+
ctx.get("context").logger.error(`[Dynamic Access Control] The role name "${role}" is already taken by a role in the database.`, {
|
|
1792
|
+
role,
|
|
1820
1793
|
organizationId
|
|
1821
1794
|
});
|
|
1822
1795
|
return ctx.render({
|
|
@@ -1825,95 +1798,91 @@ async function checkIfRoleNameIsTakenByRoleInDB({ organizationId, role: role$2,
|
|
|
1825
1798
|
}, 400);
|
|
1826
1799
|
}
|
|
1827
1800
|
}
|
|
1828
|
-
|
|
1829
|
-
//#endregion
|
|
1830
|
-
//#region src/plugins/organization/schema/invitation.ts
|
|
1801
|
+
// src/plugins/organization/schema/invitation.ts
|
|
1831
1802
|
/**
|
|
1832
1803
|
* Schema for creating an invitation
|
|
1833
1804
|
*/
|
|
1834
|
-
const createInvitationSchema = z
|
|
1805
|
+
const createInvitationSchema = z.object({
|
|
1835
1806
|
email: invitationSchema.shape.email,
|
|
1836
|
-
role: z
|
|
1807
|
+
role: z.union([role$1.openapi({ description: "The role to assign to the user" }), z.array(role$1.openapi({ description: "The roles to assign to the user" }))]).openapi({ description: "The role(s) to assign to the user. It can be `admin`, `member`, or `guest`. Eg: \"member\"" }),
|
|
1837
1808
|
organizationId: invitationSchema.shape.organizationId.optional(),
|
|
1838
|
-
resend: z
|
|
1839
|
-
teamId: z
|
|
1809
|
+
resend: z.boolean().openapi({ description: "Resend the invitation email, if the user is already invited. Eg: true" }).optional(),
|
|
1810
|
+
teamId: z.union([invitationSchema.shape.teamId.unwrap(), z.array(invitationSchema.shape.teamId.unwrap())]).optional()
|
|
1840
1811
|
});
|
|
1841
1812
|
/**
|
|
1842
1813
|
* Schema for accepting/rejecting/cancelling an invitation
|
|
1843
1814
|
*/
|
|
1844
|
-
const invitationRequestSchema = z
|
|
1815
|
+
const invitationRequestSchema = z.object({ invitationId: invitationSchema.shape.id.unwrap().openapi({ description: "The ID of the invitation to accept, reject, or cancel" }) });
|
|
1845
1816
|
/**
|
|
1846
1817
|
* Schema for accept/rejection response
|
|
1847
1818
|
* Member is null in rejection response
|
|
1848
1819
|
*/
|
|
1849
|
-
const invitationResponseSchema = z
|
|
1820
|
+
const invitationResponseSchema = z.object({
|
|
1850
1821
|
invitation: invitationSchema,
|
|
1851
1822
|
member: memberSchema.nullable().default(null)
|
|
1852
1823
|
}).openapi({ description: "Response containing invitation details and optional member information" });
|
|
1853
|
-
const getInvitationQuerySchema = z
|
|
1824
|
+
const getInvitationQuerySchema = z.object({ id: z.string().openapi({ description: "The ID of the invitation to get" }) });
|
|
1854
1825
|
const getInvitationResponseSchema = invitationSchema.extend({
|
|
1855
|
-
organizationName: z
|
|
1856
|
-
organizationSlug: z
|
|
1826
|
+
organizationName: z.string().openapi({ description: "Name of the organization" }),
|
|
1827
|
+
organizationSlug: z.string().openapi({ description: "URL-friendly slug of the organization" }),
|
|
1857
1828
|
inviterEmail: emailSchema.openapi({ description: "Email address of the user who sent the invitation" })
|
|
1858
1829
|
});
|
|
1859
1830
|
/**
|
|
1860
1831
|
* Schema for listing invitations query
|
|
1861
1832
|
*/
|
|
1862
|
-
const listInvitationsQuerySchema = z
|
|
1833
|
+
const listInvitationsQuerySchema = z.object({ organizationId: z.string().openapi({ description: "The ID of the organization to list invitations for" }).optional() });
|
|
1863
1834
|
/**
|
|
1864
1835
|
* Schema for listing invitations query
|
|
1865
1836
|
*/
|
|
1866
|
-
const listInvitationsResponseSchema = z
|
|
1837
|
+
const listInvitationsResponseSchema = z.array(invitationSchema).openapi({ description: "List of invitations" });
|
|
1867
1838
|
/**
|
|
1868
1839
|
* Schema for listing user invitations query
|
|
1869
1840
|
*/
|
|
1870
|
-
const listUserInvitationsQuerySchema = z
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
//#region src/plugins/organization/schema/member.ts
|
|
1874
|
-
const memberWithUserSchema = memberSchema.extend({ user: looseUserSchema.extend({ email: z$1.string() }) });
|
|
1841
|
+
const listUserInvitationsQuerySchema = z.object({ email: emailSchema.openapi({ description: "The email of the user to list invitations for. This only works for server side API calls." }).optional() });
|
|
1842
|
+
// src/plugins/organization/schema/member.ts
|
|
1843
|
+
const memberWithUserSchema = memberSchema.extend({ user: looseUserSchema.extend({ email: z.string() }) });
|
|
1875
1844
|
/**
|
|
1876
1845
|
* Schema for adding a member
|
|
1877
1846
|
*/
|
|
1878
|
-
const addMemberSchema = z
|
|
1879
|
-
userId: z
|
|
1880
|
-
role: z
|
|
1847
|
+
const addMemberSchema = z.object({
|
|
1848
|
+
userId: z.string().nullable().default(null).openapi({ description: "The user ID which represents the user to be added as a member. If `null` is provided, then it's expected to provide session headers. Eg: \"user-id\"" }),
|
|
1849
|
+
role: z.union([role$1.openapi({ description: "The role to assign to the user" }), z.array(role$1.openapi({ description: "The roles to assign to the user" }))]).openapi({ description: "The role(s) to assign to the new member. It can be `admin`, `member`, or `guest`. Eg: \"member\"" }),
|
|
1881
1850
|
organizationId: memberSchema.shape.organizationId.optional(),
|
|
1882
|
-
teamId: z
|
|
1851
|
+
teamId: z.string().openapi({ description: "An optional team ID to add the member to. Eg: \"team-id\"" }).optional()
|
|
1883
1852
|
});
|
|
1884
1853
|
/**
|
|
1885
1854
|
* Schema for updating member role
|
|
1886
1855
|
*/
|
|
1887
|
-
const updateMemberRoleSchema = z
|
|
1888
|
-
role: z
|
|
1856
|
+
const updateMemberRoleSchema = z.object({
|
|
1857
|
+
role: z.union([role$1.openapi({ description: "The role to assign to the user" }), z.array(role$1.openapi({ description: "The roles to assign to the user" }))]).openapi({ description: "The role(s) to assign to the member. It can be `admin`, `member`, or `guest`. Eg: \"member\"" }),
|
|
1889
1858
|
memberId: memberSchema.shape.id.unwrap().openapi({ description: "The member ID to apply the role update to" }),
|
|
1890
1859
|
organizationId: memberSchema.shape.organizationId.optional()
|
|
1891
1860
|
});
|
|
1892
1861
|
/**
|
|
1893
1862
|
* Schema for removing a member
|
|
1894
1863
|
*/
|
|
1895
|
-
const removeMemberSchema = z
|
|
1896
|
-
memberIdOrEmail: z
|
|
1864
|
+
const removeMemberSchema = z.object({
|
|
1865
|
+
memberIdOrEmail: z.string().openapi({ description: "The ID or email of the member to remove" }),
|
|
1897
1866
|
organizationId: memberSchema.shape.organizationId.optional()
|
|
1898
1867
|
});
|
|
1899
1868
|
/**
|
|
1900
1869
|
* Schema for member response with pagination
|
|
1901
1870
|
*/
|
|
1902
|
-
const memberListResponseSchema = z
|
|
1903
|
-
members: z
|
|
1904
|
-
total: z
|
|
1871
|
+
const memberListResponseSchema = z.object({
|
|
1872
|
+
members: z.array(memberWithUserSchema),
|
|
1873
|
+
total: z.number()
|
|
1905
1874
|
}).openapi({ description: "Paginated list of organization members" });
|
|
1906
1875
|
/**
|
|
1907
1876
|
* Schema for listing members query parameters
|
|
1908
1877
|
*/
|
|
1909
|
-
const listMembersQuerySchema = z
|
|
1910
|
-
limit: z
|
|
1911
|
-
offset: z
|
|
1912
|
-
sortBy: z
|
|
1913
|
-
sortDirection: z
|
|
1914
|
-
filterField: z
|
|
1915
|
-
filterValue: z
|
|
1916
|
-
filterOperator: z
|
|
1878
|
+
const listMembersQuerySchema = z.object({
|
|
1879
|
+
limit: z.string().or(z.number()).openapi({ description: "The number of members to return" }).optional(),
|
|
1880
|
+
offset: z.string().or(z.number()).openapi({ description: "The offset to start from" }).optional(),
|
|
1881
|
+
sortBy: z.string().openapi({ description: "The field to sort by" }).optional(),
|
|
1882
|
+
sortDirection: z.enum(["asc", "desc"]).openapi({ description: "The direction to sort by" }).optional(),
|
|
1883
|
+
filterField: z.string().openapi({ description: "The field to filter by" }).optional(),
|
|
1884
|
+
filterValue: z.string().or(z.number()).or(z.boolean()).openapi({ description: "The value to filter by" }).optional(),
|
|
1885
|
+
filterOperator: z.enum([
|
|
1917
1886
|
"eq",
|
|
1918
1887
|
"ne",
|
|
1919
1888
|
"lt",
|
|
@@ -1922,102 +1891,98 @@ const listMembersQuerySchema = z$1.object({
|
|
|
1922
1891
|
"gte",
|
|
1923
1892
|
"contains"
|
|
1924
1893
|
]).openapi({ description: "The operator to use for the filter" }).optional(),
|
|
1925
|
-
organizationId: z
|
|
1894
|
+
organizationId: z.string().openapi({ description: "The organization ID to list members for. If not provided, will default to the user's active organization." }).optional()
|
|
1926
1895
|
});
|
|
1927
1896
|
/**
|
|
1928
1897
|
* Schema for leave organization
|
|
1929
1898
|
*/
|
|
1930
|
-
const leaveOrganizationSchema = z
|
|
1931
|
-
|
|
1932
|
-
//#endregion
|
|
1933
|
-
//#region src/plugins/organization/schema/organization.ts
|
|
1899
|
+
const leaveOrganizationSchema = z.object({ organizationId: memberSchema.shape.organizationId.openapi({ description: "The organization ID to leave" }) });
|
|
1900
|
+
// src/plugins/organization/schema/organization.ts
|
|
1934
1901
|
/**
|
|
1935
1902
|
* Base schema for creating an organization
|
|
1936
1903
|
*/
|
|
1937
|
-
const createOrganizationBaseSchema = z
|
|
1904
|
+
const createOrganizationBaseSchema = z.object({
|
|
1938
1905
|
name: organizationSchema.shape.name,
|
|
1939
1906
|
slug: organizationSchema.shape.slug,
|
|
1940
|
-
userId: z
|
|
1907
|
+
userId: z.string().openapi({ description: "The user id of the organization creator. If not provided, the current user will be used. Should only be used by admins or when called by the server. server-only." }).optional(),
|
|
1941
1908
|
logo: organizationSchema.shape.logo.unwrap().optional(),
|
|
1942
1909
|
metadata: organizationSchema.shape.metadata,
|
|
1943
|
-
keepCurrentActiveOrganization: z
|
|
1910
|
+
keepCurrentActiveOrganization: z.boolean().openapi({ description: "Whether to keep the current active organization active after creating a new one" }).optional()
|
|
1944
1911
|
});
|
|
1945
1912
|
/**
|
|
1946
1913
|
* Schema for updating an organization
|
|
1947
1914
|
*/
|
|
1948
|
-
const updateOrganizationSchema = z
|
|
1949
|
-
data: z
|
|
1915
|
+
const updateOrganizationSchema = z.object({
|
|
1916
|
+
data: z.object({
|
|
1950
1917
|
name: organizationSchema.shape.name,
|
|
1951
1918
|
slug: organizationSchema.shape.slug,
|
|
1952
1919
|
logo: organizationSchema.shape.logo.unwrap().unwrap(),
|
|
1953
1920
|
metadata: organizationSchema.shape.metadata.unwrap().unwrap()
|
|
1954
1921
|
}),
|
|
1955
|
-
organizationId: z
|
|
1922
|
+
organizationId: z.string().openapi({ description: "The organization ID" }).optional()
|
|
1956
1923
|
});
|
|
1957
1924
|
/**
|
|
1958
1925
|
* Schema for organization response with members
|
|
1959
1926
|
*/
|
|
1960
|
-
const organizationWithMembersSchema = organizationSchema.extend({ members: z
|
|
1927
|
+
const organizationWithMembersSchema = organizationSchema.extend({ members: z.array(memberSchema) }).openapi({ description: "Organization details including member information" });
|
|
1961
1928
|
/**
|
|
1962
1929
|
* Schema for checking if a slug is available
|
|
1963
1930
|
*/
|
|
1964
|
-
const checkOrganizationSlugSchema = z
|
|
1931
|
+
const checkOrganizationSlugSchema = z.object({ slug: z.string().openapi({ description: "The organization slug to check" }) });
|
|
1965
1932
|
/**
|
|
1966
1933
|
* Schema for getting organization details
|
|
1967
1934
|
*/
|
|
1968
|
-
const getFullOrganizationQuerySchema = z
|
|
1969
|
-
organizationId: z
|
|
1970
|
-
organizationSlug: z
|
|
1971
|
-
membersLimit: z
|
|
1935
|
+
const getFullOrganizationQuerySchema = z.object({
|
|
1936
|
+
organizationId: z.string().openapi({ description: "The organization id to get" }).optional(),
|
|
1937
|
+
organizationSlug: z.string().openapi({ description: "The organization slug to get" }).optional(),
|
|
1938
|
+
membersLimit: z.number().or(z.string().transform((val) => parseInt(val))).openapi({ description: "The limit of members to get. By default, it uses the membershipLimit option which defaults to 100." }).optional()
|
|
1972
1939
|
});
|
|
1973
1940
|
/**
|
|
1974
1941
|
* Schema for full organization details
|
|
1975
1942
|
*/
|
|
1976
1943
|
const fullOrganizationSchema = organizationSchema.extend({
|
|
1977
|
-
members: z
|
|
1978
|
-
invitations: z
|
|
1979
|
-
teams: z
|
|
1944
|
+
members: z.array(memberWithUserSchema),
|
|
1945
|
+
invitations: z.array(invitationSchema),
|
|
1946
|
+
teams: z.array(teamSchema).nullable()
|
|
1980
1947
|
}).openapi({ description: "Complete organization details with members, invitations, and teams" });
|
|
1981
1948
|
/**
|
|
1982
1949
|
* Schema for organization list response
|
|
1983
1950
|
*/
|
|
1984
|
-
const organizationListResponseSchema = z
|
|
1951
|
+
const organizationListResponseSchema = z.array(organizationSchema).openapi({ description: "List of organizations" });
|
|
1985
1952
|
/**
|
|
1986
1953
|
* Schema for setting active organization
|
|
1987
1954
|
*/
|
|
1988
|
-
const setActiveOrganizationSchema = z
|
|
1989
|
-
organizationId: z
|
|
1990
|
-
organizationSlug: z
|
|
1955
|
+
const setActiveOrganizationSchema = z.object({
|
|
1956
|
+
organizationId: z.string().nullable().openapi({ description: "The organization id to set as active. It can be null to unset the active organization." }).optional(),
|
|
1957
|
+
organizationSlug: z.string().openapi({ description: "The organization slug to set as active. It can be null to unset the active organization if organizationId is not provided." }).optional()
|
|
1991
1958
|
});
|
|
1992
1959
|
/**
|
|
1993
1960
|
* Schema for deleting organization
|
|
1994
1961
|
*/
|
|
1995
|
-
const deleteOrganizationSchema = z
|
|
1962
|
+
const deleteOrganizationSchema = z.object({ organizationId: z.string().openapi({ description: "The organization id to delete" }) });
|
|
1996
1963
|
/**
|
|
1997
1964
|
* Response schema for deleted organization
|
|
1998
1965
|
*/
|
|
1999
|
-
const deleteOrganizationResponseSchema = z
|
|
2000
|
-
|
|
2001
|
-
//#endregion
|
|
2002
|
-
//#region src/plugins/organization/schema/team.ts
|
|
1966
|
+
const deleteOrganizationResponseSchema = z.string().openapi({ description: "ID of the organization that was successfully deleted" });
|
|
1967
|
+
// src/plugins/organization/schema/team.ts
|
|
2003
1968
|
/**
|
|
2004
1969
|
* Base schema for creating a team
|
|
2005
1970
|
*/
|
|
2006
|
-
const createTeamBaseSchema = z
|
|
1971
|
+
const createTeamBaseSchema = z.object({
|
|
2007
1972
|
name: teamSchema.shape.name,
|
|
2008
1973
|
organizationId: teamSchema.shape.organizationId.optional()
|
|
2009
1974
|
});
|
|
2010
1975
|
/**
|
|
2011
1976
|
* Schema for requesting team removal
|
|
2012
1977
|
*/
|
|
2013
|
-
const removeTeamSchema = z
|
|
1978
|
+
const removeTeamSchema = z.object({
|
|
2014
1979
|
teamId: teamSchema.shape.id.unwrap().openapi({ description: "The team ID of the team to remove" }),
|
|
2015
1980
|
organizationId: teamSchema.shape.organizationId.optional()
|
|
2016
1981
|
});
|
|
2017
1982
|
/**
|
|
2018
1983
|
* Schema for updating a team
|
|
2019
1984
|
*/
|
|
2020
|
-
const updateTeamSchema = z
|
|
1985
|
+
const updateTeamSchema = z.object({
|
|
2021
1986
|
teamId: teamSchema.shape.id.unwrap().openapi({ description: "The ID of the team to be updated" }),
|
|
2022
1987
|
data: teamSchema.omit({
|
|
2023
1988
|
id: true,
|
|
@@ -2028,49 +1993,47 @@ const updateTeamSchema = z$1.object({
|
|
|
2028
1993
|
/**
|
|
2029
1994
|
* Schema for setting active team
|
|
2030
1995
|
*/
|
|
2031
|
-
const setActiveTeamSchema = z
|
|
1996
|
+
const setActiveTeamSchema = z.object({ teamId: teamSchema.shape.id.unwrap().nullish().openapi({ description: "The team id to set as active. It can be null to unset the active team" }) });
|
|
2032
1997
|
/**
|
|
2033
1998
|
* Schema for team list response
|
|
2034
1999
|
*/
|
|
2035
|
-
const teamListResponseSchema = z
|
|
2000
|
+
const teamListResponseSchema = z.array(teamSchema);
|
|
2036
2001
|
/**
|
|
2037
2002
|
* Schema for listing teams query
|
|
2038
2003
|
*/
|
|
2039
|
-
const listTeamsQuerySchema = z
|
|
2004
|
+
const listTeamsQuerySchema = z.object({ organizationId: teamSchema.shape.organizationId.optional() });
|
|
2040
2005
|
/**
|
|
2041
2006
|
* Schema for listing team members query
|
|
2042
2007
|
*/
|
|
2043
|
-
const listTeamMembersQuerySchema = z
|
|
2008
|
+
const listTeamMembersQuerySchema = z.object({ teamId: teamSchema.shape.id.unwrap().optional().openapi({ description: "The team whose members we should return. If this is not provided the members of the current active team get returned." }) });
|
|
2044
2009
|
/**
|
|
2045
2010
|
* Schema for adding team member
|
|
2046
2011
|
*/
|
|
2047
|
-
const addTeamMemberSchema = z
|
|
2012
|
+
const addTeamMemberSchema = z.object({
|
|
2048
2013
|
teamId: teamMemberSchema.shape.teamId.openapi({ description: "The team the user should be a member of" }),
|
|
2049
2014
|
userId: teamMemberSchema.shape.userId.openapi({ description: "The user ID which represents the user to be added as a member" })
|
|
2050
2015
|
});
|
|
2051
2016
|
/**
|
|
2052
2017
|
* Schema for removing team member
|
|
2053
2018
|
*/
|
|
2054
|
-
const removeTeamMemberSchema = z
|
|
2019
|
+
const removeTeamMemberSchema = z.object({
|
|
2055
2020
|
teamId: teamMemberSchema.shape.teamId.openapi({ description: "The team the user should be removed from" }),
|
|
2056
2021
|
userId: teamMemberSchema.shape.userId.openapi({ description: "The user which should be removed from the team" })
|
|
2057
2022
|
});
|
|
2058
2023
|
/**
|
|
2059
2024
|
* Schema for team member response
|
|
2060
2025
|
*/
|
|
2061
|
-
const teamMemberResponseSchema = z
|
|
2062
|
-
id: z
|
|
2063
|
-
userId: z
|
|
2064
|
-
teamId: z
|
|
2026
|
+
const teamMemberResponseSchema = z.object({
|
|
2027
|
+
id: z.string().openapi({ description: "Unique identifier for the team member" }),
|
|
2028
|
+
userId: z.string().openapi({ description: "ID of the user who is a member of the team" }),
|
|
2029
|
+
teamId: z.string().openapi({ description: "ID of the team this member belongs to" }),
|
|
2065
2030
|
createdAt: dateOrIsoStringSchema.openapi({ description: "Timestamp when the member was added to the team" })
|
|
2066
2031
|
});
|
|
2067
2032
|
/**
|
|
2068
2033
|
* Schema for team member list response
|
|
2069
2034
|
*/
|
|
2070
|
-
const teamMemberListResponseSchema = z
|
|
2071
|
-
|
|
2072
|
-
//#endregion
|
|
2073
|
-
//#region src/plugins/organization/routes/crud-invites.ts
|
|
2035
|
+
const teamMemberListResponseSchema = z.array(teamMemberResponseSchema).openapi({ description: "List of team members" });
|
|
2036
|
+
// src/plugins/organization/routes/crud-invites.ts
|
|
2074
2037
|
const createInvitation = (option) => {
|
|
2075
2038
|
const additionalFieldsSchema = toZodSchema({
|
|
2076
2039
|
fields: option?.schema?.invitation?.additionalFields ?? {},
|
|
@@ -2086,7 +2049,7 @@ const createInvitation = (option) => {
|
|
|
2086
2049
|
responses: res(invitationResponseSchema.transform(toSuccess)).err(400, "Not found or already invited").err(401, "Not allowed to perform action").err(403, "Not allowed to perform action or invitation or member limit reached").bld()
|
|
2087
2050
|
}), (_authOptions) => async (ctx) => {
|
|
2088
2051
|
const session = ctx.get("session");
|
|
2089
|
-
const { organizationId: requestOrganizationId, role
|
|
2052
|
+
const { organizationId: requestOrganizationId, role, email, resend, teamId, ...additionalFields } = ctx.req.valid("json");
|
|
2090
2053
|
const organizationId = requestOrganizationId ?? session.session.activeOrganizationId;
|
|
2091
2054
|
if (!organizationId) return ctx.render({
|
|
2092
2055
|
success: False,
|
|
@@ -2111,7 +2074,7 @@ const createInvitation = (option) => {
|
|
|
2111
2074
|
message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION
|
|
2112
2075
|
}, 403);
|
|
2113
2076
|
const creatorRole = ctx.get("orgOptions").creatorRole || "owner";
|
|
2114
|
-
const roles = parseRoles(role
|
|
2077
|
+
const roles = parseRoles(role);
|
|
2115
2078
|
if (member.role !== creatorRole && roles.split(",").includes(creatorRole)) return ctx.render({
|
|
2116
2079
|
success: False,
|
|
2117
2080
|
message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE
|
|
@@ -2131,14 +2094,14 @@ const createInvitation = (option) => {
|
|
|
2131
2094
|
success: False,
|
|
2132
2095
|
message: ORGANIZATION_ERROR_CODES.USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION
|
|
2133
2096
|
}, 400);
|
|
2134
|
-
const organization
|
|
2135
|
-
if (!organization
|
|
2097
|
+
const organization = await adapter.findOrganizationById(organizationId);
|
|
2098
|
+
if (!organization) return ctx.render({
|
|
2136
2099
|
success: False,
|
|
2137
2100
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2138
2101
|
}, 400);
|
|
2139
2102
|
if (alreadyInvited.length && resend) {
|
|
2140
2103
|
const existingInvitation = alreadyInvited[0];
|
|
2141
|
-
const newExpiresAt = getDate(ctx.get("orgOptions").invitationExpiresIn ??
|
|
2104
|
+
const newExpiresAt = getDate(ctx.get("orgOptions").invitationExpiresIn ?? 172800, "sec");
|
|
2142
2105
|
await ctx.get("context").adapter.update({
|
|
2143
2106
|
model: "invitation",
|
|
2144
2107
|
where: [{
|
|
@@ -2155,7 +2118,7 @@ const createInvitation = (option) => {
|
|
|
2155
2118
|
id: updatedInvitation.id,
|
|
2156
2119
|
role: updatedInvitation.role,
|
|
2157
2120
|
email: updatedInvitation.email.toLowerCase(),
|
|
2158
|
-
organization
|
|
2121
|
+
organization,
|
|
2159
2122
|
inviter: {
|
|
2160
2123
|
...member,
|
|
2161
2124
|
user: session.user
|
|
@@ -2171,7 +2134,7 @@ const createInvitation = (option) => {
|
|
|
2171
2134
|
const optionInvitationLimit = ctx.get("orgOptions").invitationLimit;
|
|
2172
2135
|
const invitationLimit = typeof optionInvitationLimit === "function" ? await optionInvitationLimit({
|
|
2173
2136
|
user: session.user,
|
|
2174
|
-
organization
|
|
2137
|
+
organization,
|
|
2175
2138
|
member
|
|
2176
2139
|
}, ctx.get("context")) : optionInvitationLimit ?? 100;
|
|
2177
2140
|
if ((await adapter.findPendingInvitations({ organizationId })).length >= invitationLimit) return ctx.render({
|
|
@@ -2180,10 +2143,10 @@ const createInvitation = (option) => {
|
|
|
2180
2143
|
}, 403);
|
|
2181
2144
|
const orgOptions = ctx.get("orgOptions");
|
|
2182
2145
|
if (orgOptions.teams && orgOptions.teams.enabled && orgOptions.teams.maximumMembersPerTeam !== void 0 && teamId) {
|
|
2183
|
-
const teamIds
|
|
2184
|
-
for (const teamId
|
|
2146
|
+
const teamIds = typeof teamId === "string" ? [teamId] : teamId;
|
|
2147
|
+
for (const teamId of teamIds) {
|
|
2185
2148
|
const team = await adapter.findTeamById({
|
|
2186
|
-
teamId
|
|
2149
|
+
teamId,
|
|
2187
2150
|
organizationId,
|
|
2188
2151
|
includeTeamMembers: true
|
|
2189
2152
|
});
|
|
@@ -2193,7 +2156,7 @@ const createInvitation = (option) => {
|
|
|
2193
2156
|
}, 400);
|
|
2194
2157
|
if (!!orgOptions.teams?.enabled) {
|
|
2195
2158
|
const maximumMembersPerTeam = typeof orgOptions.teams.maximumMembersPerTeam === "function" ? await orgOptions.teams.maximumMembersPerTeam({
|
|
2196
|
-
teamId
|
|
2159
|
+
teamId,
|
|
2197
2160
|
session,
|
|
2198
2161
|
organizationId
|
|
2199
2162
|
}) : orgOptions.teams.maximumMembersPerTeam ?? Infinity;
|
|
@@ -2220,7 +2183,7 @@ const createInvitation = (option) => {
|
|
|
2220
2183
|
...teamIds.length > 0 && { teamId: teamIds[0] }
|
|
2221
2184
|
},
|
|
2222
2185
|
inviter: session.user,
|
|
2223
|
-
organization
|
|
2186
|
+
organization
|
|
2224
2187
|
});
|
|
2225
2188
|
if (response && typeof response === "object" && "data" in response) invitationData = {
|
|
2226
2189
|
...invitationData,
|
|
@@ -2235,7 +2198,7 @@ const createInvitation = (option) => {
|
|
|
2235
2198
|
id: invitation.id,
|
|
2236
2199
|
role: invitation.role,
|
|
2237
2200
|
email: invitation.email.toLowerCase(),
|
|
2238
|
-
organization
|
|
2201
|
+
organization,
|
|
2239
2202
|
inviter: {
|
|
2240
2203
|
...member,
|
|
2241
2204
|
user: session.user
|
|
@@ -2245,7 +2208,7 @@ const createInvitation = (option) => {
|
|
|
2245
2208
|
if (option?.organizationHooks?.afterCreateInvitation) await option?.organizationHooks.afterCreateInvitation({
|
|
2246
2209
|
invitation,
|
|
2247
2210
|
inviter: session.user,
|
|
2248
|
-
organization
|
|
2211
|
+
organization
|
|
2249
2212
|
});
|
|
2250
2213
|
return ctx.render({ invitation }, 200);
|
|
2251
2214
|
});
|
|
@@ -2280,15 +2243,15 @@ const acceptInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2280
2243
|
success: False,
|
|
2281
2244
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_MEMBERSHIP_LIMIT_REACHED
|
|
2282
2245
|
}, 401);
|
|
2283
|
-
const organization
|
|
2284
|
-
if (!organization
|
|
2246
|
+
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
2247
|
+
if (!organization) return ctx.render({
|
|
2285
2248
|
success: False,
|
|
2286
2249
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2287
2250
|
}, 400);
|
|
2288
2251
|
if (options?.organizationHooks?.beforeAcceptInvitation) await options?.organizationHooks.beforeAcceptInvitation({
|
|
2289
2252
|
invitation,
|
|
2290
2253
|
user: session.user,
|
|
2291
|
-
organization
|
|
2254
|
+
organization
|
|
2292
2255
|
});
|
|
2293
2256
|
const acceptedI = await adapter.updateInvitation({
|
|
2294
2257
|
invitationId,
|
|
@@ -2321,8 +2284,9 @@ const acceptInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2321
2284
|
}
|
|
2322
2285
|
if (onlyOne) {
|
|
2323
2286
|
const teamId = teamIds[0];
|
|
2287
|
+
const updatedSession = await adapter.setActiveTeam(session.session.token, teamId);
|
|
2324
2288
|
await setSessionCookie(ctx, authOptions, {
|
|
2325
|
-
session:
|
|
2289
|
+
session: updatedSession,
|
|
2326
2290
|
user: session.user
|
|
2327
2291
|
});
|
|
2328
2292
|
}
|
|
@@ -2342,7 +2306,7 @@ const acceptInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2342
2306
|
invitation: acceptedI,
|
|
2343
2307
|
member,
|
|
2344
2308
|
user: session.user,
|
|
2345
|
-
organization
|
|
2309
|
+
organization
|
|
2346
2310
|
});
|
|
2347
2311
|
return ctx.render({
|
|
2348
2312
|
invitation: acceptedI,
|
|
@@ -2374,15 +2338,15 @@ const rejectInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2374
2338
|
success: False,
|
|
2375
2339
|
message: ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION
|
|
2376
2340
|
}, 403);
|
|
2377
|
-
const organization
|
|
2378
|
-
if (!organization
|
|
2341
|
+
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
2342
|
+
if (!organization) return ctx.render({
|
|
2379
2343
|
success: False,
|
|
2380
2344
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2381
2345
|
}, 400);
|
|
2382
2346
|
if (options?.organizationHooks?.beforeRejectInvitation) await options?.organizationHooks.beforeRejectInvitation({
|
|
2383
2347
|
invitation,
|
|
2384
2348
|
user: session.user,
|
|
2385
|
-
organization
|
|
2349
|
+
organization
|
|
2386
2350
|
});
|
|
2387
2351
|
const rejectedI = await adapter.updateInvitation({
|
|
2388
2352
|
invitationId,
|
|
@@ -2391,7 +2355,7 @@ const rejectInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2391
2355
|
if (options?.organizationHooks?.afterRejectInvitation) await options?.organizationHooks.afterRejectInvitation({
|
|
2392
2356
|
invitation: rejectedI ?? invitation,
|
|
2393
2357
|
user: session.user,
|
|
2394
|
-
organization
|
|
2358
|
+
organization
|
|
2395
2359
|
});
|
|
2396
2360
|
return ctx.render({ invitation: rejectedI }, 200);
|
|
2397
2361
|
});
|
|
@@ -2429,15 +2393,15 @@ const cancelInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2429
2393
|
success: False,
|
|
2430
2394
|
message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION
|
|
2431
2395
|
}, 401);
|
|
2432
|
-
const organization
|
|
2433
|
-
if (!organization
|
|
2396
|
+
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
2397
|
+
if (!organization) return ctx.render({
|
|
2434
2398
|
success: False,
|
|
2435
2399
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2436
2400
|
}, 400);
|
|
2437
2401
|
if (options?.organizationHooks?.beforeCancelInvitation) await options?.organizationHooks.beforeCancelInvitation({
|
|
2438
2402
|
invitation,
|
|
2439
2403
|
cancelledBy: session.user,
|
|
2440
|
-
organization
|
|
2404
|
+
organization
|
|
2441
2405
|
});
|
|
2442
2406
|
const canceledI = await adapter.updateInvitation({
|
|
2443
2407
|
invitationId,
|
|
@@ -2446,7 +2410,7 @@ const cancelInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2446
2410
|
if (options?.organizationHooks?.afterCancelInvitation) await options?.organizationHooks.afterCancelInvitation({
|
|
2447
2411
|
invitation: canceledI ?? invitation,
|
|
2448
2412
|
cancelledBy: session.user,
|
|
2449
|
-
organization
|
|
2413
|
+
organization
|
|
2450
2414
|
});
|
|
2451
2415
|
return ctx.render(canceledI, 200);
|
|
2452
2416
|
});
|
|
@@ -2471,8 +2435,8 @@ const getInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2471
2435
|
success: False,
|
|
2472
2436
|
message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION
|
|
2473
2437
|
}, 403);
|
|
2474
|
-
const organization
|
|
2475
|
-
if (!organization
|
|
2438
|
+
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
2439
|
+
if (!organization) return ctx.render({
|
|
2476
2440
|
success: False,
|
|
2477
2441
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2478
2442
|
}, 400);
|
|
@@ -2486,8 +2450,8 @@ const getInvitation = (options) => createEndpoint(createRoute$1({
|
|
|
2486
2450
|
}, 400);
|
|
2487
2451
|
return ctx.render({
|
|
2488
2452
|
...invitation,
|
|
2489
|
-
organizationName: organization
|
|
2490
|
-
organizationSlug: organization
|
|
2453
|
+
organizationName: organization["name"],
|
|
2454
|
+
organizationSlug: organization["slug"],
|
|
2491
2455
|
inviterEmail: member.user.email
|
|
2492
2456
|
}, 200);
|
|
2493
2457
|
});
|
|
@@ -2542,9 +2506,7 @@ const listUserInvitations = (options) => createEndpoint(createRoute$1({
|
|
|
2542
2506
|
const invitations = await getOrgAdapter(ctx.get("context"), options).listUserInvitations(userEmail);
|
|
2543
2507
|
return ctx.render(invitations, 200);
|
|
2544
2508
|
});
|
|
2545
|
-
|
|
2546
|
-
//#endregion
|
|
2547
|
-
//#region src/plugins/organization/routes/crud-members.ts
|
|
2509
|
+
// src/plugins/organization/routes/crud-members.ts
|
|
2548
2510
|
const addMember = (option) => {
|
|
2549
2511
|
const additionalFieldsSchema = toZodSchema({
|
|
2550
2512
|
fields: option?.schema?.member?.additionalFields ?? {},
|
|
@@ -2560,7 +2522,7 @@ const addMember = (option) => {
|
|
|
2560
2522
|
request: req().bdy(addMemberSchema.extend(additionalFieldsSchema.shape)).bld(),
|
|
2561
2523
|
responses: res(memberSchema.transform(toSuccess)).err(400, "User not found or already a member").err(401, "Not allowed to perform action").err(403, "Membership limit reached").bld()
|
|
2562
2524
|
}), (_authOptions) => async (ctx) => {
|
|
2563
|
-
let { organizationId, role
|
|
2525
|
+
let { organizationId, role, userId, teamId, ...additionalFields } = ctx.req.valid("json");
|
|
2564
2526
|
const session = userId == null || organizationId == null ? ctx.get("session") ?? null : null;
|
|
2565
2527
|
userId ??= session?.user.id;
|
|
2566
2528
|
organizationId ??= session?.session.activeOrganizationId;
|
|
@@ -2603,15 +2565,15 @@ const addMember = (option) => {
|
|
|
2603
2565
|
success: False,
|
|
2604
2566
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_MEMBERSHIP_LIMIT_REACHED
|
|
2605
2567
|
}, 403);
|
|
2606
|
-
const organization
|
|
2607
|
-
if (!organization
|
|
2568
|
+
const organization = await adapter.findOrganizationById(organizationId);
|
|
2569
|
+
if (!organization) return ctx.render({
|
|
2608
2570
|
success: False,
|
|
2609
2571
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2610
2572
|
}, 400);
|
|
2611
2573
|
let memberData = {
|
|
2612
2574
|
organizationId,
|
|
2613
2575
|
userId: user.id,
|
|
2614
|
-
role: parseRoles(role
|
|
2576
|
+
role: parseRoles(role),
|
|
2615
2577
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2616
2578
|
...additionalFields ? additionalFields : {}
|
|
2617
2579
|
};
|
|
@@ -2620,11 +2582,11 @@ const addMember = (option) => {
|
|
|
2620
2582
|
member: {
|
|
2621
2583
|
userId: user.id,
|
|
2622
2584
|
organizationId,
|
|
2623
|
-
role: parseRoles(role
|
|
2585
|
+
role: parseRoles(role),
|
|
2624
2586
|
...additionalFields
|
|
2625
2587
|
},
|
|
2626
2588
|
user,
|
|
2627
|
-
organization
|
|
2589
|
+
organization
|
|
2628
2590
|
});
|
|
2629
2591
|
if (response && typeof response === "object" && "data" in response) memberData = {
|
|
2630
2592
|
...memberData,
|
|
@@ -2639,7 +2601,7 @@ const addMember = (option) => {
|
|
|
2639
2601
|
if (option?.organizationHooks?.afterAddMember) await option?.organizationHooks.afterAddMember({
|
|
2640
2602
|
member: createdMember,
|
|
2641
2603
|
user,
|
|
2642
|
-
organization
|
|
2604
|
+
organization
|
|
2643
2605
|
});
|
|
2644
2606
|
return ctx.render(createdMember, 200);
|
|
2645
2607
|
});
|
|
@@ -2670,7 +2632,7 @@ const removeMember = (option) => createEndpoint(createRoute$1({
|
|
|
2670
2632
|
message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
|
|
2671
2633
|
}, 400);
|
|
2672
2634
|
let toBeRemovedMember = null;
|
|
2673
|
-
if (z
|
|
2635
|
+
if (z.email().safeParse(memberIdOrEmail).success) toBeRemovedMember = await adapter.findMemberByEmail({
|
|
2674
2636
|
email: memberIdOrEmail,
|
|
2675
2637
|
organizationId
|
|
2676
2638
|
});
|
|
@@ -2705,8 +2667,8 @@ const removeMember = (option) => createEndpoint(createRoute$1({
|
|
|
2705
2667
|
success: False,
|
|
2706
2668
|
message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
|
|
2707
2669
|
}, 400);
|
|
2708
|
-
const organization
|
|
2709
|
-
if (!organization
|
|
2670
|
+
const organization = await adapter.findOrganizationById(organizationId);
|
|
2671
|
+
if (!organization) return ctx.render({
|
|
2710
2672
|
status: False,
|
|
2711
2673
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2712
2674
|
}, 400);
|
|
@@ -2718,14 +2680,14 @@ const removeMember = (option) => createEndpoint(createRoute$1({
|
|
|
2718
2680
|
if (option?.organizationHooks?.beforeRemoveMember) await option?.organizationHooks.beforeRemoveMember({
|
|
2719
2681
|
member: toBeRemovedMember,
|
|
2720
2682
|
user: userBeingRemoved,
|
|
2721
|
-
organization
|
|
2683
|
+
organization
|
|
2722
2684
|
});
|
|
2723
2685
|
await adapter.deleteMember(toBeRemovedMember.id);
|
|
2724
2686
|
if (session.user.id === toBeRemovedMember.userId && session.session.activeOrganizationId === toBeRemovedMember.organizationId) await adapter.setActiveOrganization(session.session.token, null);
|
|
2725
2687
|
if (option?.organizationHooks?.afterRemoveMember) await option?.organizationHooks.afterRemoveMember({
|
|
2726
2688
|
member: toBeRemovedMember,
|
|
2727
2689
|
user: userBeingRemoved,
|
|
2728
|
-
organization
|
|
2690
|
+
organization
|
|
2729
2691
|
});
|
|
2730
2692
|
return ctx.render(toBeRemovedMember, 200);
|
|
2731
2693
|
});
|
|
@@ -2739,7 +2701,7 @@ const updateMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2739
2701
|
responses: res(memberSchema.transform(toSuccess)).err(400, "Member not found").err(401, "Member limit reached").err(403, "Not allowed to perform this action").bld()
|
|
2740
2702
|
}), (_authOptions) => async (ctx) => {
|
|
2741
2703
|
const session = ctx.get("session");
|
|
2742
|
-
const { role
|
|
2704
|
+
const { role, memberId, organizationId: requestOrganizationId } = ctx.req.valid("json");
|
|
2743
2705
|
const organizationId = requestOrganizationId ?? session.session.activeOrganizationId;
|
|
2744
2706
|
if (!organizationId) return ctx.render({
|
|
2745
2707
|
success: False,
|
|
@@ -2766,7 +2728,7 @@ const updateMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2766
2728
|
const creatorRole = ctx.get("orgOptions").creatorRole ?? "owner";
|
|
2767
2729
|
const updatingMemberRoles = member.role.split(",");
|
|
2768
2730
|
const toBeUpdatedMemberRoles = toBeUpdatedMember.role.split(",");
|
|
2769
|
-
const roleToSet = Array.isArray(role
|
|
2731
|
+
const roleToSet = Array.isArray(role) ? role : [role];
|
|
2770
2732
|
const isUpdatingCreator = toBeUpdatedMemberRoles.includes(creatorRole);
|
|
2771
2733
|
const updaterIsCreator = updatingMemberRoles.includes(creatorRole);
|
|
2772
2734
|
const isSettingCreatorRole = roleToSet.includes(creatorRole);
|
|
@@ -2782,8 +2744,8 @@ const updateMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2782
2744
|
field: "organizationId",
|
|
2783
2745
|
value: organizationId
|
|
2784
2746
|
}]
|
|
2785
|
-
})).filter((member
|
|
2786
|
-
return member
|
|
2747
|
+
})).filter((member) => {
|
|
2748
|
+
return member.role.split(",").includes(creatorRole);
|
|
2787
2749
|
}).length <= 1 && !isSettingCreatorRole) return ctx.render({
|
|
2788
2750
|
success: False,
|
|
2789
2751
|
message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER
|
|
@@ -2799,8 +2761,8 @@ const updateMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2799
2761
|
success: False,
|
|
2800
2762
|
message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER
|
|
2801
2763
|
}, 403);
|
|
2802
|
-
const organization
|
|
2803
|
-
if (!organization
|
|
2764
|
+
const organization = await adapter.findOrganizationById(organizationId);
|
|
2765
|
+
if (!organization) return ctx.render({
|
|
2804
2766
|
success: False,
|
|
2805
2767
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
2806
2768
|
}, 400);
|
|
@@ -2810,27 +2772,27 @@ const updateMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2810
2772
|
message: "User not found"
|
|
2811
2773
|
}, 400);
|
|
2812
2774
|
const previousRole = toBeUpdatedMember.role;
|
|
2813
|
-
const newRole = parseRoles(role
|
|
2775
|
+
const newRole = parseRoles(role);
|
|
2814
2776
|
if (option?.organizationHooks?.beforeUpdateMemberRole) {
|
|
2815
2777
|
const response = await option?.organizationHooks.beforeUpdateMemberRole({
|
|
2816
2778
|
member: toBeUpdatedMember,
|
|
2817
2779
|
newRole,
|
|
2818
2780
|
user: userBeingUpdated,
|
|
2819
|
-
organization
|
|
2781
|
+
organization
|
|
2820
2782
|
});
|
|
2821
2783
|
if (response && typeof response === "object" && "data" in response) {
|
|
2822
|
-
const updatedMember
|
|
2823
|
-
if (!updatedMember
|
|
2784
|
+
const updatedMember = await adapter.updateMember(memberId, response.data.role ?? newRole);
|
|
2785
|
+
if (!updatedMember) return ctx.render({
|
|
2824
2786
|
success: False,
|
|
2825
2787
|
message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND
|
|
2826
2788
|
}, 400);
|
|
2827
2789
|
if (option?.organizationHooks?.afterUpdateMemberRole) await option?.organizationHooks.afterUpdateMemberRole({
|
|
2828
|
-
member: updatedMember
|
|
2790
|
+
member: updatedMember,
|
|
2829
2791
|
previousRole,
|
|
2830
2792
|
user: userBeingUpdated,
|
|
2831
|
-
organization
|
|
2793
|
+
organization
|
|
2832
2794
|
});
|
|
2833
|
-
return ctx.render(updatedMember
|
|
2795
|
+
return ctx.render(updatedMember, 200);
|
|
2834
2796
|
}
|
|
2835
2797
|
}
|
|
2836
2798
|
const updatedMember = await adapter.updateMember(memberId, newRole);
|
|
@@ -2842,7 +2804,7 @@ const updateMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2842
2804
|
member: updatedMember,
|
|
2843
2805
|
previousRole,
|
|
2844
2806
|
user: userBeingUpdated,
|
|
2845
|
-
organization
|
|
2807
|
+
organization
|
|
2846
2808
|
});
|
|
2847
2809
|
return ctx.render(updatedMember, 200);
|
|
2848
2810
|
});
|
|
@@ -2956,11 +2918,11 @@ const getActiveMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2956
2918
|
method: "get",
|
|
2957
2919
|
path: "/organization/get-active-member-role",
|
|
2958
2920
|
middleware: [requestOnlySessionMiddleware, orgMiddleware(option)],
|
|
2959
|
-
request: req().qry(z
|
|
2960
|
-
userId: z
|
|
2961
|
-
organizationId: z
|
|
2921
|
+
request: req().qry(z.object({
|
|
2922
|
+
userId: z.string().openapi({ description: "The user ID to get the role for. If not provided, will default to the current user's" }).optional(),
|
|
2923
|
+
organizationId: z.string().openapi({ description: "The organization ID to list members for. If not provided, will default to the user's active organization. Eg: \"organization-id\"" }).optional()
|
|
2962
2924
|
})).bld(),
|
|
2963
|
-
responses: res(z
|
|
2925
|
+
responses: res(z.string().transform(toSuccess)).err(400, "No active organization").err(403, "Not member of organization").bld()
|
|
2964
2926
|
}), (_authOptions) => async (ctx) => {
|
|
2965
2927
|
let { userId, organizationId } = ctx.req.valid("query");
|
|
2966
2928
|
const session = userId == null || organizationId == null ? ctx.get("session") ?? null : null;
|
|
@@ -2980,9 +2942,7 @@ const getActiveMemberRole = (option) => createEndpoint(createRoute$1({
|
|
|
2980
2942
|
}, 403);
|
|
2981
2943
|
return ctx.render(member.role, 200);
|
|
2982
2944
|
});
|
|
2983
|
-
|
|
2984
|
-
//#endregion
|
|
2985
|
-
//#region src/plugins/organization/routes/crud-org.ts
|
|
2945
|
+
// src/plugins/organization/routes/crud-org.ts
|
|
2986
2946
|
const createOrganization = (options) => {
|
|
2987
2947
|
const additionalFieldsSchema = toZodSchema({
|
|
2988
2948
|
fields: options?.schema?.organization?.additionalFields ?? {},
|
|
@@ -2999,7 +2959,7 @@ const createOrganization = (options) => {
|
|
|
2999
2959
|
responses: res(organizationWithMembersSchema.transform(toSuccess)).err(400).err(401).err(403).bld()
|
|
3000
2960
|
}), (_authOptions) => async (ctx) => {
|
|
3001
2961
|
const session = ctx.get("session");
|
|
3002
|
-
let { name, slug, userId, logo, metadata, keepCurrentActiveOrganization
|
|
2962
|
+
let { name, slug, userId, logo, metadata, keepCurrentActiveOrganization, ...additionalFields } = ctx.req.valid("json");
|
|
3003
2963
|
if (userId && !ctx.get("isServer")) return ctx.render({
|
|
3004
2964
|
success: False,
|
|
3005
2965
|
message: "userId is a server-only field"
|
|
@@ -3047,7 +3007,7 @@ const createOrganization = (options) => {
|
|
|
3047
3007
|
});
|
|
3048
3008
|
if (response && typeof response === "object" && "data" in response) hookResponse = response;
|
|
3049
3009
|
}
|
|
3050
|
-
const organization
|
|
3010
|
+
const organization = await adapter.createOrganization({ organization: {
|
|
3051
3011
|
...orgData,
|
|
3052
3012
|
createdAt: /* @__PURE__ */ new Date(),
|
|
3053
3013
|
...hookResponse?.data || {}
|
|
@@ -3055,15 +3015,15 @@ const createOrganization = (options) => {
|
|
|
3055
3015
|
let member;
|
|
3056
3016
|
let teamMember = null;
|
|
3057
3017
|
if (orgOptions?.teams?.enabled === true && orgOptions.teams.defaultTeam?.enabled !== false) {
|
|
3058
|
-
const defaultTeam = await orgOptions.teams.defaultTeam?.customCreateDefaultTeam?.(organization
|
|
3059
|
-
organizationId: organization
|
|
3060
|
-
name: `${organization
|
|
3018
|
+
const defaultTeam = await orgOptions.teams.defaultTeam?.customCreateDefaultTeam?.(organization, ctx.req) ?? await adapter.createTeam({
|
|
3019
|
+
organizationId: organization.id,
|
|
3020
|
+
name: `${organization.name}`,
|
|
3061
3021
|
createdAt: /* @__PURE__ */ new Date(),
|
|
3062
3022
|
updatedAt: /* @__PURE__ */ new Date()
|
|
3063
3023
|
});
|
|
3064
3024
|
member = await adapter.createMember({
|
|
3065
3025
|
userId: user.id,
|
|
3066
|
-
organizationId: organization
|
|
3026
|
+
organizationId: organization.id,
|
|
3067
3027
|
role: orgOptions.creatorRole ?? "owner"
|
|
3068
3028
|
});
|
|
3069
3029
|
teamMember = await adapter.findOrCreateTeamMember({
|
|
@@ -3072,19 +3032,19 @@ const createOrganization = (options) => {
|
|
|
3072
3032
|
});
|
|
3073
3033
|
} else member = await adapter.createMember({
|
|
3074
3034
|
userId: user.id,
|
|
3075
|
-
organizationId: organization
|
|
3035
|
+
organizationId: organization.id,
|
|
3076
3036
|
role: orgOptions.creatorRole ?? "owner"
|
|
3077
3037
|
});
|
|
3078
3038
|
if (orgOptions.organizationHooks?.afterCreateOrganization) await orgOptions.organizationHooks.afterCreateOrganization({
|
|
3079
|
-
organization
|
|
3039
|
+
organization,
|
|
3080
3040
|
user,
|
|
3081
3041
|
member
|
|
3082
3042
|
});
|
|
3083
|
-
if (session && !keepCurrentActiveOrganization) await adapter.setActiveOrganization(session.session.token, organization
|
|
3043
|
+
if (session && !keepCurrentActiveOrganization) await adapter.setActiveOrganization(session.session.token, organization.id);
|
|
3084
3044
|
if (teamMember && session && !keepCurrentActiveOrganization) await adapter.setActiveTeam(session.session.token, teamMember.teamId);
|
|
3085
3045
|
return ctx.render({
|
|
3086
|
-
...organization
|
|
3087
|
-
metadata: organization
|
|
3046
|
+
...organization,
|
|
3047
|
+
metadata: organization.metadata && typeof organization.metadata === "string" ? JSON.parse(organization.metadata) : organization.metadata,
|
|
3088
3048
|
members: [member]
|
|
3089
3049
|
}, 200);
|
|
3090
3050
|
});
|
|
@@ -3224,24 +3184,24 @@ const getFullOrganization = (options) => {
|
|
|
3224
3184
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
3225
3185
|
}, 400);
|
|
3226
3186
|
const adapter = getOrgAdapter(ctx.get("context"), options);
|
|
3227
|
-
const organization
|
|
3187
|
+
const organization = await adapter.findFullOrganization({
|
|
3228
3188
|
organizationId: orgId,
|
|
3229
3189
|
isSlug: !!organizationSlug,
|
|
3230
3190
|
...membersLimit && { membersLimit },
|
|
3231
3191
|
...ctx.get("orgOptions").teams?.enabled === true && { includeTeams: ctx.get("orgOptions").teams.enabled }
|
|
3232
3192
|
});
|
|
3233
|
-
if (!organization
|
|
3193
|
+
if (!organization) return ctx.render({
|
|
3234
3194
|
success: False,
|
|
3235
3195
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
3236
3196
|
}, 400);
|
|
3237
|
-
if (!organization
|
|
3197
|
+
if (!organization.members.find((member) => member.userId === session.user.id)) {
|
|
3238
3198
|
await adapter.setActiveOrganization(session.session.token, null);
|
|
3239
3199
|
return ctx.render({
|
|
3240
3200
|
success: False,
|
|
3241
3201
|
message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
|
|
3242
3202
|
}, 403);
|
|
3243
3203
|
}
|
|
3244
|
-
return ctx.render(organization
|
|
3204
|
+
return ctx.render(organization, 200);
|
|
3245
3205
|
});
|
|
3246
3206
|
};
|
|
3247
3207
|
const setActiveOrganization = (options) => createEndpoint(createRoute$1({
|
|
@@ -3258,8 +3218,9 @@ const setActiveOrganization = (options) => createEndpoint(createRoute$1({
|
|
|
3258
3218
|
let { organizationId, organizationSlug } = ctx.req.valid("json");
|
|
3259
3219
|
if (organizationId === null) {
|
|
3260
3220
|
if (!session.session.activeOrganizationId) return ctx.render(null, 200);
|
|
3221
|
+
const updatedSession = await adapter.setActiveOrganization(session.session.token, null);
|
|
3261
3222
|
await setSessionCookie(ctx, authOptions, {
|
|
3262
|
-
session:
|
|
3223
|
+
session: updatedSession,
|
|
3263
3224
|
user: session.user
|
|
3264
3225
|
});
|
|
3265
3226
|
return ctx.render(null, 200);
|
|
@@ -3273,12 +3234,12 @@ const setActiveOrganization = (options) => createEndpoint(createRoute$1({
|
|
|
3273
3234
|
organizationId = sessionOrgId;
|
|
3274
3235
|
}
|
|
3275
3236
|
if (organizationSlug !== void 0 && organizationId === void 0) {
|
|
3276
|
-
const organization
|
|
3277
|
-
if (!organization
|
|
3237
|
+
const organization = await adapter.findOrganizationBySlug(organizationSlug);
|
|
3238
|
+
if (!organization) return ctx.render({
|
|
3278
3239
|
success: False,
|
|
3279
3240
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
3280
3241
|
}, 400);
|
|
3281
|
-
organizationId = organization
|
|
3242
|
+
organizationId = organization.id;
|
|
3282
3243
|
}
|
|
3283
3244
|
if (organizationId === void 0) return ctx.render({
|
|
3284
3245
|
success: False,
|
|
@@ -3294,16 +3255,17 @@ const setActiveOrganization = (options) => createEndpoint(createRoute$1({
|
|
|
3294
3255
|
message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION
|
|
3295
3256
|
}, 403);
|
|
3296
3257
|
}
|
|
3297
|
-
let organization
|
|
3298
|
-
if (!organization
|
|
3258
|
+
let organization = await adapter.findOrganizationById(organizationId);
|
|
3259
|
+
if (!organization) return ctx.render({
|
|
3299
3260
|
success: False,
|
|
3300
3261
|
message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND
|
|
3301
3262
|
}, 400);
|
|
3263
|
+
const updatedSession = await adapter.setActiveOrganization(session.session.token, organization.id);
|
|
3302
3264
|
await setSessionCookie(ctx, authOptions, {
|
|
3303
|
-
session:
|
|
3265
|
+
session: updatedSession,
|
|
3304
3266
|
user: session.user
|
|
3305
3267
|
});
|
|
3306
|
-
return ctx.render(organization
|
|
3268
|
+
return ctx.render(organization, 200);
|
|
3307
3269
|
});
|
|
3308
3270
|
const listOrganizations = (options) => createEndpoint(createRoute$1({
|
|
3309
3271
|
operationId: "listOrganizations",
|
|
@@ -3318,9 +3280,7 @@ const listOrganizations = (options) => createEndpoint(createRoute$1({
|
|
|
3318
3280
|
const organizations = await adapter.listOrganizations(session.user.id);
|
|
3319
3281
|
return ctx.render(organizations, 200);
|
|
3320
3282
|
});
|
|
3321
|
-
|
|
3322
|
-
//#endregion
|
|
3323
|
-
//#region src/plugins/organization/routes/crud-team.ts
|
|
3283
|
+
// src/plugins/organization/routes/crud-team.ts
|
|
3324
3284
|
const createTeam = (options) => {
|
|
3325
3285
|
const additionalFieldsSchema = toZodSchema({
|
|
3326
3286
|
fields: options?.schema?.team?.additionalFields ?? {},
|
|
@@ -3337,7 +3297,7 @@ const createTeam = (options) => {
|
|
|
3337
3297
|
responses: res(teamSchema.transform(toSuccess), "Team created successfully").err(400).err(401).err(403).bld()
|
|
3338
3298
|
}), (_authOptions) => async (ctx) => {
|
|
3339
3299
|
const session = ctx.get("session") ?? null;
|
|
3340
|
-
const { name, organizationId = session?.session.activeOrganizationId
|
|
3300
|
+
const { name, organizationId = session?.session.activeOrganizationId, ...additionalFields } = ctx.req.valid("json");
|
|
3341
3301
|
if (!organizationId) return ctx.render({
|
|
3342
3302
|
success: False,
|
|
3343
3303
|
message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
|
|
@@ -3452,7 +3412,7 @@ const updateTeam = (options) => {
|
|
|
3452
3412
|
responses: res(teamSchema.transform(toSuccess), "Team updated successfully").err(400).err(401).err(403).bld()
|
|
3453
3413
|
}), (_authOptions) => async (ctx) => {
|
|
3454
3414
|
const session = ctx.get("session");
|
|
3455
|
-
const { teamId, data: { name, organizationId = session.session.activeOrganizationId
|
|
3415
|
+
const { teamId, data: { name, organizationId = session.session.activeOrganizationId, ...additionalFields } } = ctx.req.valid("json");
|
|
3456
3416
|
if (!organizationId) return ctx.render({
|
|
3457
3417
|
success: False,
|
|
3458
3418
|
message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION
|
|
@@ -3531,8 +3491,9 @@ const setActiveTeam = (options) => createEndpoint(createRoute$1({
|
|
|
3531
3491
|
let { teamId } = ctx.req.valid("json");
|
|
3532
3492
|
if (teamId === null) {
|
|
3533
3493
|
if (!session.session.activeTeamId) return ctx.render(null, 200);
|
|
3494
|
+
const updatedSession = await adapter.setActiveTeam(session.session.token, null);
|
|
3534
3495
|
await setSessionCookie(ctx, authOptions, {
|
|
3535
|
-
session:
|
|
3496
|
+
session: updatedSession,
|
|
3536
3497
|
user: session.user
|
|
3537
3498
|
});
|
|
3538
3499
|
return ctx.render(null, 200);
|
|
@@ -3557,8 +3518,9 @@ const setActiveTeam = (options) => createEndpoint(createRoute$1({
|
|
|
3557
3518
|
success: False,
|
|
3558
3519
|
message: ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM
|
|
3559
3520
|
}, 403);
|
|
3521
|
+
const updatedSession = await adapter.setActiveTeam(session.session.token, team.id);
|
|
3560
3522
|
await setSessionCookie(ctx, authOptions, {
|
|
3561
|
-
session:
|
|
3523
|
+
session: updatedSession,
|
|
3562
3524
|
user: session.user
|
|
3563
3525
|
});
|
|
3564
3526
|
return ctx.render(team, 200);
|
|
@@ -3698,9 +3660,7 @@ const removeTeamMember = (options) => createEndpoint(createRoute$1({
|
|
|
3698
3660
|
message: "Team member removed successfully."
|
|
3699
3661
|
}, 200);
|
|
3700
3662
|
});
|
|
3701
|
-
|
|
3702
|
-
//#endregion
|
|
3703
|
-
//#region src/plugins/organization/organization.ts
|
|
3663
|
+
// src/plugins/organization/organization.ts
|
|
3704
3664
|
function parseRoles(roles) {
|
|
3705
3665
|
return Array.isArray(roles) ? roles.join(",") : roles;
|
|
3706
3666
|
}
|
|
@@ -3711,19 +3671,19 @@ const createHasPermission = (options) => {
|
|
|
3711
3671
|
path: "/organization/has-permission",
|
|
3712
3672
|
description: "Check if the user has permission",
|
|
3713
3673
|
middleware: [orgSessionMiddleware],
|
|
3714
|
-
request: req().bdy(z
|
|
3715
|
-
permission: z
|
|
3674
|
+
request: req().bdy(z.object({ organizationId: z.string().optional() }).and(z.union([z.object({
|
|
3675
|
+
permission: z.record(z.string(), z.array(z.string())).openapi({
|
|
3716
3676
|
description: "The permission to check",
|
|
3717
3677
|
deprecated: true
|
|
3718
3678
|
}),
|
|
3719
|
-
permissions: z
|
|
3720
|
-
}), z
|
|
3721
|
-
permission: z
|
|
3722
|
-
permissions: z
|
|
3679
|
+
permissions: z.undefined().optional()
|
|
3680
|
+
}), z.object({
|
|
3681
|
+
permission: z.undefined().optional(),
|
|
3682
|
+
permissions: z.record(z.string(), z.array(z.string())).openapi({ description: "The permission to check" })
|
|
3723
3683
|
})]))).bld(),
|
|
3724
|
-
responses: res(z
|
|
3725
|
-
success: z
|
|
3726
|
-
data: z
|
|
3684
|
+
responses: res(z.object({
|
|
3685
|
+
success: z.literal(true),
|
|
3686
|
+
data: z.boolean()
|
|
3727
3687
|
})).err(400, "No permissions were provided").err(401, "User not member of organization").bld()
|
|
3728
3688
|
}), (_authOptions) => async (ctx) => {
|
|
3729
3689
|
const { organizationId, permission, permissions } = ctx.req.valid("json");
|
|
@@ -3754,38 +3714,486 @@ const createHasPermission = (options) => {
|
|
|
3754
3714
|
};
|
|
3755
3715
|
function organization(options) {
|
|
3756
3716
|
let endpoints = {
|
|
3717
|
+
/**
|
|
3718
|
+
* ### Endpoint
|
|
3719
|
+
*
|
|
3720
|
+
* POST `/organization/create`
|
|
3721
|
+
*
|
|
3722
|
+
* ### API Methods
|
|
3723
|
+
*
|
|
3724
|
+
* **server:**
|
|
3725
|
+
* `auth.api.createOrganization`
|
|
3726
|
+
*
|
|
3727
|
+
* **client:**
|
|
3728
|
+
* `authClient.organization.create`
|
|
3729
|
+
*
|
|
3730
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-create)
|
|
3731
|
+
*/
|
|
3757
3732
|
createOrganization: createOrganization(options),
|
|
3733
|
+
/**
|
|
3734
|
+
* ### Endpoint
|
|
3735
|
+
*
|
|
3736
|
+
* POST `/organization/update`
|
|
3737
|
+
*
|
|
3738
|
+
* ### API Methods
|
|
3739
|
+
*
|
|
3740
|
+
* **server:**
|
|
3741
|
+
* `auth.api.updateOrganization`
|
|
3742
|
+
*
|
|
3743
|
+
* **client:**
|
|
3744
|
+
* `authClient.organization.update`
|
|
3745
|
+
*
|
|
3746
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-update)
|
|
3747
|
+
*/
|
|
3758
3748
|
updateOrganization: updateOrganization(options),
|
|
3749
|
+
/**
|
|
3750
|
+
* ### Endpoint
|
|
3751
|
+
*
|
|
3752
|
+
* POST `/organization/delete`
|
|
3753
|
+
*
|
|
3754
|
+
* ### API Methods
|
|
3755
|
+
*
|
|
3756
|
+
* **server:**
|
|
3757
|
+
* `auth.api.deleteOrganization`
|
|
3758
|
+
*
|
|
3759
|
+
* **client:**
|
|
3760
|
+
* `authClient.organization.delete`
|
|
3761
|
+
*
|
|
3762
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-delete)
|
|
3763
|
+
*/
|
|
3759
3764
|
deleteOrganization: deleteOrganization(options),
|
|
3765
|
+
/**
|
|
3766
|
+
* ### Endpoint
|
|
3767
|
+
*
|
|
3768
|
+
* POST `/organization/set-active`
|
|
3769
|
+
*
|
|
3770
|
+
* ### API Methods
|
|
3771
|
+
*
|
|
3772
|
+
* **server:**
|
|
3773
|
+
* `auth.api.setActiveOrganization`
|
|
3774
|
+
*
|
|
3775
|
+
* **client:**
|
|
3776
|
+
* `authClient.organization.setActive`
|
|
3777
|
+
*
|
|
3778
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-set-active)
|
|
3779
|
+
*/
|
|
3760
3780
|
setActiveOrganization: setActiveOrganization(options),
|
|
3781
|
+
/**
|
|
3782
|
+
* ### Endpoint
|
|
3783
|
+
*
|
|
3784
|
+
* GET `/organization/get-full-organization`
|
|
3785
|
+
*
|
|
3786
|
+
* ### API Methods
|
|
3787
|
+
*
|
|
3788
|
+
* **server:**
|
|
3789
|
+
* `auth.api.getFullOrganization`
|
|
3790
|
+
*
|
|
3791
|
+
* **client:**
|
|
3792
|
+
* `authClient.organization.getFullOrganization`
|
|
3793
|
+
*
|
|
3794
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-get-full-organization)
|
|
3795
|
+
*/
|
|
3761
3796
|
getFullOrganization: getFullOrganization(options),
|
|
3797
|
+
/**
|
|
3798
|
+
* ### Endpoint
|
|
3799
|
+
*
|
|
3800
|
+
* GET `/organization/list`
|
|
3801
|
+
*
|
|
3802
|
+
* ### API Methods
|
|
3803
|
+
*
|
|
3804
|
+
* **server:**
|
|
3805
|
+
* `auth.api.listOrganizations`
|
|
3806
|
+
*
|
|
3807
|
+
* **client:**
|
|
3808
|
+
* `authClient.organization.list`
|
|
3809
|
+
*
|
|
3810
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-list)
|
|
3811
|
+
*/
|
|
3762
3812
|
listOrganizations: listOrganizations(options),
|
|
3813
|
+
/**
|
|
3814
|
+
* ### Endpoint
|
|
3815
|
+
*
|
|
3816
|
+
* POST `/organization/invite-member`
|
|
3817
|
+
*
|
|
3818
|
+
* ### API Methods
|
|
3819
|
+
*
|
|
3820
|
+
* **server:**
|
|
3821
|
+
* `auth.api.createInvitation`
|
|
3822
|
+
*
|
|
3823
|
+
* **client:**
|
|
3824
|
+
* `authClient.organization.inviteMember`
|
|
3825
|
+
*
|
|
3826
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-invite-member)
|
|
3827
|
+
*/
|
|
3763
3828
|
createInvitation: createInvitation(options),
|
|
3829
|
+
/**
|
|
3830
|
+
* ### Endpoint
|
|
3831
|
+
*
|
|
3832
|
+
* POST `/organization/cancel-invitation`
|
|
3833
|
+
*
|
|
3834
|
+
* ### API Methods
|
|
3835
|
+
*
|
|
3836
|
+
* **server:**
|
|
3837
|
+
* `auth.api.cancelInvitation`
|
|
3838
|
+
*
|
|
3839
|
+
* **client:**
|
|
3840
|
+
* `authClient.organization.cancelInvitation`
|
|
3841
|
+
*
|
|
3842
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-cancel-invitation)
|
|
3843
|
+
*/
|
|
3764
3844
|
cancelInvitation: cancelInvitation(options),
|
|
3845
|
+
/**
|
|
3846
|
+
* ### Endpoint
|
|
3847
|
+
*
|
|
3848
|
+
* POST `/organization/accept-invitation`
|
|
3849
|
+
*
|
|
3850
|
+
* ### API Methods
|
|
3851
|
+
*
|
|
3852
|
+
* **server:**
|
|
3853
|
+
* `auth.api.acceptInvitation`
|
|
3854
|
+
*
|
|
3855
|
+
* **client:**
|
|
3856
|
+
* `authClient.organization.acceptInvitation`
|
|
3857
|
+
*
|
|
3858
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-accept-invitation)
|
|
3859
|
+
*/
|
|
3765
3860
|
acceptInvitation: acceptInvitation(options),
|
|
3861
|
+
/**
|
|
3862
|
+
* ### Endpoint
|
|
3863
|
+
*
|
|
3864
|
+
* GET `/organization/get-invitation`
|
|
3865
|
+
*
|
|
3866
|
+
* ### API Methods
|
|
3867
|
+
*
|
|
3868
|
+
* **server:**
|
|
3869
|
+
* `auth.api.getInvitation`
|
|
3870
|
+
*
|
|
3871
|
+
* **client:**
|
|
3872
|
+
* `authClient.organization.getInvitation`
|
|
3873
|
+
*
|
|
3874
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-get-invitation)
|
|
3875
|
+
*/
|
|
3766
3876
|
getInvitation: getInvitation(options),
|
|
3877
|
+
/**
|
|
3878
|
+
* ### Endpoint
|
|
3879
|
+
*
|
|
3880
|
+
* POST `/organization/reject-invitation`
|
|
3881
|
+
*
|
|
3882
|
+
* ### API Methods
|
|
3883
|
+
*
|
|
3884
|
+
* **server:**
|
|
3885
|
+
* `auth.api.rejectInvitation`
|
|
3886
|
+
*
|
|
3887
|
+
* **client:**
|
|
3888
|
+
* `authClient.organization.rejectInvitation`
|
|
3889
|
+
*
|
|
3890
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-reject-invitation)
|
|
3891
|
+
*/
|
|
3767
3892
|
rejectInvitation: rejectInvitation(options),
|
|
3893
|
+
/**
|
|
3894
|
+
* ### Endpoint
|
|
3895
|
+
*
|
|
3896
|
+
* GET `/organization/list-invitations`
|
|
3897
|
+
*
|
|
3898
|
+
* ### API Methods
|
|
3899
|
+
*
|
|
3900
|
+
* **server:**
|
|
3901
|
+
* `auth.api.listInvitations`
|
|
3902
|
+
*
|
|
3903
|
+
* **client:**
|
|
3904
|
+
* `authClient.organization.listInvitations`
|
|
3905
|
+
*
|
|
3906
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-list-invitations)
|
|
3907
|
+
*/
|
|
3768
3908
|
listInvitations: listInvitations(options),
|
|
3909
|
+
/**
|
|
3910
|
+
* ### Endpoint
|
|
3911
|
+
*
|
|
3912
|
+
* GET `/organization/get-active-member`
|
|
3913
|
+
*
|
|
3914
|
+
* ### API Methods
|
|
3915
|
+
*
|
|
3916
|
+
* **server:**
|
|
3917
|
+
* `auth.api.getActiveMember`
|
|
3918
|
+
*
|
|
3919
|
+
* **client:**
|
|
3920
|
+
* `authClient.organization.getActiveMember`
|
|
3921
|
+
*
|
|
3922
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-get-active-member)
|
|
3923
|
+
*/
|
|
3769
3924
|
getActiveMember: getActiveMember(options),
|
|
3925
|
+
/**
|
|
3926
|
+
* ### Endpoint
|
|
3927
|
+
*
|
|
3928
|
+
* POST `/organization/check-slug`
|
|
3929
|
+
*
|
|
3930
|
+
* ### API Methods
|
|
3931
|
+
*
|
|
3932
|
+
* **server:**
|
|
3933
|
+
* `auth.api.checkOrganizationSlug`
|
|
3934
|
+
*
|
|
3935
|
+
* **client:**
|
|
3936
|
+
* `authClient.organization.checkSlug`
|
|
3937
|
+
*
|
|
3938
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-check-slug)
|
|
3939
|
+
*/
|
|
3770
3940
|
checkOrganizationSlug: checkOrganizationSlug(options),
|
|
3941
|
+
/**
|
|
3942
|
+
* ### Endpoint
|
|
3943
|
+
*
|
|
3944
|
+
* POST `/organization/add-member`
|
|
3945
|
+
*
|
|
3946
|
+
* ### API Methods
|
|
3947
|
+
*
|
|
3948
|
+
* **server:**
|
|
3949
|
+
* `auth.api.addMember`
|
|
3950
|
+
*
|
|
3951
|
+
* **client:**
|
|
3952
|
+
* `authClient.organization.addMember`
|
|
3953
|
+
*
|
|
3954
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-add-member)
|
|
3955
|
+
*/
|
|
3771
3956
|
addMember: addMember(options),
|
|
3957
|
+
/**
|
|
3958
|
+
* ### Endpoint
|
|
3959
|
+
*
|
|
3960
|
+
* POST `/organization/remove-member`
|
|
3961
|
+
*
|
|
3962
|
+
* ### API Methods
|
|
3963
|
+
*
|
|
3964
|
+
* **server:**
|
|
3965
|
+
* `auth.api.removeMember`
|
|
3966
|
+
*
|
|
3967
|
+
* **client:**
|
|
3968
|
+
* `authClient.organization.removeMember`
|
|
3969
|
+
*
|
|
3970
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-remove-member)
|
|
3971
|
+
*/
|
|
3772
3972
|
removeMember: removeMember(options),
|
|
3973
|
+
/**
|
|
3974
|
+
* ### Endpoint
|
|
3975
|
+
*
|
|
3976
|
+
* POST `/organization/update-member-role`
|
|
3977
|
+
*
|
|
3978
|
+
* ### API Methods
|
|
3979
|
+
*
|
|
3980
|
+
* **server:**
|
|
3981
|
+
* `auth.api.updateMemberRole`
|
|
3982
|
+
*
|
|
3983
|
+
* **client:**
|
|
3984
|
+
* `authClient.organization.updateMemberRole`
|
|
3985
|
+
*
|
|
3986
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-update-member-role)
|
|
3987
|
+
*/
|
|
3773
3988
|
updateMemberRole: updateMemberRole(options),
|
|
3989
|
+
/**
|
|
3990
|
+
* ### Endpoint
|
|
3991
|
+
*
|
|
3992
|
+
* POST `/organization/leave`
|
|
3993
|
+
*
|
|
3994
|
+
* ### API Methods
|
|
3995
|
+
*
|
|
3996
|
+
* **server:**
|
|
3997
|
+
* `auth.api.leaveOrganization`
|
|
3998
|
+
*
|
|
3999
|
+
* **client:**
|
|
4000
|
+
* `authClient.organization.leave`
|
|
4001
|
+
*
|
|
4002
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-leave)
|
|
4003
|
+
*/
|
|
3774
4004
|
leaveOrganization: leaveOrganization(options),
|
|
4005
|
+
/**
|
|
4006
|
+
* ### Endpoint
|
|
4007
|
+
*
|
|
4008
|
+
* GET `/organization/list-members`
|
|
4009
|
+
*
|
|
4010
|
+
* ### API Methods
|
|
4011
|
+
*
|
|
4012
|
+
* **server:**
|
|
4013
|
+
* `auth.api.listMembers`
|
|
4014
|
+
*
|
|
4015
|
+
* **client:**
|
|
4016
|
+
* `authClient.organization.listMembers`
|
|
4017
|
+
*
|
|
4018
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-list-members)
|
|
4019
|
+
*/
|
|
3775
4020
|
listUserInvitations: listUserInvitations(options),
|
|
4021
|
+
/**
|
|
4022
|
+
* ### Endpoint
|
|
4023
|
+
*
|
|
4024
|
+
* GET `/organization/list-members`
|
|
4025
|
+
*
|
|
4026
|
+
* ### API Methods
|
|
4027
|
+
*
|
|
4028
|
+
* **server:**
|
|
4029
|
+
* `auth.api.listMembers`
|
|
4030
|
+
*
|
|
4031
|
+
* **client:**
|
|
4032
|
+
* `authClient.organization.listMembers`
|
|
4033
|
+
*/
|
|
3776
4034
|
listMembers: listMembers(options),
|
|
4035
|
+
/**
|
|
4036
|
+
* ### Endpoint
|
|
4037
|
+
*
|
|
4038
|
+
* GET `/organization/get-active-member-role`
|
|
4039
|
+
*
|
|
4040
|
+
* ### API Methods
|
|
4041
|
+
*
|
|
4042
|
+
* **server:**
|
|
4043
|
+
* `auth.api.getActiveMemberRole`
|
|
4044
|
+
*
|
|
4045
|
+
* **client:**
|
|
4046
|
+
* `authClient.organization.getActiveMemberRole`
|
|
4047
|
+
*
|
|
4048
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-get-active-member-role)
|
|
4049
|
+
*/
|
|
3777
4050
|
getActiveMemberRole: getActiveMemberRole(options)
|
|
3778
4051
|
};
|
|
3779
4052
|
const teamSupport = options?.teams?.enabled === true;
|
|
3780
4053
|
const teamEndpoints = {
|
|
4054
|
+
/**
|
|
4055
|
+
* ### Endpoint
|
|
4056
|
+
*
|
|
4057
|
+
* POST `/organization/create-team`
|
|
4058
|
+
*
|
|
4059
|
+
* ### API Methods
|
|
4060
|
+
*
|
|
4061
|
+
* **server:**
|
|
4062
|
+
* `auth.api.createTeam`
|
|
4063
|
+
*
|
|
4064
|
+
* **client:**
|
|
4065
|
+
* `authClient.organization.createTeam`
|
|
4066
|
+
*
|
|
4067
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-create-team)
|
|
4068
|
+
*/
|
|
3781
4069
|
createTeam: createTeam(options),
|
|
4070
|
+
/**
|
|
4071
|
+
* ### Endpoint
|
|
4072
|
+
*
|
|
4073
|
+
* GET `/organization/list-teams`
|
|
4074
|
+
*
|
|
4075
|
+
* ### API Methods
|
|
4076
|
+
*
|
|
4077
|
+
* **server:**
|
|
4078
|
+
* `auth.api.listOrganizationTeams`
|
|
4079
|
+
*
|
|
4080
|
+
* **client:**
|
|
4081
|
+
* `authClient.organization.listTeams`
|
|
4082
|
+
*
|
|
4083
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-list-teams)
|
|
4084
|
+
*/
|
|
3782
4085
|
listOrganizationTeams: listOrganizationTeams(options),
|
|
4086
|
+
/**
|
|
4087
|
+
* ### Endpoint
|
|
4088
|
+
*
|
|
4089
|
+
* POST `/organization/remove-team`
|
|
4090
|
+
*
|
|
4091
|
+
* ### API Methods
|
|
4092
|
+
*
|
|
4093
|
+
* **server:**
|
|
4094
|
+
* `auth.api.removeTeam`
|
|
4095
|
+
*
|
|
4096
|
+
* **client:**
|
|
4097
|
+
* `authClient.organization.removeTeam`
|
|
4098
|
+
*
|
|
4099
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-remove-team)
|
|
4100
|
+
*/
|
|
3783
4101
|
removeTeam: removeTeam(options),
|
|
4102
|
+
/**
|
|
4103
|
+
* ### Endpoint
|
|
4104
|
+
*
|
|
4105
|
+
* POST `/organization/update-team`
|
|
4106
|
+
*
|
|
4107
|
+
* ### API Methods
|
|
4108
|
+
*
|
|
4109
|
+
* **server:**
|
|
4110
|
+
* `auth.api.updateTeam`
|
|
4111
|
+
*
|
|
4112
|
+
* **client:**
|
|
4113
|
+
* `authClient.organization.updateTeam`
|
|
4114
|
+
*
|
|
4115
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-method-organization-update-team)
|
|
4116
|
+
*/
|
|
3784
4117
|
updateTeam: updateTeam(options),
|
|
4118
|
+
/**
|
|
4119
|
+
* ### Endpoint
|
|
4120
|
+
*
|
|
4121
|
+
* POST `/organization/set-active-team`
|
|
4122
|
+
*
|
|
4123
|
+
* ### API Methods
|
|
4124
|
+
*
|
|
4125
|
+
* **server:**
|
|
4126
|
+
* `auth.api.setActiveTeam`
|
|
4127
|
+
*
|
|
4128
|
+
* **client:**
|
|
4129
|
+
* `authClient.organization.setActiveTeam`
|
|
4130
|
+
*
|
|
4131
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-set-active-team)
|
|
4132
|
+
*/
|
|
3785
4133
|
setActiveTeam: setActiveTeam(options),
|
|
4134
|
+
/**
|
|
4135
|
+
* ### Endpoint
|
|
4136
|
+
*
|
|
4137
|
+
* GET `/organization/list-user-teams`
|
|
4138
|
+
*
|
|
4139
|
+
* ### API Methods
|
|
4140
|
+
*
|
|
4141
|
+
* **server:**
|
|
4142
|
+
* `auth.api.listUserTeams`
|
|
4143
|
+
*
|
|
4144
|
+
* **client:**
|
|
4145
|
+
* `authClient.organization.listUserTeams`
|
|
4146
|
+
*
|
|
4147
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-set-active-team)
|
|
4148
|
+
*/
|
|
3786
4149
|
listUserTeams: listUserTeams(options),
|
|
4150
|
+
/**
|
|
4151
|
+
* ### Endpoint
|
|
4152
|
+
*
|
|
4153
|
+
* POST `/organization/list-team-members`
|
|
4154
|
+
*
|
|
4155
|
+
* ### API Methods
|
|
4156
|
+
*
|
|
4157
|
+
* **server:**
|
|
4158
|
+
* `auth.api.listTeamMembers`
|
|
4159
|
+
*
|
|
4160
|
+
* **client:**
|
|
4161
|
+
* `authClient.organization.listTeamMembers`
|
|
4162
|
+
*
|
|
4163
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-set-active-team)
|
|
4164
|
+
*/
|
|
3787
4165
|
listTeamMembers: listTeamMembers(options),
|
|
4166
|
+
/**
|
|
4167
|
+
* ### Endpoint
|
|
4168
|
+
*
|
|
4169
|
+
* POST `/organization/add-team-member`
|
|
4170
|
+
*
|
|
4171
|
+
* ### API Methods
|
|
4172
|
+
*
|
|
4173
|
+
* **server:**
|
|
4174
|
+
* `auth.api.addTeamMember`
|
|
4175
|
+
*
|
|
4176
|
+
* **client:**
|
|
4177
|
+
* `authClient.organization.addTeamMember`
|
|
4178
|
+
*
|
|
4179
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-add-team-member)
|
|
4180
|
+
*/
|
|
3788
4181
|
addTeamMember: addTeamMember(options),
|
|
4182
|
+
/**
|
|
4183
|
+
* ### Endpoint
|
|
4184
|
+
*
|
|
4185
|
+
* POST `/organization/remove-team-member`
|
|
4186
|
+
*
|
|
4187
|
+
* ### API Methods
|
|
4188
|
+
*
|
|
4189
|
+
* **server:**
|
|
4190
|
+
* `auth.api.removeTeamMember`
|
|
4191
|
+
*
|
|
4192
|
+
* **client:**
|
|
4193
|
+
* `authClient.organization.removeTeamMember`
|
|
4194
|
+
*
|
|
4195
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/organization#api-remove-team-member)
|
|
4196
|
+
*/
|
|
3789
4197
|
removeTeamMember: removeTeamMember(options)
|
|
3790
4198
|
};
|
|
3791
4199
|
if (teamSupport) endpoints = {
|
|
@@ -3804,7 +4212,7 @@ function organization(options) {
|
|
|
3804
4212
|
...endpoints,
|
|
3805
4213
|
...dynamicAccessControlEndpoints
|
|
3806
4214
|
};
|
|
3807
|
-
const teamSchema
|
|
4215
|
+
const teamSchema = teamSupport ? {
|
|
3808
4216
|
team: {
|
|
3809
4217
|
modelName: options?.schema?.team?.modelName,
|
|
3810
4218
|
fields: {
|
|
@@ -3865,7 +4273,7 @@ function organization(options) {
|
|
|
3865
4273
|
}
|
|
3866
4274
|
}
|
|
3867
4275
|
} : {};
|
|
3868
|
-
const organizationRoleSchema
|
|
4276
|
+
const organizationRoleSchema = options?.dynamicAccessControl?.enabled ? { organizationRole: {
|
|
3869
4277
|
fields: {
|
|
3870
4278
|
organizationId: {
|
|
3871
4279
|
type: "string",
|
|
@@ -3902,7 +4310,7 @@ function organization(options) {
|
|
|
3902
4310
|
},
|
|
3903
4311
|
modelName: options?.schema?.organizationRole?.modelName
|
|
3904
4312
|
} } : {};
|
|
3905
|
-
const schema
|
|
4313
|
+
const schema = {
|
|
3906
4314
|
organization: {
|
|
3907
4315
|
modelName: options?.schema?.organization?.modelName,
|
|
3908
4316
|
fields: {
|
|
@@ -3937,8 +4345,8 @@ function organization(options) {
|
|
|
3937
4345
|
...options?.schema?.organization?.additionalFields || {}
|
|
3938
4346
|
}
|
|
3939
4347
|
},
|
|
3940
|
-
...organizationRoleSchema
|
|
3941
|
-
...teamSchema
|
|
4348
|
+
...organizationRoleSchema,
|
|
4349
|
+
...teamSchema,
|
|
3942
4350
|
member: {
|
|
3943
4351
|
modelName: options?.schema?.member?.modelName,
|
|
3944
4352
|
fields: {
|
|
@@ -4043,7 +4451,7 @@ function organization(options) {
|
|
|
4043
4451
|
hasPermission: createHasPermission(options)
|
|
4044
4452
|
},
|
|
4045
4453
|
schema: {
|
|
4046
|
-
...schema
|
|
4454
|
+
...schema,
|
|
4047
4455
|
session: { fields: {
|
|
4048
4456
|
activeOrganizationId: {
|
|
4049
4457
|
type: "string",
|
|
@@ -4069,9 +4477,7 @@ function organization(options) {
|
|
|
4069
4477
|
options
|
|
4070
4478
|
};
|
|
4071
4479
|
}
|
|
4072
|
-
|
|
4073
|
-
//#endregion
|
|
4074
|
-
//#region src/plugins/two-factor/error-code.ts
|
|
4480
|
+
// src/plugins/two-factor/error-code.ts
|
|
4075
4481
|
const TWO_FACTOR_ERROR_CODES = {
|
|
4076
4482
|
OTP_NOT_ENABLED: "OTP not enabled",
|
|
4077
4483
|
OTP_HAS_EXPIRED: "OTP has expired",
|
|
@@ -4083,39 +4489,55 @@ const TWO_FACTOR_ERROR_CODES = {
|
|
|
4083
4489
|
TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.",
|
|
4084
4490
|
INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie"
|
|
4085
4491
|
};
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
const
|
|
4090
|
-
const
|
|
4091
|
-
const
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4492
|
+
// src/plugins/two-factor/schema/totp.ts
|
|
4493
|
+
const generateTOTPSchema = z.object({ secret: z.string().openapi({ description: "The secret to generate the TOTP code" }) });
|
|
4494
|
+
const generateTOTPResponseSchema = z.string();
|
|
4495
|
+
const getTOTPURISchema = z.object({ password: z.string().openapi({ description: "User password" }) });
|
|
4496
|
+
const getTOTPURIResponseSchema = z.url();
|
|
4497
|
+
const verifyTOTPSchema = z.object({
|
|
4498
|
+
code: z.string().openapi({ description: "The otp code to verify. Eg: \"012345\"" }),
|
|
4499
|
+
/**
|
|
4500
|
+
* if true, the device will be trusted
|
|
4501
|
+
* for 30 days. It'll be refreshed on
|
|
4502
|
+
* every sign in request within this time.
|
|
4503
|
+
*/
|
|
4504
|
+
trustDevice: z.boolean().openapi({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional()
|
|
4096
4505
|
});
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4506
|
+
// src/plugins/two-factor/schema/otp.ts
|
|
4507
|
+
const sendTwoFactorOTPSchema = z.object({
|
|
4508
|
+
/**
|
|
4509
|
+
* if true, the device will be trusted
|
|
4510
|
+
* for 30 days. It'll be refreshed on
|
|
4511
|
+
* every sign in request within this time.
|
|
4512
|
+
*/
|
|
4513
|
+
trustDevice: z.boolean().optional().openapi({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }) });
|
|
4514
|
+
const verifyTwoFactorOTPSchema = z.object({
|
|
4515
|
+
code: z.string().openapi({ description: "The otp code to verify. Eg: \"012345\"" }),
|
|
4516
|
+
/**
|
|
4517
|
+
* if true, the device will be trusted
|
|
4518
|
+
* for 30 days. It'll be refreshed on
|
|
4519
|
+
* every sign in request within this time.
|
|
4520
|
+
*/
|
|
4521
|
+
trustDevice: z.boolean().optional().openapi({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" })
|
|
4104
4522
|
});
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4523
|
+
// src/plugins/two-factor/schema/backup-codes.ts
|
|
4524
|
+
const verifyBackupCodeSchema = z.object({
|
|
4525
|
+
code: z.string().openapi({ description: `A backup code to verify. Eg: "123456"` }),
|
|
4526
|
+
/**
|
|
4527
|
+
* Disable setting the session cookie
|
|
4528
|
+
*/
|
|
4529
|
+
disableSession: z.boolean().openapi({ description: "If true, the session cookie will not be set." }).optional(),
|
|
4530
|
+
/**
|
|
4531
|
+
* if true, the device will be trusted
|
|
4532
|
+
* for 30 days. It'll be refreshed on
|
|
4533
|
+
* every sign in request within this time.
|
|
4534
|
+
*/
|
|
4535
|
+
trustDevice: z.boolean().openapi({ description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true" }).optional()
|
|
4112
4536
|
});
|
|
4113
|
-
const generateBackupCodesSchema = z
|
|
4114
|
-
const viewBackupCodesSchema = z
|
|
4115
|
-
const backupCodesResponseSchema = z
|
|
4116
|
-
|
|
4117
|
-
//#endregion
|
|
4118
|
-
//#region src/plugins/two-factor/schema/index.ts
|
|
4537
|
+
const generateBackupCodesSchema = z.object({ password: z.string().openapi({ description: "The users password." }) });
|
|
4538
|
+
const viewBackupCodesSchema = z.object({ userId: z.coerce.string().openapi({ description: `The user ID to view all backup codes. Eg: "user-id"` }) });
|
|
4539
|
+
const backupCodesResponseSchema = z.array(z.string()).openapi({ description: "Array of generated backup codes in plain text" });
|
|
4540
|
+
// src/plugins/two-factor/schema/index.ts
|
|
4119
4541
|
const schema = {
|
|
4120
4542
|
user: { fields: { twoFactorEnabled: {
|
|
4121
4543
|
type: "boolean",
|
|
@@ -4145,24 +4567,20 @@ const schema = {
|
|
|
4145
4567
|
}
|
|
4146
4568
|
} }
|
|
4147
4569
|
};
|
|
4148
|
-
const enableTwoFactorSchema = z
|
|
4149
|
-
password: z
|
|
4150
|
-
issuer: z
|
|
4570
|
+
const enableTwoFactorSchema = z.object({
|
|
4571
|
+
password: z.string().openapi({ description: "User password" }),
|
|
4572
|
+
issuer: z.string().openapi({ description: "Custom issuer for the TOTP URI" }).optional()
|
|
4151
4573
|
});
|
|
4152
|
-
const disableTwoFactorSchema = z
|
|
4153
|
-
const totpURISchema = z
|
|
4154
|
-
totpURI: z
|
|
4155
|
-
backupCodes: z
|
|
4574
|
+
const disableTwoFactorSchema = z.object({ password: z.string().openapi({ description: "User password" }) });
|
|
4575
|
+
const totpURISchema = z.object({
|
|
4576
|
+
totpURI: z.url().openapi({ description: "TOTP URI" }),
|
|
4577
|
+
backupCodes: z.array(z.string()).openapi({ description: "Backup codes" })
|
|
4156
4578
|
});
|
|
4157
|
-
|
|
4158
|
-
//#endregion
|
|
4159
|
-
//#region src/plugins/two-factor/constant.ts
|
|
4579
|
+
// src/plugins/two-factor/constant.ts
|
|
4160
4580
|
const TWO_FACTOR_COOKIE_NAME = "two_factor";
|
|
4161
4581
|
const TRUST_DEVICE_COOKIE_NAME = "trust_device";
|
|
4162
|
-
const TRUST_DEVICE_COOKIE_MAX_AGE =
|
|
4163
|
-
|
|
4164
|
-
//#endregion
|
|
4165
|
-
//#region src/plugins/two-factor/verify-two-factor.ts
|
|
4582
|
+
const TRUST_DEVICE_COOKIE_MAX_AGE = 2592e3;
|
|
4583
|
+
// src/plugins/two-factor/verify-two-factor.ts
|
|
4166
4584
|
async function verifyTwoFactor(ctx, options) {
|
|
4167
4585
|
const invalid = (errorKey) => ctx.render({
|
|
4168
4586
|
success: False,
|
|
@@ -4189,30 +4607,30 @@ async function verifyTwoFactor(ctx, options) {
|
|
|
4189
4607
|
}, 401);
|
|
4190
4608
|
const dontRememberMe = await (0, cookies_exports.getSignedCookie)(ctx, context.secret, context.authCookies.dontRememberToken.name);
|
|
4191
4609
|
return {
|
|
4192
|
-
valid: async (ctx
|
|
4193
|
-
const session
|
|
4194
|
-
if (!session
|
|
4610
|
+
valid: async (ctx) => {
|
|
4611
|
+
const session = await context.internalAdapter.createSession(verificationToken.value, !!dontRememberMe);
|
|
4612
|
+
if (!session) return ctx.render({
|
|
4195
4613
|
success: False,
|
|
4196
4614
|
message: "Failed to create session"
|
|
4197
4615
|
}, 500);
|
|
4198
|
-
await setSessionCookie(ctx
|
|
4199
|
-
session
|
|
4616
|
+
await setSessionCookie(ctx, options, {
|
|
4617
|
+
session,
|
|
4200
4618
|
user
|
|
4201
4619
|
});
|
|
4202
|
-
if (ctx
|
|
4203
|
-
const trustDeviceCookie = context.createAuthCookie(TRUST_DEVICE_COOKIE_NAME, { maxAge:
|
|
4620
|
+
if (ctx.req.valid("json").trustDevice === true) {
|
|
4621
|
+
const trustDeviceCookie = context.createAuthCookie(TRUST_DEVICE_COOKIE_NAME, { maxAge: 2592e3 });
|
|
4204
4622
|
/**
|
|
4205
4623
|
* create a token that will be used to
|
|
4206
4624
|
* verify the device
|
|
4207
4625
|
*/
|
|
4208
|
-
const token = await createHMAC("SHA-256", "base64urlnopad").sign(context.secret, `${user.id}!${session
|
|
4209
|
-
await (0, cookies_exports.setSignedCookie)(ctx
|
|
4210
|
-
(0, cookies_exports.deleteCookie)(ctx
|
|
4211
|
-
(0, cookies_exports.deleteCookie)(ctx
|
|
4626
|
+
const token = await createHMAC("SHA-256", "base64urlnopad").sign(context.secret, `${user.id}!${session.token}`);
|
|
4627
|
+
await (0, cookies_exports.setSignedCookie)(ctx, trustDeviceCookie.name, `${token}!${session.token}`, context.secret, trustDeviceCookie.attributes);
|
|
4628
|
+
(0, cookies_exports.deleteCookie)(ctx, context.authCookies.dontRememberToken.name, context.authCookies.dontRememberToken.options);
|
|
4629
|
+
(0, cookies_exports.deleteCookie)(ctx, cookieName.name, cookieName.attributes);
|
|
4212
4630
|
}
|
|
4213
|
-
return ctx
|
|
4631
|
+
return ctx.render({
|
|
4214
4632
|
success: True,
|
|
4215
|
-
token: session
|
|
4633
|
+
token: session.token,
|
|
4216
4634
|
user: {
|
|
4217
4635
|
id: user.id,
|
|
4218
4636
|
email: user.email,
|
|
@@ -4233,7 +4651,7 @@ async function verifyTwoFactor(ctx, options) {
|
|
|
4233
4651
|
};
|
|
4234
4652
|
}
|
|
4235
4653
|
return {
|
|
4236
|
-
valid: (ctx
|
|
4654
|
+
valid: (ctx) => ctx.render({
|
|
4237
4655
|
success: True,
|
|
4238
4656
|
token: session.session.token,
|
|
4239
4657
|
user: {
|
|
@@ -4251,11 +4669,9 @@ async function verifyTwoFactor(ctx, options) {
|
|
|
4251
4669
|
key: `${session.user.id}!${session.session.id}`
|
|
4252
4670
|
};
|
|
4253
4671
|
}
|
|
4254
|
-
|
|
4255
|
-
//#endregion
|
|
4256
|
-
//#region src/plugins/two-factor/backup-codes/index.ts
|
|
4672
|
+
// src/plugins/two-factor/backup-codes/index.ts
|
|
4257
4673
|
function generateBackupCodesFn(options) {
|
|
4258
|
-
return Array.from({ length: options?.amount ?? 10 }).fill(null).map(() => generateRandomString(options?.length ?? 10, "a-z", "0-9", "A-Z")).map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
|
|
4674
|
+
return Array.from({ length: options?.amount ?? 10 }).fill(null).map(() => generateRandomString$2(options?.length ?? 10, "a-z", "0-9", "A-Z")).map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
|
|
4259
4675
|
}
|
|
4260
4676
|
async function generateBackupCodes(secret, options) {
|
|
4261
4677
|
const backupCodes = options?.customBackupCodesGenerate ? options.customBackupCodesGenerate() : generateBackupCodesFn(options);
|
|
@@ -4287,11 +4703,17 @@ async function verifyBackupCode(data, key, options) {
|
|
|
4287
4703
|
};
|
|
4288
4704
|
}
|
|
4289
4705
|
async function getBackupCodes(backupCodes, key, options) {
|
|
4290
|
-
if (options?.storeBackupCodes === "encrypted")
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4706
|
+
if (options?.storeBackupCodes === "encrypted") {
|
|
4707
|
+
const decrypted = await (0, crypto_exports.symmetricDecrypt)({
|
|
4708
|
+
key,
|
|
4709
|
+
data: backupCodes
|
|
4710
|
+
});
|
|
4711
|
+
return safeJSONParse(decrypted);
|
|
4712
|
+
}
|
|
4713
|
+
if (typeof options?.storeBackupCodes === "object" && "decrypt" in options?.storeBackupCodes) {
|
|
4714
|
+
const decrypted = await options?.storeBackupCodes.decrypt(backupCodes);
|
|
4715
|
+
return safeJSONParse(decrypted);
|
|
4716
|
+
}
|
|
4295
4717
|
return safeJSONParse(backupCodes);
|
|
4296
4718
|
}
|
|
4297
4719
|
const backupCode2fa = (options) => {
|
|
@@ -4299,6 +4721,21 @@ const backupCode2fa = (options) => {
|
|
|
4299
4721
|
return {
|
|
4300
4722
|
id: "backup_code",
|
|
4301
4723
|
routes: {
|
|
4724
|
+
/**
|
|
4725
|
+
* ### Endpoint
|
|
4726
|
+
*
|
|
4727
|
+
* POST `/two-factor/verify-backup-code`
|
|
4728
|
+
*
|
|
4729
|
+
* ### API Methods
|
|
4730
|
+
*
|
|
4731
|
+
* **server:**
|
|
4732
|
+
* `auth.api.verifyBackupCode`
|
|
4733
|
+
*
|
|
4734
|
+
* **client:**
|
|
4735
|
+
* `authClient.twoFactor.verifyBackupCode`
|
|
4736
|
+
*
|
|
4737
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-verify-backup-code)
|
|
4738
|
+
*/
|
|
4302
4739
|
verifyBackupCode: createEndpoint(createRoute$1({
|
|
4303
4740
|
operationId: "verifyBackupCode",
|
|
4304
4741
|
method: "post",
|
|
@@ -4311,20 +4748,20 @@ const backupCode2fa = (options) => {
|
|
|
4311
4748
|
const response = await verifyTwoFactor(ctx, authOptions);
|
|
4312
4749
|
if (response instanceof Response) return response;
|
|
4313
4750
|
const { session, valid } = response;
|
|
4314
|
-
const twoFactor
|
|
4751
|
+
const twoFactor = await context.adapter.findOne({
|
|
4315
4752
|
model: twoFactorTable,
|
|
4316
4753
|
where: [{
|
|
4317
4754
|
field: "userId",
|
|
4318
4755
|
value: session.user.id
|
|
4319
4756
|
}]
|
|
4320
4757
|
});
|
|
4321
|
-
if (!twoFactor
|
|
4758
|
+
if (!twoFactor) return ctx.render({
|
|
4322
4759
|
success: False,
|
|
4323
4760
|
message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
|
|
4324
4761
|
}, 400);
|
|
4325
4762
|
const { code, disableSession } = ctx.req.valid("json");
|
|
4326
4763
|
const validate = await verifyBackupCode({
|
|
4327
|
-
backupCodes: twoFactor
|
|
4764
|
+
backupCodes: twoFactor.backupCodes,
|
|
4328
4765
|
code
|
|
4329
4766
|
}, context.secret, options);
|
|
4330
4767
|
if (!validate.status) return ctx.render({
|
|
@@ -4350,6 +4787,21 @@ const backupCode2fa = (options) => {
|
|
|
4350
4787
|
user: session.user
|
|
4351
4788
|
}, 200);
|
|
4352
4789
|
}),
|
|
4790
|
+
/**
|
|
4791
|
+
* ### Endpoint
|
|
4792
|
+
*
|
|
4793
|
+
* POST `/two-factor/generate-backup-codes`
|
|
4794
|
+
*
|
|
4795
|
+
* ### API Methods
|
|
4796
|
+
*
|
|
4797
|
+
* **server:**
|
|
4798
|
+
* `auth.api.generateBackupCodes`
|
|
4799
|
+
*
|
|
4800
|
+
* **client:**
|
|
4801
|
+
* `authClient.twoFactor.generateBackupCodes`
|
|
4802
|
+
*
|
|
4803
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-generate-backup-codes)
|
|
4804
|
+
*/
|
|
4353
4805
|
generateBackupCodes: createEndpoint(createRoute$1({
|
|
4354
4806
|
operationId: "generateBackupCodes",
|
|
4355
4807
|
method: "post",
|
|
@@ -4365,8 +4817,8 @@ const backupCode2fa = (options) => {
|
|
|
4365
4817
|
success: False,
|
|
4366
4818
|
message: TWO_FACTOR_ERROR_CODES.TWO_FACTOR_NOT_ENABLED
|
|
4367
4819
|
}, 400);
|
|
4368
|
-
const res
|
|
4369
|
-
if (res
|
|
4820
|
+
const res = await context.password.checkPassword(user.id, ctx);
|
|
4821
|
+
if (res instanceof Response) return res;
|
|
4370
4822
|
const backupCodes = await generateBackupCodes(context.secret, options);
|
|
4371
4823
|
await context.adapter.updateMany({
|
|
4372
4824
|
model: twoFactorTable,
|
|
@@ -4378,6 +4830,21 @@ const backupCode2fa = (options) => {
|
|
|
4378
4830
|
});
|
|
4379
4831
|
return ctx.render(backupCodes.backupCodes, 200);
|
|
4380
4832
|
}),
|
|
4833
|
+
/**
|
|
4834
|
+
* ### Endpoint
|
|
4835
|
+
*
|
|
4836
|
+
* GET `/two-factor/view-backup-codes`
|
|
4837
|
+
*
|
|
4838
|
+
* ### API Methods
|
|
4839
|
+
*
|
|
4840
|
+
* **server:**
|
|
4841
|
+
* `auth.api.viewBackupCodes`
|
|
4842
|
+
*
|
|
4843
|
+
* **client:**
|
|
4844
|
+
* `authClient.twoFactor.viewBackupCodes`
|
|
4845
|
+
*
|
|
4846
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-view-backup-codes)
|
|
4847
|
+
*/
|
|
4381
4848
|
viewBackupCodes: createEndpoint(createRoute$1({
|
|
4382
4849
|
operationId: "viewBackupCodes",
|
|
4383
4850
|
method: "post",
|
|
@@ -4392,18 +4859,18 @@ const backupCode2fa = (options) => {
|
|
|
4392
4859
|
message: "Unauthorized"
|
|
4393
4860
|
}, 401);
|
|
4394
4861
|
const { userId } = ctx.req.valid("json");
|
|
4395
|
-
const twoFactor
|
|
4862
|
+
const twoFactor = await context.adapter.findOne({
|
|
4396
4863
|
model: twoFactorTable,
|
|
4397
4864
|
where: [{
|
|
4398
4865
|
field: "userId",
|
|
4399
4866
|
value: userId
|
|
4400
4867
|
}]
|
|
4401
4868
|
});
|
|
4402
|
-
if (!twoFactor
|
|
4869
|
+
if (!twoFactor) return ctx.render({
|
|
4403
4870
|
success: False,
|
|
4404
4871
|
message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
|
|
4405
4872
|
}, 400);
|
|
4406
|
-
const decryptedBackupCodes = await getBackupCodes(twoFactor
|
|
4873
|
+
const decryptedBackupCodes = await getBackupCodes(twoFactor.backupCodes, context.secret, options);
|
|
4407
4874
|
if (!decryptedBackupCodes) return ctx.render({
|
|
4408
4875
|
success: False,
|
|
4409
4876
|
message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
|
|
@@ -4413,16 +4880,12 @@ const backupCode2fa = (options) => {
|
|
|
4413
4880
|
}
|
|
4414
4881
|
};
|
|
4415
4882
|
};
|
|
4416
|
-
|
|
4417
|
-
//#endregion
|
|
4418
|
-
//#region src/plugins/two-factor/utils.ts
|
|
4883
|
+
// src/plugins/two-factor/utils.ts
|
|
4419
4884
|
const defaultKeyHasher = async (token) => {
|
|
4420
4885
|
const hash = await createHash("SHA-256").digest(new TextEncoder().encode(token));
|
|
4421
4886
|
return base64Url.encode(new Uint8Array(hash), { padding: false });
|
|
4422
4887
|
};
|
|
4423
|
-
|
|
4424
|
-
//#endregion
|
|
4425
|
-
//#region src/plugins/two-factor/otp/index.ts
|
|
4888
|
+
// src/plugins/two-factor/otp/index.ts
|
|
4426
4889
|
/**
|
|
4427
4890
|
* The otp adapter is created from the totp adapter.
|
|
4428
4891
|
*/
|
|
@@ -4456,6 +4919,21 @@ const otp2fa = (options) => {
|
|
|
4456
4919
|
return {
|
|
4457
4920
|
id: "otp",
|
|
4458
4921
|
routes: {
|
|
4922
|
+
/**
|
|
4923
|
+
* ### Endpoint
|
|
4924
|
+
*
|
|
4925
|
+
* POST `/two-factor/send-otp`
|
|
4926
|
+
*
|
|
4927
|
+
* ### API Methods
|
|
4928
|
+
*
|
|
4929
|
+
* **server:**
|
|
4930
|
+
* `auth.api.send2FaOTP`
|
|
4931
|
+
*
|
|
4932
|
+
* **client:**
|
|
4933
|
+
* `authClient.twoFactor.sendOtp`
|
|
4934
|
+
*
|
|
4935
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-send-otp)
|
|
4936
|
+
*/
|
|
4459
4937
|
sendTwoFactorOTP: createEndpoint(createRoute$1({
|
|
4460
4938
|
operationId: "sendTwoFactorOTP",
|
|
4461
4939
|
method: "post",
|
|
@@ -4493,6 +4971,21 @@ const otp2fa = (options) => {
|
|
|
4493
4971
|
}, ctx.req);
|
|
4494
4972
|
return ctx.render({ success: True }, 200);
|
|
4495
4973
|
}),
|
|
4974
|
+
/**
|
|
4975
|
+
* ### Endpoint
|
|
4976
|
+
*
|
|
4977
|
+
* POST `/two-factor/verify-otp`
|
|
4978
|
+
*
|
|
4979
|
+
* ### API Methods
|
|
4980
|
+
*
|
|
4981
|
+
* **server:**
|
|
4982
|
+
* `auth.api.verifyOTP`
|
|
4983
|
+
*
|
|
4984
|
+
* **client:**
|
|
4985
|
+
* `authClient.twoFactor.verifyOtp`
|
|
4986
|
+
*
|
|
4987
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-verify-otp)
|
|
4988
|
+
*/
|
|
4496
4989
|
verifyTwoFactorOTP: createEndpoint(createRoute$1({
|
|
4497
4990
|
operationId: "verifyTwoFactorOTP",
|
|
4498
4991
|
method: "post",
|
|
@@ -4560,9 +5053,7 @@ const otp2fa = (options) => {
|
|
|
4560
5053
|
}
|
|
4561
5054
|
};
|
|
4562
5055
|
};
|
|
4563
|
-
|
|
4564
|
-
//#endregion
|
|
4565
|
-
//#region src/plugins/two-factor/totp/index.ts
|
|
5056
|
+
// src/plugins/two-factor/totp/index.ts
|
|
4566
5057
|
const totp2fa = (options) => {
|
|
4567
5058
|
const opts = {
|
|
4568
5059
|
...options,
|
|
@@ -4573,6 +5064,21 @@ const totp2fa = (options) => {
|
|
|
4573
5064
|
return {
|
|
4574
5065
|
id: "totp",
|
|
4575
5066
|
routes: {
|
|
5067
|
+
/**
|
|
5068
|
+
* ### Endpoint
|
|
5069
|
+
*
|
|
5070
|
+
* POST `/totp/generate`
|
|
5071
|
+
*
|
|
5072
|
+
* ### API Methods
|
|
5073
|
+
*
|
|
5074
|
+
* **server:**
|
|
5075
|
+
* `auth.api.generateTOTP`
|
|
5076
|
+
*
|
|
5077
|
+
* **client:**
|
|
5078
|
+
* `authClient.totp.generate`
|
|
5079
|
+
*
|
|
5080
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/totp#api-method-totp-generate)
|
|
5081
|
+
*/
|
|
4576
5082
|
generateTOTP: createEndpoint(createRoute$1({
|
|
4577
5083
|
operationId: "generateTOTP",
|
|
4578
5084
|
method: "post",
|
|
@@ -4596,6 +5102,21 @@ const totp2fa = (options) => {
|
|
|
4596
5102
|
}).totp();
|
|
4597
5103
|
return ctx.json(code, 200);
|
|
4598
5104
|
}),
|
|
5105
|
+
/**
|
|
5106
|
+
* ### Endpoint
|
|
5107
|
+
*
|
|
5108
|
+
* POST `/two-factor/get-totp-uri`
|
|
5109
|
+
*
|
|
5110
|
+
* ### API Methods
|
|
5111
|
+
*
|
|
5112
|
+
* **server:**
|
|
5113
|
+
* `auth.api.getTOTPURI`
|
|
5114
|
+
*
|
|
5115
|
+
* **client:**
|
|
5116
|
+
* `authClient.twoFactor.getTotpUri`
|
|
5117
|
+
*
|
|
5118
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/two-factor#api-method-two-factor-get-totp-uri)
|
|
5119
|
+
*/
|
|
4599
5120
|
getTOTPURI: createEndpoint(createRoute$1({
|
|
4600
5121
|
operationId: "getTOTPURI",
|
|
4601
5122
|
method: "post",
|
|
@@ -4615,23 +5136,23 @@ const totp2fa = (options) => {
|
|
|
4615
5136
|
}, 400);
|
|
4616
5137
|
}
|
|
4617
5138
|
const session = ctx.get("session");
|
|
4618
|
-
const twoFactor
|
|
5139
|
+
const twoFactor = await context.adapter.findOne({
|
|
4619
5140
|
model: twoFactorTable,
|
|
4620
5141
|
where: [{
|
|
4621
5142
|
field: "userId",
|
|
4622
5143
|
value: session.user.id
|
|
4623
5144
|
}]
|
|
4624
5145
|
});
|
|
4625
|
-
if (!twoFactor
|
|
5146
|
+
if (!twoFactor) return ctx.render({
|
|
4626
5147
|
success: False,
|
|
4627
5148
|
message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
|
|
4628
5149
|
}, 400);
|
|
4629
5150
|
const secret = await (0, crypto_exports.symmetricDecrypt)({
|
|
4630
5151
|
key: context.secret,
|
|
4631
|
-
data: twoFactor
|
|
5152
|
+
data: twoFactor.secret
|
|
4632
5153
|
});
|
|
4633
|
-
const res
|
|
4634
|
-
if (res
|
|
5154
|
+
const res = await context.password.checkPassword(session.user.id, ctx);
|
|
5155
|
+
if (res instanceof Response) return res;
|
|
4635
5156
|
const totpURI = createOTP(secret, {
|
|
4636
5157
|
digits: opts.digits,
|
|
4637
5158
|
period: opts.period
|
|
@@ -4658,21 +5179,22 @@ const totp2fa = (options) => {
|
|
|
4658
5179
|
const result = await verifyTwoFactor(ctx, authOptions);
|
|
4659
5180
|
if (result instanceof Response) return result;
|
|
4660
5181
|
const { session, valid, invalid } = result;
|
|
4661
|
-
const twoFactor
|
|
5182
|
+
const twoFactor = await context.adapter.findOne({
|
|
4662
5183
|
model: twoFactorTable,
|
|
4663
5184
|
where: [{
|
|
4664
5185
|
field: "userId",
|
|
4665
5186
|
value: session.user.id
|
|
4666
5187
|
}]
|
|
4667
5188
|
});
|
|
4668
|
-
if (!twoFactor
|
|
5189
|
+
if (!twoFactor) return ctx.render({
|
|
4669
5190
|
success: False,
|
|
4670
5191
|
message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
|
|
4671
5192
|
}, 400);
|
|
4672
|
-
|
|
5193
|
+
const decrypted = await (0, crypto_exports.symmetricDecrypt)({
|
|
4673
5194
|
key: context.secret,
|
|
4674
|
-
data: twoFactor
|
|
4675
|
-
})
|
|
5195
|
+
data: twoFactor.secret
|
|
5196
|
+
});
|
|
5197
|
+
if (!await createOTP(decrypted, {
|
|
4676
5198
|
period: opts.period,
|
|
4677
5199
|
digits: opts.digits
|
|
4678
5200
|
}).verify(ctx.req.valid("json").code)) return await invalid("INVALID_CODE");
|
|
@@ -4698,9 +5220,7 @@ const totp2fa = (options) => {
|
|
|
4698
5220
|
}
|
|
4699
5221
|
};
|
|
4700
5222
|
};
|
|
4701
|
-
|
|
4702
|
-
//#endregion
|
|
4703
|
-
//#region src/plugins/two-factor/client.ts
|
|
5223
|
+
// src/plugins/two-factor/client.ts
|
|
4704
5224
|
const twoFactorClient = (options) => {
|
|
4705
5225
|
return {
|
|
4706
5226
|
id: "two-factor",
|
|
@@ -4726,9 +5246,7 @@ const twoFactorClient = (options) => {
|
|
|
4726
5246
|
}]
|
|
4727
5247
|
};
|
|
4728
5248
|
};
|
|
4729
|
-
|
|
4730
|
-
//#endregion
|
|
4731
|
-
//#region src/plugins/two-factor/index.ts
|
|
5249
|
+
// src/plugins/two-factor/index.ts
|
|
4732
5250
|
const twoFactor = (options) => {
|
|
4733
5251
|
const opts = { twoFactorTable: "twoFactor" };
|
|
4734
5252
|
const backupCodeOptions = {
|
|
@@ -4744,6 +5262,21 @@ const twoFactor = (options) => {
|
|
|
4744
5262
|
...totp.routes,
|
|
4745
5263
|
...otp.routes,
|
|
4746
5264
|
...backupCode.routes,
|
|
5265
|
+
/**
|
|
5266
|
+
* ### Endpoint
|
|
5267
|
+
*
|
|
5268
|
+
* POST `/two-factor/enable`
|
|
5269
|
+
*
|
|
5270
|
+
* ### API Methods
|
|
5271
|
+
*
|
|
5272
|
+
* **server:**
|
|
5273
|
+
* `auth.api.enableTwoFactor`
|
|
5274
|
+
*
|
|
5275
|
+
* **client:**
|
|
5276
|
+
* `authClient.twoFactor.enable`
|
|
5277
|
+
*
|
|
5278
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-enable)
|
|
5279
|
+
*/
|
|
4747
5280
|
enableTwoFactor: createEndpoint(createRoute$1({
|
|
4748
5281
|
operationId: "enableTwoFactor",
|
|
4749
5282
|
method: "post",
|
|
@@ -4765,7 +5298,7 @@ const twoFactor = (options) => {
|
|
|
4765
5298
|
success: False,
|
|
4766
5299
|
message: BASE_ERROR_CODES.INVALID_PASSWORD
|
|
4767
5300
|
}, 400);
|
|
4768
|
-
const secret = generateRandomString(32);
|
|
5301
|
+
const secret = generateRandomString$2(32);
|
|
4769
5302
|
const encryptedSecret = await (0, crypto_exports.symmetricEncrypt)({
|
|
4770
5303
|
key: context.secret,
|
|
4771
5304
|
data: secret
|
|
@@ -4777,11 +5310,12 @@ const twoFactor = (options) => {
|
|
|
4777
5310
|
success: False,
|
|
4778
5311
|
message: BASE_ERROR_CODES.FAILED_TO_UPDATE_USER
|
|
4779
5312
|
}, 500);
|
|
5313
|
+
const newSession = await context.internalAdapter.createSession(updatedUser.id, false, session.session);
|
|
4780
5314
|
/**
|
|
4781
5315
|
* Update the session cookie with the new user data
|
|
4782
5316
|
*/
|
|
4783
5317
|
await setSessionCookie(ctx, authOptions, {
|
|
4784
|
-
session:
|
|
5318
|
+
session: newSession,
|
|
4785
5319
|
user: updatedUser
|
|
4786
5320
|
});
|
|
4787
5321
|
await context.internalAdapter.deleteSession(session.session.token);
|
|
@@ -4810,6 +5344,21 @@ const twoFactor = (options) => {
|
|
|
4810
5344
|
backupCodes: backupCodes.backupCodes
|
|
4811
5345
|
}, 200);
|
|
4812
5346
|
}),
|
|
5347
|
+
/**
|
|
5348
|
+
* ### Endpoint
|
|
5349
|
+
*
|
|
5350
|
+
* POST `/two-factor/disable`
|
|
5351
|
+
*
|
|
5352
|
+
* ### API Methods
|
|
5353
|
+
*
|
|
5354
|
+
* **server:**
|
|
5355
|
+
* `auth.api.disableTwoFactor`
|
|
5356
|
+
*
|
|
5357
|
+
* **client:**
|
|
5358
|
+
* `authClient.twoFactor.disable`
|
|
5359
|
+
*
|
|
5360
|
+
* @see [Read our docs to learn more.](https://faire-auth.com/docs/plugins/2fa#api-method-two-factor-disable)
|
|
5361
|
+
*/
|
|
4813
5362
|
disableTwoFactor: createEndpoint(createRoute$1({
|
|
4814
5363
|
operationId: "disableTwoFactor",
|
|
4815
5364
|
method: "post",
|
|
@@ -4843,11 +5392,12 @@ const twoFactor = (options) => {
|
|
|
4843
5392
|
value: updatedUser.id
|
|
4844
5393
|
}]
|
|
4845
5394
|
});
|
|
5395
|
+
const newSession = await context.internalAdapter.createSession(updatedUser.id, false, session.session);
|
|
4846
5396
|
/**
|
|
4847
5397
|
* Update the session cookie with the new user data
|
|
4848
5398
|
*/
|
|
4849
5399
|
await setSessionCookie(ctx, authOptions, {
|
|
4850
|
-
session:
|
|
5400
|
+
session: newSession,
|
|
4851
5401
|
user: updatedUser
|
|
4852
5402
|
});
|
|
4853
5403
|
await context.internalAdapter.deleteSession(session.session.token);
|
|
@@ -4880,7 +5430,7 @@ const twoFactor = (options) => {
|
|
|
4880
5430
|
await context.internalAdapter.deleteSession(data.session.token);
|
|
4881
5431
|
const maxAge = (options?.otpOptions?.period ?? 3) * 60;
|
|
4882
5432
|
const twoFactorCookie = context.createAuthCookie(TWO_FACTOR_COOKIE_NAME, { maxAge });
|
|
4883
|
-
const identifier = `2fa-${generateRandomString(20)}`;
|
|
5433
|
+
const identifier = `2fa-${generateRandomString$2(20)}`;
|
|
4884
5434
|
await context.internalAdapter.createVerificationValue({
|
|
4885
5435
|
value: data.user.id,
|
|
4886
5436
|
identifier,
|
|
@@ -4899,9 +5449,7 @@ const twoFactor = (options) => {
|
|
|
4899
5449
|
$ERROR_CODES: TWO_FACTOR_ERROR_CODES
|
|
4900
5450
|
};
|
|
4901
5451
|
};
|
|
4902
|
-
|
|
4903
|
-
//#endregion
|
|
4904
|
-
//#region src/plugins/username/error-codes.ts
|
|
5452
|
+
// src/plugins/username/error-codes.ts
|
|
4905
5453
|
const USERNAME_ERROR_CODES = {
|
|
4906
5454
|
INVALID_USERNAME_OR_PASSWORD: "Invalid username or password",
|
|
4907
5455
|
EMAIL_NOT_VERIFIED: "Email not verified",
|
|
@@ -4912,9 +5460,7 @@ const USERNAME_ERROR_CODES = {
|
|
|
4912
5460
|
INVALID_USERNAME: "Username is invalid",
|
|
4913
5461
|
INVALID_DISPLAY_USERNAME: "Display username is invalid"
|
|
4914
5462
|
};
|
|
4915
|
-
|
|
4916
|
-
//#endregion
|
|
4917
|
-
//#region src/plugins/username/schema.ts
|
|
5463
|
+
// src/plugins/username/schema.ts
|
|
4918
5464
|
const getSchema = (normalizer) => {
|
|
4919
5465
|
return { user: { fields: {
|
|
4920
5466
|
username: {
|
|
@@ -4936,17 +5482,15 @@ const getSchema = (normalizer) => {
|
|
|
4936
5482
|
}
|
|
4937
5483
|
} } };
|
|
4938
5484
|
};
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
function defaultUsernameValidator(username$1) {
|
|
4943
|
-
return /^[a-zA-Z0-9_.]+$/.test(username$1);
|
|
5485
|
+
// src/plugins/username/index.ts
|
|
5486
|
+
function defaultUsernameValidator(username) {
|
|
5487
|
+
return /^[a-zA-Z0-9_.]+$/.test(username);
|
|
4944
5488
|
}
|
|
4945
5489
|
const username = (options) => {
|
|
4946
|
-
const normalizer = (username
|
|
4947
|
-
if (options?.usernameNormalization === false) return username
|
|
4948
|
-
if (options?.usernameNormalization) return options.usernameNormalization(username
|
|
4949
|
-
return username
|
|
5490
|
+
const normalizer = (username) => {
|
|
5491
|
+
if (options?.usernameNormalization === false) return username;
|
|
5492
|
+
if (options?.usernameNormalization) return options.usernameNormalization(username);
|
|
5493
|
+
return username.toLowerCase();
|
|
4950
5494
|
};
|
|
4951
5495
|
const displayUsernameNormalizer = (displayUsername) => {
|
|
4952
5496
|
return options?.displayUsernameNormalization ? options.displayUsernameNormalization(displayUsername) : displayUsername;
|
|
@@ -4958,19 +5502,19 @@ const username = (options) => {
|
|
|
4958
5502
|
return;
|
|
4959
5503
|
}
|
|
4960
5504
|
const session = ctx.get("session");
|
|
4961
|
-
const username
|
|
4962
|
-
if (username
|
|
5505
|
+
const username = typeof input.data.username === "string" && options?.validationOrder?.username === "post-normalization" ? normalizer(input.data.username) : input.data.username;
|
|
5506
|
+
if (username !== void 0 && typeof username === "string") {
|
|
4963
5507
|
const minUsernameLength = options?.minUsernameLength || 3;
|
|
4964
5508
|
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
4965
|
-
if (username
|
|
5509
|
+
if (username.length < minUsernameLength) return ctx.render({
|
|
4966
5510
|
success: False,
|
|
4967
5511
|
message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
|
|
4968
5512
|
}, 400);
|
|
4969
|
-
if (username
|
|
5513
|
+
if (username.length > maxUsernameLength) return ctx.render({
|
|
4970
5514
|
success: False,
|
|
4971
5515
|
message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG
|
|
4972
5516
|
}, 400);
|
|
4973
|
-
if (!await (options?.usernameValidator ?? defaultUsernameValidator)(username
|
|
5517
|
+
if (!await (options?.usernameValidator ?? defaultUsernameValidator)(username)) return ctx.render({
|
|
4974
5518
|
success: False,
|
|
4975
5519
|
message: USERNAME_ERROR_CODES.INVALID_USERNAME
|
|
4976
5520
|
}, 400);
|
|
@@ -4978,7 +5522,7 @@ const username = (options) => {
|
|
|
4978
5522
|
model: "user",
|
|
4979
5523
|
where: [{
|
|
4980
5524
|
field: "username",
|
|
4981
|
-
value: username
|
|
5525
|
+
value: username
|
|
4982
5526
|
}]
|
|
4983
5527
|
});
|
|
4984
5528
|
const blockChangeSignUp = ctx.get("path") === "/sign-up/email" && user;
|
|
@@ -5005,20 +5549,20 @@ const username = (options) => {
|
|
|
5005
5549
|
init: (_ctx) => {
|
|
5006
5550
|
return { options: { databaseHooks: { user: {
|
|
5007
5551
|
create: { async before(user, _context) {
|
|
5008
|
-
const username
|
|
5552
|
+
const username = "username" in user ? user["username"] : null;
|
|
5009
5553
|
const displayUsername = "displayUsername" in user ? user["displayUsername"] : null;
|
|
5010
5554
|
return { data: {
|
|
5011
5555
|
...user,
|
|
5012
|
-
...username
|
|
5556
|
+
...username ? { username: normalizer(username) } : {},
|
|
5013
5557
|
...displayUsername ? { displayUsername: displayUsernameNormalizer(displayUsername) } : {}
|
|
5014
5558
|
} };
|
|
5015
5559
|
} },
|
|
5016
5560
|
update: { async before(user, _context) {
|
|
5017
|
-
const username
|
|
5561
|
+
const username = "username" in user ? user["username"] : null;
|
|
5018
5562
|
const displayUsername = "displayUsername" in user ? user["displayUsername"] : null;
|
|
5019
5563
|
return { data: {
|
|
5020
5564
|
...user,
|
|
5021
|
-
...username
|
|
5565
|
+
...username ? { username: normalizer(username) } : {},
|
|
5022
5566
|
...displayUsername ? { displayUsername: displayUsernameNormalizer(displayUsername) } : {}
|
|
5023
5567
|
} };
|
|
5024
5568
|
} }
|
|
@@ -5031,41 +5575,41 @@ const username = (options) => {
|
|
|
5031
5575
|
path: "/sign-in/username",
|
|
5032
5576
|
summary: "Sign in with username",
|
|
5033
5577
|
description: "Sign in with username",
|
|
5034
|
-
request: req().bdy(z
|
|
5035
|
-
username: z
|
|
5036
|
-
password: z
|
|
5037
|
-
rememberMe: z
|
|
5038
|
-
callbackURL: z
|
|
5578
|
+
request: req().bdy(z.object({
|
|
5579
|
+
username: z.string().openapi({ description: "The username of the user" }),
|
|
5580
|
+
password: z.string().openapi({ description: "The password of the user" }),
|
|
5581
|
+
rememberMe: z.boolean().openapi({ description: "Remember the user session" }).optional(),
|
|
5582
|
+
callbackURL: z.string().openapi({ description: "The URL to redirect to after email verification" }).optional()
|
|
5039
5583
|
})).bld(),
|
|
5040
5584
|
responses: res(SCHEMAS[Definitions.TOKEN_USER].default).err(400, "Invalid username").err(401, "Invalid email or password").err(403, "Email not verified").err(500, "Failed to create session").bld()
|
|
5041
5585
|
}), (authOptions) => async (ctx) => {
|
|
5042
5586
|
const context = ctx.get("context");
|
|
5043
|
-
let { username
|
|
5044
|
-
if (!username
|
|
5587
|
+
let { username, password, rememberMe, callbackURL } = ctx.req.valid("json");
|
|
5588
|
+
if (!username || !password) {
|
|
5045
5589
|
context.logger.error("Username or password not found");
|
|
5046
5590
|
return ctx.render({
|
|
5047
5591
|
success: False,
|
|
5048
5592
|
message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
5049
5593
|
}, 401);
|
|
5050
5594
|
}
|
|
5051
|
-
username
|
|
5595
|
+
username = options?.validationOrder?.username === "pre-normalization" ? normalizer(username) : username;
|
|
5052
5596
|
const minUsernameLength = options?.minUsernameLength ?? 3;
|
|
5053
5597
|
const maxUsernameLength = options?.maxUsernameLength ?? 30;
|
|
5054
|
-
if (username
|
|
5055
|
-
context.logger.error("Username too short", { username
|
|
5598
|
+
if (username.length < minUsernameLength) {
|
|
5599
|
+
context.logger.error("Username too short", { username });
|
|
5056
5600
|
return ctx.render({
|
|
5057
5601
|
success: False,
|
|
5058
5602
|
message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
|
|
5059
5603
|
}, 400);
|
|
5060
5604
|
}
|
|
5061
|
-
if (username
|
|
5062
|
-
context.logger.error("Username too long", { username
|
|
5605
|
+
if (username.length > maxUsernameLength) {
|
|
5606
|
+
context.logger.error("Username too long", { username });
|
|
5063
5607
|
return ctx.render({
|
|
5064
5608
|
success: False,
|
|
5065
5609
|
message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG
|
|
5066
5610
|
}, 400);
|
|
5067
5611
|
}
|
|
5068
|
-
if (!(options?.usernameValidator ?? defaultUsernameValidator)(username
|
|
5612
|
+
if (!(options?.usernameValidator ?? defaultUsernameValidator)(username)) return ctx.render({
|
|
5069
5613
|
success: False,
|
|
5070
5614
|
message: USERNAME_ERROR_CODES.INVALID_USERNAME
|
|
5071
5615
|
}, 400);
|
|
@@ -5073,12 +5617,12 @@ const username = (options) => {
|
|
|
5073
5617
|
model: "user",
|
|
5074
5618
|
where: [{
|
|
5075
5619
|
field: "username",
|
|
5076
|
-
value: normalizer(username
|
|
5620
|
+
value: normalizer(username)
|
|
5077
5621
|
}]
|
|
5078
5622
|
});
|
|
5079
5623
|
if (!user) {
|
|
5080
5624
|
await context.password.hash(password);
|
|
5081
|
-
context.logger.error("User not found", { username
|
|
5625
|
+
context.logger.error("User not found", { username });
|
|
5082
5626
|
return ctx.render({
|
|
5083
5627
|
success: False,
|
|
5084
5628
|
message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
@@ -5100,7 +5644,7 @@ const username = (options) => {
|
|
|
5100
5644
|
}, 401);
|
|
5101
5645
|
const currentPassword = account?.password;
|
|
5102
5646
|
if (!currentPassword) {
|
|
5103
|
-
context.logger.error("Password not found", { username
|
|
5647
|
+
context.logger.error("Password not found", { username });
|
|
5104
5648
|
return ctx.render({
|
|
5105
5649
|
success: False,
|
|
5106
5650
|
message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD
|
|
@@ -5164,22 +5708,22 @@ const username = (options) => {
|
|
|
5164
5708
|
operationId: "isUsernameAvailable",
|
|
5165
5709
|
method: "post",
|
|
5166
5710
|
path: "/is-username-available",
|
|
5167
|
-
request: req().bdy(z
|
|
5168
|
-
responses: res(z
|
|
5711
|
+
request: req().bdy(z.object({ username: z.string().openapi({ description: "The username to check" }) })).bld(),
|
|
5712
|
+
responses: res(z.object({ available: z.boolean() }), "Whether or not the username is available").err(400, "Invalid username").bld()
|
|
5169
5713
|
}), (_authOptions) => async (ctx) => {
|
|
5170
5714
|
const context = ctx.get("context");
|
|
5171
|
-
const { username
|
|
5715
|
+
const { username } = ctx.req.valid("json");
|
|
5172
5716
|
const minUsernameLength = options?.minUsernameLength || 3;
|
|
5173
5717
|
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
5174
|
-
if (username
|
|
5718
|
+
if (username.length < minUsernameLength) return ctx.render({
|
|
5175
5719
|
success: False,
|
|
5176
5720
|
message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
|
|
5177
5721
|
}, 400);
|
|
5178
|
-
if (username
|
|
5722
|
+
if (username.length > maxUsernameLength) return ctx.render({
|
|
5179
5723
|
success: False,
|
|
5180
5724
|
message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG
|
|
5181
5725
|
}, 400);
|
|
5182
|
-
if (!await (options?.usernameValidator || defaultUsernameValidator)(username
|
|
5726
|
+
if (!await (options?.usernameValidator || defaultUsernameValidator)(username)) return ctx.render({
|
|
5183
5727
|
success: False,
|
|
5184
5728
|
message: USERNAME_ERROR_CODES.INVALID_USERNAME
|
|
5185
5729
|
}, 400);
|
|
@@ -5187,7 +5731,7 @@ const username = (options) => {
|
|
|
5187
5731
|
model: "user",
|
|
5188
5732
|
where: [{
|
|
5189
5733
|
field: "username",
|
|
5190
|
-
value: normalizer(username
|
|
5734
|
+
value: normalizer(username)
|
|
5191
5735
|
}]
|
|
5192
5736
|
})) return ctx.render({ available: false }, 200);
|
|
5193
5737
|
return ctx.render({ available: true }, 200);
|
|
@@ -5204,6 +5748,4 @@ const username = (options) => {
|
|
|
5204
5748
|
$ERROR_CODES: USERNAME_ERROR_CODES
|
|
5205
5749
|
};
|
|
5206
5750
|
};
|
|
5207
|
-
|
|
5208
|
-
//#endregion
|
|
5209
|
-
export { TWO_FACTOR_ERROR_CODES, USERNAME_ERROR_CODES, bearer, createAccessControl, createEndpoint, createHook, createMiddleware, createRoute, magicLink, oAuthProxy, organization, parseRoles, passkey, role, twoFactor, twoFactorClient, username };
|
|
5751
|
+
export { TWO_FACTOR_ERROR_CODES, USERNAME_ERROR_CODES, bearer, createAccessControl, createEndpoint, createHook, createMiddleware, createRoute, magicLink, oAuthProxy, organization, parseRoles, passkey, role, twoFactor, twoFactorClient, username };
|