better-auth 1.6.10 → 1.6.12
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/api/index.d.mts +8 -2
- package/dist/api/routes/callback.d.mts +1 -1
- package/dist/api/routes/callback.mjs +36 -40
- package/dist/api/routes/email-verification.d.mts +1 -0
- package/dist/api/routes/email-verification.mjs +4 -3
- package/dist/api/routes/session.mjs +14 -9
- package/dist/api/routes/sign-in.d.mts +1 -0
- package/dist/api/routes/sign-in.mjs +2 -1
- package/dist/api/routes/sign-up.d.mts +1 -0
- package/dist/api/routes/sign-up.mjs +9 -7
- package/dist/api/routes/update-user.mjs +5 -5
- package/dist/client/index.d.mts +2 -2
- package/dist/client/parser.mjs +0 -1
- package/dist/client/plugins/index.d.mts +3 -3
- package/dist/client/proxy.mjs +2 -1
- package/dist/context/helpers.mjs +3 -2
- package/dist/cookies/cookie-utils.d.mts +24 -1
- package/dist/cookies/cookie-utils.mjs +85 -22
- package/dist/cookies/index.d.mts +2 -3
- package/dist/cookies/index.mjs +39 -11
- package/dist/cookies/session-store.mjs +4 -23
- package/dist/db/get-migration.mjs +4 -4
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +3 -2
- package/dist/db/internal-adapter.mjs +96 -1
- package/dist/db/schema.d.mts +15 -2
- package/dist/db/schema.mjs +26 -1
- package/dist/db/with-hooks.d.mts +1 -0
- package/dist/db/with-hooks.mjs +58 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/oauth2/errors.mjs +16 -1
- package/dist/oauth2/link-account.mjs +6 -4
- package/dist/oauth2/state.mjs +8 -2
- package/dist/package.mjs +1 -1
- package/dist/plugins/access/access.d.mts +3 -15
- package/dist/plugins/access/access.mjs +11 -6
- package/dist/plugins/access/index.d.mts +2 -2
- package/dist/plugins/access/types.d.mts +11 -4
- package/dist/plugins/admin/access/statement.d.mts +29 -93
- package/dist/plugins/admin/admin.mjs +0 -4
- package/dist/plugins/admin/client.d.mts +1 -1
- package/dist/plugins/admin/routes.mjs +1 -0
- package/dist/plugins/anonymous/client.d.mts +1 -0
- package/dist/plugins/anonymous/error-codes.d.mts +1 -0
- package/dist/plugins/anonymous/error-codes.mjs +1 -0
- package/dist/plugins/anonymous/index.d.mts +1 -0
- package/dist/plugins/anonymous/index.mjs +16 -2
- package/dist/plugins/bearer/index.mjs +4 -9
- package/dist/plugins/captcha/index.mjs +2 -2
- package/dist/plugins/device-authorization/error-codes.mjs +1 -0
- package/dist/plugins/device-authorization/index.d.mts +1 -0
- package/dist/plugins/device-authorization/routes.mjs +34 -3
- package/dist/plugins/generic-oauth/index.d.mts +1 -1
- package/dist/plugins/generic-oauth/index.mjs +6 -6
- package/dist/plugins/generic-oauth/routes.mjs +34 -32
- package/dist/plugins/generic-oauth/types.d.mts +7 -0
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/last-login-method/client.mjs +2 -2
- package/dist/plugins/magic-link/index.d.mts +8 -1
- package/dist/plugins/magic-link/index.mjs +4 -17
- package/dist/plugins/mcp/authorize.mjs +8 -2
- package/dist/plugins/mcp/index.mjs +73 -34
- package/dist/plugins/multi-session/index.mjs +2 -2
- package/dist/plugins/oauth-proxy/index.mjs +44 -31
- package/dist/plugins/oauth-proxy/utils.mjs +3 -10
- package/dist/plugins/oidc-provider/authorize.mjs +8 -2
- package/dist/plugins/oidc-provider/index.mjs +63 -37
- package/dist/plugins/one-tap/index.mjs +13 -8
- package/dist/plugins/open-api/generator.mjs +16 -5
- package/dist/plugins/organization/access/statement.d.mts +68 -201
- package/dist/plugins/organization/adapter.mjs +61 -56
- package/dist/plugins/organization/client.d.mts +3 -1
- package/dist/plugins/organization/error-codes.d.mts +2 -0
- package/dist/plugins/organization/error-codes.mjs +3 -1
- package/dist/plugins/organization/routes/crud-access-control.d.mts +2 -2
- package/dist/plugins/organization/routes/crud-invites.mjs +7 -2
- package/dist/plugins/organization/types.d.mts +12 -2
- package/dist/plugins/two-factor/index.mjs +3 -2
- package/dist/plugins/username/index.d.mts +24 -2
- package/dist/plugins/username/index.mjs +49 -3
- package/dist/state.d.mts +2 -2
- package/dist/state.mjs +18 -4
- package/dist/test-utils/headers.mjs +2 -7
- package/dist/test-utils/test-instance.d.mts +25 -6
- package/dist/test-utils/test-instance.mjs +11 -2
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/url.d.mts +2 -1
- package/dist/utils/url.mjs +9 -3
- package/package.json +15 -14
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AuthorizeResponse } from "../../access/access.mjs";
|
|
1
|
+
import { ExactRoleStatements, Role, RoleInput, Statements } from "../../access/types.mjs";
|
|
3
2
|
//#region src/plugins/organization/access/statement.d.ts
|
|
4
3
|
declare const defaultStatements: {
|
|
5
4
|
readonly organization: readonly ["update", "delete"];
|
|
@@ -9,137 +8,100 @@ declare const defaultStatements: {
|
|
|
9
8
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
10
9
|
};
|
|
11
10
|
declare const defaultAc: {
|
|
12
|
-
newRole<
|
|
11
|
+
newRole<const TRoleStatements extends Statements>(statements: RoleInput<{
|
|
13
12
|
readonly organization: readonly ["update", "delete"];
|
|
14
13
|
readonly member: readonly ["create", "update", "delete"];
|
|
15
14
|
readonly invitation: readonly ["create", "cancel"];
|
|
16
15
|
readonly team: readonly ["create", "update", "delete"];
|
|
17
16
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
18
|
-
}>): {
|
|
19
|
-
authorize<K_1 extends K>(request: K_1 extends infer T extends keyof Subset<K, {
|
|
20
|
-
readonly organization: readonly ["update", "delete"];
|
|
21
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
22
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
23
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
24
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
25
|
-
}> ? { [key in T]?: Subset<K, {
|
|
26
|
-
readonly organization: readonly ["update", "delete"];
|
|
27
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
28
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
29
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
30
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
31
|
-
}>[key] | {
|
|
32
|
-
actions: Subset<K, {
|
|
33
|
-
readonly organization: readonly ["update", "delete"];
|
|
34
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
35
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
36
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
37
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
38
|
-
}>[key];
|
|
39
|
-
connector: "OR" | "AND";
|
|
40
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
41
|
-
statements: Subset<K, {
|
|
42
|
-
readonly organization: readonly ["update", "delete"];
|
|
43
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
44
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
45
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
46
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
47
|
-
}>;
|
|
48
|
-
};
|
|
49
|
-
statements: {
|
|
50
|
-
readonly organization: readonly ["update", "delete"];
|
|
51
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
52
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
53
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
54
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
declare const adminAc: {
|
|
58
|
-
authorize<K extends "organization" | "member" | "team" | "ac" | "invitation">(request: K extends infer T extends keyof Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
59
|
-
readonly organization: readonly ["update", "delete"];
|
|
60
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
61
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
62
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
63
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
64
|
-
}> ? { [key in T]?: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
65
|
-
readonly organization: readonly ["update", "delete"];
|
|
66
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
67
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
68
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
69
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
70
|
-
}>[key] | {
|
|
71
|
-
actions: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
72
|
-
readonly organization: readonly ["update", "delete"];
|
|
73
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
74
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
75
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
76
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
77
|
-
}>[key];
|
|
78
|
-
connector: "OR" | "AND";
|
|
79
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
80
|
-
statements: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
17
|
+
}, TRoleStatements>): Role<ExactRoleStatements<TRoleStatements>, {
|
|
81
18
|
readonly organization: readonly ["update", "delete"];
|
|
82
19
|
readonly member: readonly ["create", "update", "delete"];
|
|
83
20
|
readonly invitation: readonly ["create", "cancel"];
|
|
84
21
|
readonly team: readonly ["create", "update", "delete"];
|
|
85
22
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
86
23
|
}>;
|
|
87
|
-
|
|
88
|
-
declare const ownerAc: {
|
|
89
|
-
authorize<K extends "organization" | "member" | "team" | "ac" | "invitation">(request: K extends infer T extends keyof Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
90
|
-
readonly organization: readonly ["update", "delete"];
|
|
91
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
92
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
93
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
94
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
95
|
-
}> ? { [key in T]?: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
96
|
-
readonly organization: readonly ["update", "delete"];
|
|
97
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
98
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
99
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
100
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
101
|
-
}>[key] | {
|
|
102
|
-
actions: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
103
|
-
readonly organization: readonly ["update", "delete"];
|
|
104
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
105
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
106
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
107
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
108
|
-
}>[key];
|
|
109
|
-
connector: "OR" | "AND";
|
|
110
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
111
|
-
statements: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
24
|
+
statements: {
|
|
112
25
|
readonly organization: readonly ["update", "delete"];
|
|
113
26
|
readonly member: readonly ["create", "update", "delete"];
|
|
114
27
|
readonly invitation: readonly ["create", "cancel"];
|
|
115
28
|
readonly team: readonly ["create", "update", "delete"];
|
|
116
29
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
117
|
-
}
|
|
30
|
+
};
|
|
118
31
|
};
|
|
119
|
-
declare const
|
|
120
|
-
|
|
32
|
+
declare const adminAc: Role<ExactRoleStatements<{
|
|
33
|
+
readonly organization: ["update"];
|
|
34
|
+
readonly invitation: ["create", "cancel"];
|
|
35
|
+
readonly member: ["create", "update", "delete"];
|
|
36
|
+
readonly team: ["create", "update", "delete"];
|
|
37
|
+
readonly ac: ["create", "read", "update", "delete"];
|
|
38
|
+
}>, {
|
|
39
|
+
readonly organization: readonly ["update", "delete"];
|
|
40
|
+
readonly member: readonly ["create", "update", "delete"];
|
|
41
|
+
readonly invitation: readonly ["create", "cancel"];
|
|
42
|
+
readonly team: readonly ["create", "update", "delete"];
|
|
43
|
+
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
44
|
+
}>;
|
|
45
|
+
declare const ownerAc: Role<ExactRoleStatements<{
|
|
46
|
+
readonly organization: ["update", "delete"];
|
|
47
|
+
readonly member: ["create", "update", "delete"];
|
|
48
|
+
readonly invitation: ["create", "cancel"];
|
|
49
|
+
readonly team: ["create", "update", "delete"];
|
|
50
|
+
readonly ac: ["create", "read", "update", "delete"];
|
|
51
|
+
}>, {
|
|
52
|
+
readonly organization: readonly ["update", "delete"];
|
|
53
|
+
readonly member: readonly ["create", "update", "delete"];
|
|
54
|
+
readonly invitation: readonly ["create", "cancel"];
|
|
55
|
+
readonly team: readonly ["create", "update", "delete"];
|
|
56
|
+
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
57
|
+
}>;
|
|
58
|
+
declare const memberAc: Role<ExactRoleStatements<{
|
|
59
|
+
readonly organization: [];
|
|
60
|
+
readonly member: [];
|
|
61
|
+
readonly invitation: [];
|
|
62
|
+
readonly team: [];
|
|
63
|
+
readonly ac: ["read"];
|
|
64
|
+
}>, {
|
|
65
|
+
readonly organization: readonly ["update", "delete"];
|
|
66
|
+
readonly member: readonly ["create", "update", "delete"];
|
|
67
|
+
readonly invitation: readonly ["create", "cancel"];
|
|
68
|
+
readonly team: readonly ["create", "update", "delete"];
|
|
69
|
+
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
70
|
+
}>;
|
|
71
|
+
declare const defaultRoles: {
|
|
72
|
+
admin: Role<ExactRoleStatements<{
|
|
73
|
+
readonly organization: ["update"];
|
|
74
|
+
readonly invitation: ["create", "cancel"];
|
|
75
|
+
readonly member: ["create", "update", "delete"];
|
|
76
|
+
readonly team: ["create", "update", "delete"];
|
|
77
|
+
readonly ac: ["create", "read", "update", "delete"];
|
|
78
|
+
}>, {
|
|
121
79
|
readonly organization: readonly ["update", "delete"];
|
|
122
80
|
readonly member: readonly ["create", "update", "delete"];
|
|
123
81
|
readonly invitation: readonly ["create", "cancel"];
|
|
124
82
|
readonly team: readonly ["create", "update", "delete"];
|
|
125
83
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
126
|
-
}
|
|
84
|
+
}>;
|
|
85
|
+
owner: Role<ExactRoleStatements<{
|
|
86
|
+
readonly organization: ["update", "delete"];
|
|
87
|
+
readonly member: ["create", "update", "delete"];
|
|
88
|
+
readonly invitation: ["create", "cancel"];
|
|
89
|
+
readonly team: ["create", "update", "delete"];
|
|
90
|
+
readonly ac: ["create", "read", "update", "delete"];
|
|
91
|
+
}>, {
|
|
127
92
|
readonly organization: readonly ["update", "delete"];
|
|
128
93
|
readonly member: readonly ["create", "update", "delete"];
|
|
129
94
|
readonly invitation: readonly ["create", "cancel"];
|
|
130
95
|
readonly team: readonly ["create", "update", "delete"];
|
|
131
96
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
connector: "OR" | "AND";
|
|
141
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
142
|
-
statements: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
97
|
+
}>;
|
|
98
|
+
member: Role<ExactRoleStatements<{
|
|
99
|
+
readonly organization: [];
|
|
100
|
+
readonly member: [];
|
|
101
|
+
readonly invitation: [];
|
|
102
|
+
readonly team: [];
|
|
103
|
+
readonly ac: ["read"];
|
|
104
|
+
}>, {
|
|
143
105
|
readonly organization: readonly ["update", "delete"];
|
|
144
106
|
readonly member: readonly ["create", "update", "delete"];
|
|
145
107
|
readonly invitation: readonly ["create", "cancel"];
|
|
@@ -147,100 +109,5 @@ declare const memberAc: {
|
|
|
147
109
|
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
148
110
|
}>;
|
|
149
111
|
};
|
|
150
|
-
declare const defaultRoles: {
|
|
151
|
-
admin: {
|
|
152
|
-
authorize<K extends "organization" | "member" | "team" | "ac" | "invitation">(request: K extends infer T extends keyof Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
153
|
-
readonly organization: readonly ["update", "delete"];
|
|
154
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
155
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
156
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
157
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
158
|
-
}> ? { [key in T]?: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
159
|
-
readonly organization: readonly ["update", "delete"];
|
|
160
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
161
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
162
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
163
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
164
|
-
}>[key] | {
|
|
165
|
-
actions: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
166
|
-
readonly organization: readonly ["update", "delete"];
|
|
167
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
168
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
169
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
170
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
171
|
-
}>[key];
|
|
172
|
-
connector: "OR" | "AND";
|
|
173
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
174
|
-
statements: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
175
|
-
readonly organization: readonly ["update", "delete"];
|
|
176
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
177
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
178
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
179
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
180
|
-
}>;
|
|
181
|
-
};
|
|
182
|
-
owner: {
|
|
183
|
-
authorize<K extends "organization" | "member" | "team" | "ac" | "invitation">(request: K extends infer T extends keyof Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
184
|
-
readonly organization: readonly ["update", "delete"];
|
|
185
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
186
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
187
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
188
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
189
|
-
}> ? { [key in T]?: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
190
|
-
readonly organization: readonly ["update", "delete"];
|
|
191
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
192
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
193
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
194
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
195
|
-
}>[key] | {
|
|
196
|
-
actions: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
197
|
-
readonly organization: readonly ["update", "delete"];
|
|
198
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
199
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
200
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
201
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
202
|
-
}>[key];
|
|
203
|
-
connector: "OR" | "AND";
|
|
204
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
205
|
-
statements: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
206
|
-
readonly organization: readonly ["update", "delete"];
|
|
207
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
208
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
209
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
210
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
211
|
-
}>;
|
|
212
|
-
};
|
|
213
|
-
member: {
|
|
214
|
-
authorize<K extends "organization" | "member" | "team" | "ac" | "invitation">(request: K extends infer T extends keyof Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
215
|
-
readonly organization: readonly ["update", "delete"];
|
|
216
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
217
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
218
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
219
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
220
|
-
}> ? { [key in T]?: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
221
|
-
readonly organization: readonly ["update", "delete"];
|
|
222
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
223
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
224
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
225
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
226
|
-
}>[key] | {
|
|
227
|
-
actions: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
228
|
-
readonly organization: readonly ["update", "delete"];
|
|
229
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
230
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
231
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
232
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
233
|
-
}>[key];
|
|
234
|
-
connector: "OR" | "AND";
|
|
235
|
-
} | undefined } : never, connector?: "OR" | "AND"): AuthorizeResponse;
|
|
236
|
-
statements: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
|
|
237
|
-
readonly organization: readonly ["update", "delete"];
|
|
238
|
-
readonly member: readonly ["create", "update", "delete"];
|
|
239
|
-
readonly invitation: readonly ["create", "cancel"];
|
|
240
|
-
readonly team: readonly ["create", "update", "delete"];
|
|
241
|
-
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
242
|
-
}>;
|
|
243
|
-
};
|
|
244
|
-
};
|
|
245
112
|
//#endregion
|
|
246
113
|
export { adminAc, defaultAc, defaultRoles, defaultStatements, memberAc, ownerAc };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getDate } from "../../utils/date.mjs";
|
|
2
2
|
import { parseJSON } from "../../client/parser.mjs";
|
|
3
|
-
import { getCurrentAdapter } from "@better-auth/core/context";
|
|
3
|
+
import { getCurrentAdapter, runWithTransaction } from "@better-auth/core/context";
|
|
4
4
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
5
5
|
import { filterOutputFields } from "@better-auth/core/utils/db";
|
|
6
6
|
//#region src/plugins/organization/adapter.ts
|
|
@@ -184,46 +184,49 @@ const getOrgAdapter = (context, options) => {
|
|
|
184
184
|
});
|
|
185
185
|
},
|
|
186
186
|
deleteMember: async ({ memberId, organizationId, userId: _userId }) => {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
return runWithTransaction(baseAdapter, async () => {
|
|
188
|
+
const adapter = await getCurrentAdapter(baseAdapter);
|
|
189
|
+
let userId;
|
|
190
|
+
if (!_userId) {
|
|
191
|
+
const member = await adapter.findOne({
|
|
192
|
+
model: "member",
|
|
193
|
+
where: [{
|
|
194
|
+
field: "id",
|
|
195
|
+
value: memberId
|
|
196
|
+
}]
|
|
197
|
+
});
|
|
198
|
+
if (!member) throw new BetterAuthError("Member not found");
|
|
199
|
+
userId = member.userId;
|
|
200
|
+
} else userId = _userId;
|
|
201
|
+
const member = await adapter.delete({
|
|
191
202
|
model: "member",
|
|
192
203
|
where: [{
|
|
193
204
|
field: "id",
|
|
194
205
|
value: memberId
|
|
195
206
|
}]
|
|
196
207
|
});
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
208
|
+
if (options?.teams?.enabled) {
|
|
209
|
+
const teams = await adapter.findMany({
|
|
210
|
+
model: "team",
|
|
211
|
+
where: [{
|
|
212
|
+
field: "organizationId",
|
|
213
|
+
value: organizationId
|
|
214
|
+
}]
|
|
215
|
+
});
|
|
216
|
+
if (teams.length > 0) await adapter.deleteMany({
|
|
217
|
+
model: "teamMember",
|
|
218
|
+
where: [{
|
|
219
|
+
field: "userId",
|
|
220
|
+
value: userId
|
|
221
|
+
}, {
|
|
222
|
+
field: "teamId",
|
|
223
|
+
value: teams.map((team) => team.id),
|
|
224
|
+
operator: "in"
|
|
225
|
+
}]
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return member;
|
|
206
229
|
});
|
|
207
|
-
if (options?.teams?.enabled) {
|
|
208
|
-
const teams = await adapter.findMany({
|
|
209
|
-
model: "team",
|
|
210
|
-
where: [{
|
|
211
|
-
field: "organizationId",
|
|
212
|
-
value: organizationId
|
|
213
|
-
}]
|
|
214
|
-
});
|
|
215
|
-
await Promise.all(teams.map((team) => adapter.deleteMany({
|
|
216
|
-
model: "teamMember",
|
|
217
|
-
where: [{
|
|
218
|
-
field: "teamId",
|
|
219
|
-
value: team.id
|
|
220
|
-
}, {
|
|
221
|
-
field: "userId",
|
|
222
|
-
value: userId
|
|
223
|
-
}]
|
|
224
|
-
})));
|
|
225
|
-
}
|
|
226
|
-
return member;
|
|
227
230
|
},
|
|
228
231
|
updateOrganization: async (organizationId, data) => {
|
|
229
232
|
const organization = await (await getCurrentAdapter(baseAdapter)).update({
|
|
@@ -244,29 +247,31 @@ const getOrgAdapter = (context, options) => {
|
|
|
244
247
|
}, orgAdditionalFields);
|
|
245
248
|
},
|
|
246
249
|
deleteOrganization: async (organizationId) => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
250
|
+
return runWithTransaction(baseAdapter, async () => {
|
|
251
|
+
const adapter = await getCurrentAdapter(baseAdapter);
|
|
252
|
+
await adapter.deleteMany({
|
|
253
|
+
model: "member",
|
|
254
|
+
where: [{
|
|
255
|
+
field: "organizationId",
|
|
256
|
+
value: organizationId
|
|
257
|
+
}]
|
|
258
|
+
});
|
|
259
|
+
await adapter.deleteMany({
|
|
260
|
+
model: "invitation",
|
|
261
|
+
where: [{
|
|
262
|
+
field: "organizationId",
|
|
263
|
+
value: organizationId
|
|
264
|
+
}]
|
|
265
|
+
});
|
|
266
|
+
await adapter.delete({
|
|
267
|
+
model: "organization",
|
|
268
|
+
where: [{
|
|
269
|
+
field: "id",
|
|
270
|
+
value: organizationId
|
|
271
|
+
}]
|
|
272
|
+
});
|
|
273
|
+
return organizationId;
|
|
268
274
|
});
|
|
269
|
-
return organizationId;
|
|
270
275
|
},
|
|
271
276
|
setActiveOrganization: async (sessionToken, organizationId, ctx) => {
|
|
272
277
|
return await context.internalAdapter.updateSession(sessionToken, { activeOrganizationId: organizationId });
|
|
@@ -238,6 +238,7 @@ declare const organizationClient: <CO extends OrganizationClientOptions>(options
|
|
|
238
238
|
INVITATION_NOT_FOUND: _better_auth_core_utils_error_codes0.RawError<"INVITATION_NOT_FOUND">;
|
|
239
239
|
YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: _better_auth_core_utils_error_codes0.RawError<"YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION">;
|
|
240
240
|
EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION: _better_auth_core_utils_error_codes0.RawError<"EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION">;
|
|
241
|
+
EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION: _better_auth_core_utils_error_codes0.RawError<"EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION">;
|
|
241
242
|
YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: _better_auth_core_utils_error_codes0.RawError<"YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION">;
|
|
242
243
|
INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: _better_auth_core_utils_error_codes0.RawError<"INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION">;
|
|
243
244
|
YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: _better_auth_core_utils_error_codes0.RawError<"YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE">;
|
|
@@ -272,6 +273,7 @@ declare const organizationClient: <CO extends OrganizationClientOptions>(options
|
|
|
272
273
|
ROLE_NAME_IS_ALREADY_TAKEN: _better_auth_core_utils_error_codes0.RawError<"ROLE_NAME_IS_ALREADY_TAKEN">;
|
|
273
274
|
CANNOT_DELETE_A_PRE_DEFINED_ROLE: _better_auth_core_utils_error_codes0.RawError<"CANNOT_DELETE_A_PRE_DEFINED_ROLE">;
|
|
274
275
|
ROLE_IS_ASSIGNED_TO_MEMBERS: _better_auth_core_utils_error_codes0.RawError<"ROLE_IS_ASSIGNED_TO_MEMBERS">;
|
|
276
|
+
INVALID_TEAM_ID: _better_auth_core_utils_error_codes0.RawError<"INVALID_TEAM_ID">;
|
|
275
277
|
};
|
|
276
278
|
};
|
|
277
279
|
declare const inferOrgAdditionalFields: <O extends {
|
|
@@ -370,4 +372,4 @@ declare const inferOrgAdditionalFields: <O extends {
|
|
|
370
372
|
additionalFields: unknown;
|
|
371
373
|
} ? K : never]: S_1[K] } : undefined : undefined : undefined : S;
|
|
372
374
|
//#endregion
|
|
373
|
-
export { clientSideHasPermission, inferOrgAdditionalFields, organizationClient };
|
|
375
|
+
export { OrganizationClientOptions, clientSideHasPermission, inferOrgAdditionalFields, organizationClient };
|
|
@@ -25,6 +25,7 @@ declare const ORGANIZATION_ERROR_CODES: {
|
|
|
25
25
|
INVITATION_NOT_FOUND: _better_auth_core_utils_error_codes0.RawError<"INVITATION_NOT_FOUND">;
|
|
26
26
|
YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: _better_auth_core_utils_error_codes0.RawError<"YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION">;
|
|
27
27
|
EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION: _better_auth_core_utils_error_codes0.RawError<"EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION">;
|
|
28
|
+
EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION: _better_auth_core_utils_error_codes0.RawError<"EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION">;
|
|
28
29
|
YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: _better_auth_core_utils_error_codes0.RawError<"YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION">;
|
|
29
30
|
INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: _better_auth_core_utils_error_codes0.RawError<"INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION">;
|
|
30
31
|
YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: _better_auth_core_utils_error_codes0.RawError<"YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE">;
|
|
@@ -59,6 +60,7 @@ declare const ORGANIZATION_ERROR_CODES: {
|
|
|
59
60
|
ROLE_NAME_IS_ALREADY_TAKEN: _better_auth_core_utils_error_codes0.RawError<"ROLE_NAME_IS_ALREADY_TAKEN">;
|
|
60
61
|
CANNOT_DELETE_A_PRE_DEFINED_ROLE: _better_auth_core_utils_error_codes0.RawError<"CANNOT_DELETE_A_PRE_DEFINED_ROLE">;
|
|
61
62
|
ROLE_IS_ASSIGNED_TO_MEMBERS: _better_auth_core_utils_error_codes0.RawError<"ROLE_IS_ASSIGNED_TO_MEMBERS">;
|
|
63
|
+
INVALID_TEAM_ID: _better_auth_core_utils_error_codes0.RawError<"INVALID_TEAM_ID">;
|
|
62
64
|
};
|
|
63
65
|
//#endregion
|
|
64
66
|
export { ORGANIZATION_ERROR_CODES };
|
|
@@ -24,6 +24,7 @@ const ORGANIZATION_ERROR_CODES = defineErrorCodes({
|
|
|
24
24
|
INVITATION_NOT_FOUND: "Invitation not found",
|
|
25
25
|
YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: "You are not the recipient of the invitation",
|
|
26
26
|
EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION: "Email verification required before accepting or rejecting invitation",
|
|
27
|
+
EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION: "Email verification required to view or list invitations for the session email",
|
|
27
28
|
YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: "You are not allowed to cancel this invitation",
|
|
28
29
|
INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: "Inviter is no longer a member of the organization",
|
|
29
30
|
YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: "You are not allowed to invite a user with this role",
|
|
@@ -57,7 +58,8 @@ const ORGANIZATION_ERROR_CODES = defineErrorCodes({
|
|
|
57
58
|
INVALID_RESOURCE: "The provided permission includes an invalid resource",
|
|
58
59
|
ROLE_NAME_IS_ALREADY_TAKEN: "That role name is already taken",
|
|
59
60
|
CANNOT_DELETE_A_PRE_DEFINED_ROLE: "Cannot delete a pre-defined role",
|
|
60
|
-
ROLE_IS_ASSIGNED_TO_MEMBERS: "Cannot delete a role that is assigned to members. Please reassign the members to a different role first"
|
|
61
|
+
ROLE_IS_ASSIGNED_TO_MEMBERS: "Cannot delete a role that is assigned to members. Please reassign the members to a different role first",
|
|
62
|
+
INVALID_TEAM_ID: "Team id contains a reserved character"
|
|
61
63
|
});
|
|
62
64
|
//#endregion
|
|
63
65
|
export { ORGANIZATION_ERROR_CODES };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InferAdditionalFieldsFromPluginOptions } from "../../../db/field.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { ExactRoleStatements } from "../../access/types.mjs";
|
|
3
3
|
import { OrganizationOptions } from "../types.mjs";
|
|
4
4
|
import { OrganizationRole } from "../schema.mjs";
|
|
5
5
|
import * as better_call0 from "better-call";
|
|
@@ -91,7 +91,7 @@ declare const createOrgRole: <O extends OrganizationOptions>(options: O) => bett
|
|
|
91
91
|
createdAt: Date;
|
|
92
92
|
updatedAt?: Date | undefined;
|
|
93
93
|
} & InferAdditionalFieldsFromPluginOptions<"organizationRole", O, false>;
|
|
94
|
-
statements:
|
|
94
|
+
statements: ExactRoleStatements<Record<string, string[]>>;
|
|
95
95
|
}>;
|
|
96
96
|
declare const deleteOrgRole: <O extends OrganizationOptions>(options: O) => better_call0.StrictEndpoint<"/organization/delete-role", {
|
|
97
97
|
method: "POST";
|
|
@@ -155,6 +155,9 @@ const createInvitation = (option) => {
|
|
|
155
155
|
member
|
|
156
156
|
}, ctx.context) : ctx.context.orgOptions.invitationLimit ?? 100;
|
|
157
157
|
if ((await adapter.findPendingInvitations({ organizationId })).length >= invitationLimit) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.INVITATION_LIMIT_REACHED);
|
|
158
|
+
if (ctx.context.orgOptions.teams?.enabled && "teamId" in ctx.body && ctx.body.teamId) {
|
|
159
|
+
if ((typeof ctx.body.teamId === "string" ? [ctx.body.teamId] : ctx.body.teamId).some((id) => id.includes(","))) throw APIError.from("BAD_REQUEST", ORGANIZATION_ERROR_CODES.INVALID_TEAM_ID);
|
|
160
|
+
}
|
|
158
161
|
if (ctx.context.orgOptions.teams && ctx.context.orgOptions.teams.enabled && typeof ctx.context.orgOptions.teams.maximumMembersPerTeam !== "undefined" && "teamId" in ctx.body && ctx.body.teamId) {
|
|
159
162
|
const teamIds = typeof ctx.body.teamId === "string" ? [ctx.body.teamId] : ctx.body.teamId;
|
|
160
163
|
for (const teamId of teamIds) {
|
|
@@ -244,7 +247,7 @@ const acceptInvitation = (options) => createAuthEndpoint("/organization/accept-i
|
|
|
244
247
|
const invitation = await adapter.findInvitationById(ctx.body.invitationId);
|
|
245
248
|
if (!invitation || invitation.expiresAt < /* @__PURE__ */ new Date() || invitation.status !== "pending") throw APIError.from("BAD_REQUEST", ORGANIZATION_ERROR_CODES.INVITATION_NOT_FOUND);
|
|
246
249
|
if (invitation.email.toLowerCase() !== session.user.email.toLowerCase()) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION);
|
|
247
|
-
if (ctx.context.orgOptions.requireEmailVerificationOnInvitation && !session.user.emailVerified) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION);
|
|
250
|
+
if ((ctx.context.orgOptions.requireEmailVerificationOnInvitation ?? true) && !session.user.emailVerified) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION);
|
|
248
251
|
const membershipLimit = ctx.context.orgOptions?.membershipLimit || 100;
|
|
249
252
|
const membersCount = await adapter.countMembers({ organizationId: invitation.organizationId });
|
|
250
253
|
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
@@ -333,7 +336,7 @@ const rejectInvitation = (options) => createAuthEndpoint("/organization/reject-i
|
|
|
333
336
|
code: "INVITATION_NOT_FOUND"
|
|
334
337
|
});
|
|
335
338
|
if (invitation.email.toLowerCase() !== session.user.email.toLowerCase()) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION);
|
|
336
|
-
if (ctx.context.orgOptions.requireEmailVerificationOnInvitation && !session.user.emailVerified) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION);
|
|
339
|
+
if ((ctx.context.orgOptions.requireEmailVerificationOnInvitation ?? true) && !session.user.emailVerified) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION);
|
|
337
340
|
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
338
341
|
if (!organization) throw APIError.from("BAD_REQUEST", ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND);
|
|
339
342
|
if (options?.organizationHooks?.beforeRejectInvitation) await options?.organizationHooks.beforeRejectInvitation({
|
|
@@ -452,6 +455,7 @@ const getInvitation = (options) => createAuthEndpoint("/organization/get-invitat
|
|
|
452
455
|
const invitation = await adapter.findInvitationById(ctx.query.id);
|
|
453
456
|
if (!invitation || invitation.status !== "pending" || invitation.expiresAt < /* @__PURE__ */ new Date()) throw APIError.fromStatus("BAD_REQUEST", { message: "Invitation not found!" });
|
|
454
457
|
if (invitation.email.toLowerCase() !== session.user.email.toLowerCase()) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION);
|
|
458
|
+
if ((ctx.context.orgOptions.requireEmailVerificationOnInvitation ?? true) && !session.user.emailVerified) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION);
|
|
455
459
|
const organization = await adapter.findOrganizationById(invitation.organizationId);
|
|
456
460
|
if (!organization) throw APIError.from("BAD_REQUEST", ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND);
|
|
457
461
|
const member = await adapter.findMemberByOrgId({
|
|
@@ -537,6 +541,7 @@ const listUserInvitations = (options) => createAuthEndpoint("/organization/list-
|
|
|
537
541
|
}, async (ctx) => {
|
|
538
542
|
const session = await getSessionFromCtx(ctx);
|
|
539
543
|
if (ctx.request && ctx.query?.email) throw APIError.fromStatus("BAD_REQUEST", { message: "User email cannot be passed for client side API calls." });
|
|
544
|
+
if (session && (ctx.context.orgOptions.requireEmailVerificationOnInvitation ?? true) && !session.user.emailVerified) throw APIError.from("FORBIDDEN", ORGANIZATION_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION);
|
|
540
545
|
const userEmail = session?.user.email || ctx.query?.email;
|
|
541
546
|
if (!userEmail) throw APIError.fromStatus("BAD_REQUEST", { message: "Missing session headers, or email query parameter." });
|
|
542
547
|
const pendingInvitations = (await getOrgAdapter(ctx.context, options).listUserInvitations(userEmail)).filter((inv) => inv.status === "pending");
|
|
@@ -165,9 +165,19 @@ interface OrganizationOptions {
|
|
|
165
165
|
*/
|
|
166
166
|
cancelPendingInvitationsOnReInvite?: boolean | undefined;
|
|
167
167
|
/**
|
|
168
|
-
* Require email verification on
|
|
168
|
+
* Require email verification on session-authenticated recipient invitation
|
|
169
|
+
* calls (accept, reject, get, list). Defaults to `true` so unverified
|
|
170
|
+
* accounts registered against a victim's email cannot accept, read, or
|
|
171
|
+
* enumerate invitations targeted at that email. Server-side
|
|
172
|
+
* `listUserInvitations` calls without a session (caller passes
|
|
173
|
+
* `ctx.query.email`) continue to bypass the gate because the caller is
|
|
174
|
+
* trusted. Set to `false` for backward compatibility on apps that do not
|
|
175
|
+
* require email verification; understand the takeover risk before doing so.
|
|
169
176
|
*
|
|
170
|
-
* @default
|
|
177
|
+
* @default true
|
|
178
|
+
*
|
|
179
|
+
* @deprecated The option will be removed on the next minor; the gate will
|
|
180
|
+
* become unconditional. Plan to verify emails before invitation acceptance.
|
|
171
181
|
*/
|
|
172
182
|
requireEmailVerificationOnInvitation?: boolean | undefined;
|
|
173
183
|
/**
|
|
@@ -2,7 +2,7 @@ import { mergeSchema } from "../../db/schema.mjs";
|
|
|
2
2
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
3
|
import { symmetricEncrypt } from "../../crypto/index.mjs";
|
|
4
4
|
import { deleteSessionCookie, expireCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
5
|
-
import { sessionMiddleware } from "../../api/routes/session.mjs";
|
|
5
|
+
import { sensitiveSessionMiddleware, sessionMiddleware } from "../../api/routes/session.mjs";
|
|
6
6
|
import { shouldRequirePassword, validatePassword } from "../../utils/password.mjs";
|
|
7
7
|
import { PACKAGE_VERSION } from "../../version.mjs";
|
|
8
8
|
import { TWO_FACTOR_ERROR_CODES } from "./error-code.mjs";
|
|
@@ -138,7 +138,7 @@ const twoFactor = (options) => {
|
|
|
138
138
|
disableTwoFactor: createAuthEndpoint("/two-factor/disable", {
|
|
139
139
|
method: "POST",
|
|
140
140
|
body: disableTwoFactorBodySchema,
|
|
141
|
-
use: [
|
|
141
|
+
use: [sensitiveSessionMiddleware],
|
|
142
142
|
metadata: { openapi: {
|
|
143
143
|
summary: "Disable two factor authentication",
|
|
144
144
|
description: "Use this endpoint to disable two factor authentication.",
|
|
@@ -224,6 +224,7 @@ const twoFactor = (options) => {
|
|
|
224
224
|
*/
|
|
225
225
|
deleteSessionCookie(ctx, true);
|
|
226
226
|
await ctx.context.internalAdapter.deleteSession(data.session.token);
|
|
227
|
+
ctx.context.setNewSession(null);
|
|
227
228
|
const maxAge = options?.twoFactorCookieMaxAge ?? 600;
|
|
228
229
|
const twoFactorCookie = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME, { maxAge });
|
|
229
230
|
const identifier = `2fa-${generateRandomString(20)}`;
|