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.
Files changed (90) hide show
  1. package/dist/api/index.d.mts +8 -2
  2. package/dist/api/routes/callback.d.mts +1 -1
  3. package/dist/api/routes/callback.mjs +36 -40
  4. package/dist/api/routes/email-verification.d.mts +1 -0
  5. package/dist/api/routes/email-verification.mjs +4 -3
  6. package/dist/api/routes/session.mjs +14 -9
  7. package/dist/api/routes/sign-in.d.mts +1 -0
  8. package/dist/api/routes/sign-in.mjs +2 -1
  9. package/dist/api/routes/sign-up.d.mts +1 -0
  10. package/dist/api/routes/sign-up.mjs +9 -7
  11. package/dist/api/routes/update-user.mjs +5 -5
  12. package/dist/client/index.d.mts +2 -2
  13. package/dist/client/parser.mjs +0 -1
  14. package/dist/client/plugins/index.d.mts +3 -3
  15. package/dist/client/proxy.mjs +2 -1
  16. package/dist/context/helpers.mjs +3 -2
  17. package/dist/cookies/cookie-utils.d.mts +24 -1
  18. package/dist/cookies/cookie-utils.mjs +85 -22
  19. package/dist/cookies/index.d.mts +2 -3
  20. package/dist/cookies/index.mjs +39 -11
  21. package/dist/cookies/session-store.mjs +4 -23
  22. package/dist/db/get-migration.mjs +4 -4
  23. package/dist/db/index.d.mts +2 -2
  24. package/dist/db/index.mjs +3 -2
  25. package/dist/db/internal-adapter.mjs +96 -1
  26. package/dist/db/schema.d.mts +15 -2
  27. package/dist/db/schema.mjs +26 -1
  28. package/dist/db/with-hooks.d.mts +1 -0
  29. package/dist/db/with-hooks.mjs +58 -1
  30. package/dist/index.d.mts +2 -2
  31. package/dist/index.mjs +2 -2
  32. package/dist/oauth2/errors.mjs +16 -1
  33. package/dist/oauth2/link-account.mjs +6 -4
  34. package/dist/oauth2/state.mjs +8 -2
  35. package/dist/package.mjs +1 -1
  36. package/dist/plugins/access/access.d.mts +3 -15
  37. package/dist/plugins/access/access.mjs +11 -6
  38. package/dist/plugins/access/index.d.mts +2 -2
  39. package/dist/plugins/access/types.d.mts +11 -4
  40. package/dist/plugins/admin/access/statement.d.mts +29 -93
  41. package/dist/plugins/admin/admin.mjs +0 -4
  42. package/dist/plugins/admin/client.d.mts +1 -1
  43. package/dist/plugins/admin/routes.mjs +1 -0
  44. package/dist/plugins/anonymous/client.d.mts +1 -0
  45. package/dist/plugins/anonymous/error-codes.d.mts +1 -0
  46. package/dist/plugins/anonymous/error-codes.mjs +1 -0
  47. package/dist/plugins/anonymous/index.d.mts +1 -0
  48. package/dist/plugins/anonymous/index.mjs +16 -2
  49. package/dist/plugins/bearer/index.mjs +4 -9
  50. package/dist/plugins/captcha/index.mjs +2 -2
  51. package/dist/plugins/device-authorization/error-codes.mjs +1 -0
  52. package/dist/plugins/device-authorization/index.d.mts +1 -0
  53. package/dist/plugins/device-authorization/routes.mjs +34 -3
  54. package/dist/plugins/generic-oauth/index.d.mts +1 -1
  55. package/dist/plugins/generic-oauth/index.mjs +6 -6
  56. package/dist/plugins/generic-oauth/routes.mjs +34 -32
  57. package/dist/plugins/generic-oauth/types.d.mts +7 -0
  58. package/dist/plugins/index.d.mts +2 -2
  59. package/dist/plugins/last-login-method/client.mjs +2 -2
  60. package/dist/plugins/magic-link/index.d.mts +8 -1
  61. package/dist/plugins/magic-link/index.mjs +4 -17
  62. package/dist/plugins/mcp/authorize.mjs +8 -2
  63. package/dist/plugins/mcp/index.mjs +73 -34
  64. package/dist/plugins/multi-session/index.mjs +2 -2
  65. package/dist/plugins/oauth-proxy/index.mjs +44 -31
  66. package/dist/plugins/oauth-proxy/utils.mjs +3 -10
  67. package/dist/plugins/oidc-provider/authorize.mjs +8 -2
  68. package/dist/plugins/oidc-provider/index.mjs +63 -37
  69. package/dist/plugins/one-tap/index.mjs +13 -8
  70. package/dist/plugins/open-api/generator.mjs +16 -5
  71. package/dist/plugins/organization/access/statement.d.mts +68 -201
  72. package/dist/plugins/organization/adapter.mjs +61 -56
  73. package/dist/plugins/organization/client.d.mts +3 -1
  74. package/dist/plugins/organization/error-codes.d.mts +2 -0
  75. package/dist/plugins/organization/error-codes.mjs +3 -1
  76. package/dist/plugins/organization/routes/crud-access-control.d.mts +2 -2
  77. package/dist/plugins/organization/routes/crud-invites.mjs +7 -2
  78. package/dist/plugins/organization/types.d.mts +12 -2
  79. package/dist/plugins/two-factor/index.mjs +3 -2
  80. package/dist/plugins/username/index.d.mts +24 -2
  81. package/dist/plugins/username/index.mjs +49 -3
  82. package/dist/state.d.mts +2 -2
  83. package/dist/state.mjs +18 -4
  84. package/dist/test-utils/headers.mjs +2 -7
  85. package/dist/test-utils/test-instance.d.mts +25 -6
  86. package/dist/test-utils/test-instance.mjs +11 -2
  87. package/dist/utils/index.d.mts +1 -1
  88. package/dist/utils/url.d.mts +2 -1
  89. package/dist/utils/url.mjs +9 -3
  90. package/package.json +15 -14
@@ -1,5 +1,4 @@
1
- import { Subset } from "../../access/types.mjs";
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<K extends "organization" | "member" | "team" | "ac" | "invitation">(statements: Subset<K, {
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 memberAc: {
120
- authorize<K extends "organization" | "member" | "team" | "ac" | "invitation">(request: K extends infer T extends keyof Subset<"organization" | "member" | "team" | "ac" | "invitation", {
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
- }> ? { [key in T]?: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
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
- }>[key] | {
133
- actions: Subset<"organization" | "member" | "team" | "ac" | "invitation", {
134
- readonly organization: readonly ["update", "delete"];
135
- readonly member: readonly ["create", "update", "delete"];
136
- readonly invitation: readonly ["create", "cancel"];
137
- readonly team: readonly ["create", "update", "delete"];
138
- readonly ac: readonly ["create", "read", "update", "delete"];
139
- }>[key];
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
- const adapter = await getCurrentAdapter(baseAdapter);
188
- let userId;
189
- if (!_userId) {
190
- const member = await adapter.findOne({
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 (!member) throw new BetterAuthError("Member not found");
198
- userId = member.userId;
199
- } else userId = _userId;
200
- const member = await adapter.delete({
201
- model: "member",
202
- where: [{
203
- field: "id",
204
- value: memberId
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
- const adapter = await getCurrentAdapter(baseAdapter);
248
- await adapter.deleteMany({
249
- model: "member",
250
- where: [{
251
- field: "organizationId",
252
- value: organizationId
253
- }]
254
- });
255
- await adapter.deleteMany({
256
- model: "invitation",
257
- where: [{
258
- field: "organizationId",
259
- value: organizationId
260
- }]
261
- });
262
- await adapter.delete({
263
- model: "organization",
264
- where: [{
265
- field: "id",
266
- value: organizationId
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 { Statements, Subset } from "../../access/types.mjs";
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: Subset<string, 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 accepting or rejecting an invitation
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 false
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: [sessionMiddleware],
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)}`;