@better-auth-ui/react 1.6.8 → 1.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/dist/components/auth/auth-context.d.ts +3 -0
  2. package/dist/components/auth/email/email-localization.d.ts +11 -3
  3. package/dist/components/auth/email/index.d.ts +1 -0
  4. package/dist/components/auth/email/organization-invitation.d.ts +121 -0
  5. package/dist/components/mutation-invalidator.d.ts +1 -0
  6. package/dist/email.js +1348 -11
  7. package/dist/hooks/auth/use-authenticate.d.ts +1 -1
  8. package/dist/hooks/auth/use-user.d.ts +12 -12
  9. package/dist/hooks/use-auth-query.d.ts +1 -1
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +2469 -79
  12. package/dist/lib/auth-client.d.ts +4 -1
  13. package/dist/lib/auth-plugin.d.ts +16 -0
  14. package/dist/lib/auth-server.d.ts +8 -1
  15. package/dist/lib/settings-tab.d.ts +19 -0
  16. package/dist/mutations/api-key/create-api-key-mutation.d.ts +6 -4
  17. package/dist/mutations/api-key/delete-api-key-mutation.d.ts +7 -3
  18. package/dist/mutations/auth/sign-in-email-mutation.d.ts +5 -5
  19. package/dist/mutations/auth/sign-out-mutation.d.ts +6 -4
  20. package/dist/mutations/auth/sign-up-email-mutation.d.ts +5 -5
  21. package/dist/mutations/multi-session/revoke-multi-session-mutation.d.ts +5 -5
  22. package/dist/mutations/multi-session/set-active-session-mutation.d.ts +5 -6
  23. package/dist/mutations/organization/accept-invitation-mutation.d.ts +43 -0
  24. package/dist/mutations/organization/cancel-invitation-mutation.d.ts +25 -0
  25. package/dist/mutations/organization/check-slug-mutation.d.ts +21 -0
  26. package/dist/mutations/organization/create-organization-mutation.d.ts +39 -0
  27. package/dist/mutations/organization/delete-organization-mutation.d.ts +21 -0
  28. package/dist/mutations/organization/index.d.ts +12 -0
  29. package/dist/mutations/organization/invite-member-mutation.d.ts +43 -0
  30. package/dist/mutations/organization/leave-organization-mutation.d.ts +65 -0
  31. package/dist/mutations/organization/reject-invitation-mutation.d.ts +31 -0
  32. package/dist/mutations/organization/remove-member-mutation.d.ts +35 -0
  33. package/dist/mutations/organization/set-active-organization-mutation.d.ts +71 -0
  34. package/dist/mutations/organization/update-member-role-mutation.d.ts +31 -0
  35. package/dist/mutations/organization/update-organization-mutation.d.ts +21 -0
  36. package/dist/mutations/passkey/add-passkey-mutation.d.ts +5 -5
  37. package/dist/mutations/passkey/delete-passkey-mutation.d.ts +5 -5
  38. package/dist/mutations/passkey/sign-in-passkey-mutation.d.ts +5 -5
  39. package/dist/mutations/settings/change-email-mutation.d.ts +6 -5
  40. package/dist/mutations/settings/revoke-session-mutation.d.ts +5 -5
  41. package/dist/mutations/settings/unlink-account-mutation.d.ts +5 -5
  42. package/dist/mutations/settings/update-user-mutation.d.ts +5 -5
  43. package/dist/mutations/username/sign-in-username-mutation.d.ts +5 -5
  44. package/dist/plugins/captcha-plugin.d.ts +2 -2
  45. package/dist/plugins.js +27 -2
  46. package/dist/queries/api-key/list-api-keys-query.d.ts +17 -8
  47. package/dist/queries/auth/session-query.d.ts +1 -1
  48. package/dist/queries/multi-session/list-device-sessions-query.d.ts +5 -5
  49. package/dist/queries/organization/active-organization-query.d.ts +39 -0
  50. package/dist/queries/organization/full-organization-query.d.ts +37 -0
  51. package/dist/queries/organization/has-permission-query.d.ts +21 -0
  52. package/dist/queries/organization/index.d.ts +7 -0
  53. package/dist/queries/organization/list-invitations-query.d.ts +29 -0
  54. package/dist/queries/organization/list-members-query.d.ts +61 -0
  55. package/dist/queries/organization/list-organizations-query.d.ts +22 -0
  56. package/dist/queries/organization/list-user-invitations-query.d.ts +29 -0
  57. package/dist/queries/passkey/list-passkeys-query.d.ts +5 -5
  58. package/dist/queries/settings/account-info-query.d.ts +1 -1
  59. package/dist/queries/settings/list-accounts-query.d.ts +1 -1
  60. package/dist/queries/settings/list-sessions-query.d.ts +1 -1
  61. package/dist/server/queries/api-key/list-api-keys-query.d.ts +83 -0
  62. package/dist/server/queries/multi-session/list-device-sessions-query.d.ts +4 -4
  63. package/dist/server/queries/organization/active-organization-query.d.ts +61 -0
  64. package/dist/server/queries/organization/full-organization-query.d.ts +42 -0
  65. package/dist/server/queries/organization/has-permission-query.d.ts +66 -0
  66. package/dist/server/queries/organization/list-invitations-query.d.ts +34 -0
  67. package/dist/server/queries/organization/list-members-query.d.ts +66 -0
  68. package/dist/server/queries/organization/list-organizations-query.d.ts +27 -0
  69. package/dist/server/queries/organization/list-user-invitations-query.d.ts +34 -0
  70. package/dist/server/queries/passkey/list-passkeys-query.d.ts +4 -4
  71. package/dist/server.d.ts +8 -0
  72. package/dist/server.js +131 -7
  73. package/dist/use-auth-plugin-ClcP84Uz.js +80 -0
  74. package/package.json +19 -16
  75. package/src/components/auth/auth-context.tsx +7 -0
  76. package/src/components/auth/auth-provider.tsx +19 -31
  77. package/src/components/auth/email/email-localization.tsx +5 -3
  78. package/src/components/auth/email/index.ts +1 -0
  79. package/src/components/auth/email/organization-invitation.tsx +454 -0
  80. package/src/components/mutation-invalidator.tsx +67 -0
  81. package/src/index.ts +3 -0
  82. package/src/lib/auth-client.ts +7 -0
  83. package/src/lib/auth-plugin.ts +17 -0
  84. package/src/lib/auth-server.ts +17 -1
  85. package/src/lib/settings-tab.ts +21 -0
  86. package/src/mutations/api-key/create-api-key-mutation.ts +34 -19
  87. package/src/mutations/api-key/delete-api-key-mutation.ts +36 -19
  88. package/src/mutations/auth/sign-in-email-mutation.ts +18 -22
  89. package/src/mutations/auth/sign-out-mutation.ts +19 -13
  90. package/src/mutations/auth/sign-up-email-mutation.ts +18 -22
  91. package/src/mutations/multi-session/revoke-multi-session-mutation.ts +31 -20
  92. package/src/mutations/multi-session/set-active-session-mutation.ts +25 -44
  93. package/src/mutations/organization/accept-invitation-mutation.ts +71 -0
  94. package/src/mutations/organization/cancel-invitation-mutation.ts +67 -0
  95. package/src/mutations/organization/check-slug-mutation.ts +55 -0
  96. package/src/mutations/organization/create-organization-mutation.ts +71 -0
  97. package/src/mutations/organization/delete-organization-mutation.ts +71 -0
  98. package/src/mutations/organization/index.ts +12 -0
  99. package/src/mutations/organization/invite-member-mutation.ts +81 -0
  100. package/src/mutations/organization/leave-organization-mutation.ts +74 -0
  101. package/src/mutations/organization/reject-invitation-mutation.ts +64 -0
  102. package/src/mutations/organization/remove-member-mutation.ts +67 -0
  103. package/src/mutations/organization/set-active-organization-mutation.ts +142 -0
  104. package/src/mutations/organization/update-member-role-mutation.ts +81 -0
  105. package/src/mutations/organization/update-organization-mutation.ts +84 -0
  106. package/src/mutations/passkey/add-passkey-mutation.ts +28 -17
  107. package/src/mutations/passkey/delete-passkey-mutation.ts +27 -17
  108. package/src/mutations/passkey/sign-in-passkey-mutation.ts +18 -21
  109. package/src/mutations/settings/change-email-mutation.ts +22 -18
  110. package/src/mutations/settings/delete-user-mutation.ts +2 -2
  111. package/src/mutations/settings/revoke-session-mutation.ts +24 -19
  112. package/src/mutations/settings/unlink-account-mutation.ts +24 -17
  113. package/src/mutations/settings/update-user-mutation.ts +18 -30
  114. package/src/mutations/username/is-username-available-mutation.ts +2 -2
  115. package/src/mutations/username/sign-in-username-mutation.ts +20 -23
  116. package/src/queries/api-key/list-api-keys-query.ts +20 -8
  117. package/src/queries/multi-session/list-device-sessions-query.ts +2 -2
  118. package/src/queries/organization/active-organization-query.ts +140 -0
  119. package/src/queries/organization/full-organization-query.ts +113 -0
  120. package/src/queries/organization/has-permission-query.ts +124 -0
  121. package/src/queries/organization/index.ts +7 -0
  122. package/src/queries/organization/list-invitations-query.ts +133 -0
  123. package/src/queries/organization/list-members-query.ts +133 -0
  124. package/src/queries/organization/list-organizations-query.ts +122 -0
  125. package/src/queries/organization/list-user-invitations-query.ts +122 -0
  126. package/src/queries/passkey/list-passkeys-query.ts +2 -2
  127. package/src/server/queries/api-key/list-api-keys-query.ts +94 -0
  128. package/src/server/queries/multi-session/list-device-sessions-query.ts +2 -2
  129. package/src/server/queries/organization/active-organization-query.ts +92 -0
  130. package/src/server/queries/organization/full-organization-query.ts +64 -0
  131. package/src/server/queries/organization/has-permission-query.ts +66 -0
  132. package/src/server/queries/organization/list-invitations-query.ts +81 -0
  133. package/src/server/queries/organization/list-members-query.ts +75 -0
  134. package/src/server/queries/organization/list-organizations-query.ts +67 -0
  135. package/src/server/queries/organization/list-user-invitations-query.ts +68 -0
  136. package/src/server/queries/passkey/list-passkeys-query.ts +2 -2
  137. package/src/server.ts +8 -0
  138. package/dist/components/auth/auth-provider.js +0 -44
  139. package/dist/components/auth/email/email-changed.js +0 -156
  140. package/dist/components/auth/email/email-localization.js +0 -19
  141. package/dist/components/auth/email/email-styles.js +0 -104
  142. package/dist/components/auth/email/email-verification.js +0 -140
  143. package/dist/components/auth/email/index.js +0 -9
  144. package/dist/components/auth/email/magic-link.js +0 -141
  145. package/dist/components/auth/email/new-device.js +0 -223
  146. package/dist/components/auth/email/otp-email.js +0 -129
  147. package/dist/components/auth/email/password-changed.js +0 -159
  148. package/dist/components/auth/email/reset-password.js +0 -140
  149. package/dist/components/auth/fetch-options-provider.js +0 -30
  150. package/dist/components/icons/apple.js +0 -17
  151. package/dist/components/icons/atlassian.js +0 -39
  152. package/dist/components/icons/cognito.js +0 -20
  153. package/dist/components/icons/discord.js +0 -17
  154. package/dist/components/icons/dropbox.js +0 -17
  155. package/dist/components/icons/facebook.js +0 -20
  156. package/dist/components/icons/figma.js +0 -35
  157. package/dist/components/icons/github.js +0 -17
  158. package/dist/components/icons/gitlab.js +0 -31
  159. package/dist/components/icons/google.js +0 -40
  160. package/dist/components/icons/huggingface.js +0 -39
  161. package/dist/components/icons/index.js +0 -35
  162. package/dist/components/icons/kakao.js +0 -17
  163. package/dist/components/icons/kick.js +0 -21
  164. package/dist/components/icons/line.js +0 -17
  165. package/dist/components/icons/linear.js +0 -17
  166. package/dist/components/icons/linkedin.js +0 -34
  167. package/dist/components/icons/microsoft.js +0 -31
  168. package/dist/components/icons/naver.js +0 -17
  169. package/dist/components/icons/notion.js +0 -17
  170. package/dist/components/icons/paybin.js +0 -33
  171. package/dist/components/icons/paypal.js +0 -31
  172. package/dist/components/icons/polar.js +0 -25
  173. package/dist/components/icons/railway.js +0 -17
  174. package/dist/components/icons/reddit.js +0 -22
  175. package/dist/components/icons/roblox.js +0 -18
  176. package/dist/components/icons/salesforce.js +0 -17
  177. package/dist/components/icons/slack.js +0 -31
  178. package/dist/components/icons/spotify.js +0 -17
  179. package/dist/components/icons/tiktok.js +0 -24
  180. package/dist/components/icons/twitch.js +0 -44
  181. package/dist/components/icons/vercel.js +0 -17
  182. package/dist/components/icons/vk.js +0 -17
  183. package/dist/components/icons/wechat.js +0 -17
  184. package/dist/components/icons/x.js +0 -17
  185. package/dist/components/icons/zoom.js +0 -70
  186. package/dist/components/settings/account/theme-preview.js +0 -289
  187. package/dist/hooks/auth/use-authenticate.js +0 -23
  188. package/dist/hooks/auth/use-user.js +0 -11
  189. package/dist/hooks/use-auth-mutation.js +0 -11
  190. package/dist/hooks/use-auth-plugin.js +0 -10
  191. package/dist/hooks/use-auth-query.js +0 -15
  192. package/dist/lib/provider-icons.js +0 -76
  193. package/dist/lib/utils.js +0 -8
  194. package/dist/mutations/api-key/create-api-key-mutation.js +0 -29
  195. package/dist/mutations/api-key/delete-api-key-mutation.js +0 -29
  196. package/dist/mutations/auth/request-password-reset-mutation.js +0 -24
  197. package/dist/mutations/auth/reset-password-mutation.js +0 -24
  198. package/dist/mutations/auth/send-verification-email-mutation.js +0 -24
  199. package/dist/mutations/auth/sign-in-email-mutation.js +0 -29
  200. package/dist/mutations/auth/sign-in-social-mutation.js +0 -24
  201. package/dist/mutations/auth/sign-out-mutation.js +0 -28
  202. package/dist/mutations/auth/sign-up-email-mutation.js +0 -29
  203. package/dist/mutations/auth-mutation-options.js +0 -19
  204. package/dist/mutations/magic-link/sign-in-magic-link-mutation.js +0 -24
  205. package/dist/mutations/multi-session/revoke-multi-session-mutation.js +0 -29
  206. package/dist/mutations/multi-session/set-active-session-mutation.js +0 -31
  207. package/dist/mutations/passkey/add-passkey-mutation.js +0 -29
  208. package/dist/mutations/passkey/delete-passkey-mutation.js +0 -29
  209. package/dist/mutations/passkey/sign-in-passkey-mutation.js +0 -29
  210. package/dist/mutations/settings/change-email-mutation.js +0 -29
  211. package/dist/mutations/settings/change-password-mutation.js +0 -24
  212. package/dist/mutations/settings/delete-user-mutation.js +0 -24
  213. package/dist/mutations/settings/link-social-mutation.js +0 -24
  214. package/dist/mutations/settings/revoke-session-mutation.js +0 -29
  215. package/dist/mutations/settings/unlink-account-mutation.js +0 -29
  216. package/dist/mutations/settings/update-user-mutation.js +0 -35
  217. package/dist/mutations/username/is-username-available-mutation.js +0 -24
  218. package/dist/mutations/username/sign-in-username-mutation.js +0 -29
  219. package/dist/plugins/captcha-plugin.js +0 -29
  220. package/dist/queries/api-key/list-api-keys-query.js +0 -31
  221. package/dist/queries/auth/session-query.js +0 -30
  222. package/dist/queries/auth-query-options.js +0 -17
  223. package/dist/queries/multi-session/list-device-sessions-query.js +0 -31
  224. package/dist/queries/passkey/list-passkeys-query.js +0 -31
  225. package/dist/queries/settings/account-info-query.js +0 -31
  226. package/dist/queries/settings/list-accounts-query.js +0 -31
  227. package/dist/queries/settings/list-sessions-query.js +0 -31
  228. package/dist/server/queries/auth/session-query.js +0 -13
  229. package/dist/server/queries/multi-session/list-device-sessions-query.js +0 -12
  230. package/dist/server/queries/passkey/list-passkeys-query.js +0 -12
  231. package/dist/server/queries/settings/account-info-query.js +0 -12
  232. package/dist/server/queries/settings/list-accounts-query.js +0 -12
  233. package/dist/server/queries/settings/list-sessions-query.js +0 -12
@@ -0,0 +1,454 @@
1
+ import {
2
+ Body,
3
+ Button,
4
+ Container,
5
+ Head,
6
+ Heading,
7
+ Hr,
8
+ Html,
9
+ Img,
10
+ Link,
11
+ Preview,
12
+ pixelBasedPreset,
13
+ Section,
14
+ Tailwind,
15
+ Text
16
+ } from "@react-email/components"
17
+ import type { ReactNode } from "react"
18
+
19
+ import { cn } from "../../../lib/utils"
20
+ import {
21
+ type EmailClassNames,
22
+ type EmailColors,
23
+ EmailStyles
24
+ } from "./email-styles"
25
+
26
+ const organizationInvitationEmailLocalization = {
27
+ YOU_RE_INVITED_TO_ORGANIZATION: "You're invited to {organizationName}",
28
+ YOU_RE_INVITED: "You're invited",
29
+ LOGO: "Logo",
30
+ ORGANIZATION_LOGO: "Organization logo",
31
+ INVITED_TO_JOIN_ORGANIZATION:
32
+ "{inviterName} ({inviterEmail}) has invited you to join {organizationName} on {appName} as a {role}.",
33
+ ACCEPT_INVITATION: "Accept invitation",
34
+ VIEW_INVITATION: "View invitation",
35
+ OR_COPY_AND_PASTE_URL: "Or copy and paste this URL into your browser:",
36
+ THIS_INVITATION_EXPIRES_IN_HOURS:
37
+ "This invitation expires in {expirationHours} hours.",
38
+ EMAIL_SENT_BY: "Email sent by {appName}.",
39
+ IF_YOU_DIDNT_EXPECT_THIS_INVITATION:
40
+ "If you didn't expect this invitation, you can safely ignore this email.",
41
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
42
+ }
43
+
44
+ /**
45
+ * Localization strings for the OrganizationInvitationEmail component.
46
+ *
47
+ * Contains all text content used in the organization invitation email template.
48
+ */
49
+ export type OrganizationInvitationEmailLocalization =
50
+ typeof organizationInvitationEmailLocalization
51
+
52
+ /**
53
+ * Props for the OrganizationInvitationEmail component.
54
+ */
55
+ export interface OrganizationInvitationEmailProps {
56
+ /**
57
+ * URL where the invitee can review and accept the invitation.
58
+ *
59
+ * @remarks Pass `{baseUrl}/settings/organizations` — this is where pending
60
+ * organization invitations are listed in the settings UI.
61
+ */
62
+ url: string
63
+ /** Email address of the user being invited */
64
+ email?: string
65
+ /** Name of the person who sent the invitation */
66
+ inviterName?: string
67
+ /** Email address of the person who sent the invitation */
68
+ inviterEmail?: string
69
+ /** Name of the organization the user is being invited to */
70
+ organizationName?: string
71
+ /** Organization logo URL(s) - a single string or light/dark variants. */
72
+ organizationLogoURL?: string | { light: string; dark: string }
73
+ /** Role being offered to the invitee (e.g. "member", "admin", "owner") */
74
+ role?: string
75
+ /** Name of the application sending the email */
76
+ appName?: string
77
+ /** Number of hours until the invitation expires */
78
+ expirationHours?: number
79
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
80
+ logoURL?: string | { light: string; dark: string }
81
+ /** Custom CSS class names for styling specific parts of the email */
82
+ classNames?: EmailClassNames
83
+ /** Custom color scheme for light and dark modes */
84
+ colors?: EmailColors
85
+ /** Whether to show the "Powered by better-auth" footer */
86
+ poweredBy?: boolean
87
+ /** Whether to enable dark mode support */
88
+ darkMode?: boolean
89
+ /** Additional React nodes to inject into the email head */
90
+ head?: ReactNode
91
+ /**
92
+ * Localization overrides for customizing email text
93
+ * @remarks `OrganizationInvitationEmailLocalization`
94
+ */
95
+ localization?: Partial<OrganizationInvitationEmailLocalization>
96
+ }
97
+
98
+ /**
99
+ * Email template component that invites a user to join an organization.
100
+ *
101
+ * This email includes:
102
+ * - Inviter and organization details
103
+ * - Role being offered
104
+ * - Accept invitation button linking to `{baseUrl}/settings/organizations`
105
+ * - Fallback URL for manual copy/paste
106
+ * - Optional expiration time information
107
+ * - Security notice for unexpected invitations
108
+ * - Customizable branding and styling
109
+ * - Support for light/dark mode themes
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * <OrganizationInvitationEmail
114
+ * url="https://example.com/settings/organizations"
115
+ * email="invitee@example.com"
116
+ * inviterName="Jane Doe"
117
+ * inviterEmail="jane@example.com"
118
+ * organizationName="Acme Inc."
119
+ * organizationLogoURL="https://example.com/acme-logo.png"
120
+ * role="member"
121
+ * appName="My App"
122
+ * expirationHours={48}
123
+ * darkMode={true}
124
+ * />
125
+ * ```
126
+ */
127
+ export const OrganizationInvitationEmail = ({
128
+ url,
129
+ email,
130
+ inviterName,
131
+ inviterEmail,
132
+ organizationName,
133
+ organizationLogoURL,
134
+ role,
135
+ appName,
136
+ expirationHours = 48,
137
+ logoURL,
138
+ colors,
139
+ classNames,
140
+ darkMode = true,
141
+ poweredBy,
142
+ head,
143
+ ...props
144
+ }: OrganizationInvitationEmailProps) => {
145
+ const localization = {
146
+ ...OrganizationInvitationEmail.localization,
147
+ ...props.localization
148
+ }
149
+
150
+ const previewText = organizationName
151
+ ? localization.YOU_RE_INVITED_TO_ORGANIZATION.replace(
152
+ "{organizationName}",
153
+ organizationName
154
+ )
155
+ : localization.YOU_RE_INVITED
156
+
157
+ return (
158
+ <Html>
159
+ <Head>
160
+ <meta content="light dark" name="color-scheme" />
161
+ <meta content="light dark" name="supported-color-schemes" />
162
+
163
+ <EmailStyles colors={colors} darkMode={darkMode} />
164
+
165
+ {head}
166
+ </Head>
167
+
168
+ <Preview>{previewText}</Preview>
169
+
170
+ <Tailwind config={{ presets: [pixelBasedPreset] }}>
171
+ <Body className={cn("bg-background font-sans", classNames?.body)}>
172
+ <Container
173
+ className={cn(
174
+ "mx-auto my-auto max-w-xl px-2 py-10",
175
+ classNames?.container
176
+ )}
177
+ >
178
+ <Section
179
+ className={cn(
180
+ "bg-card text-card-foreground rounded-none border border-border p-8",
181
+ classNames?.card
182
+ )}
183
+ >
184
+ {logoURL &&
185
+ (typeof logoURL === "string" ? (
186
+ <Img
187
+ src={logoURL}
188
+ width={48}
189
+ height={48}
190
+ alt={appName || localization.LOGO}
191
+ className={cn("mx-auto mb-8", classNames?.logo)}
192
+ />
193
+ ) : (
194
+ <>
195
+ <Img
196
+ src={logoURL.light}
197
+ width={48}
198
+ height={48}
199
+ alt={appName || localization.LOGO}
200
+ className={cn(
201
+ "mx-auto mb-8 logo-light",
202
+ classNames?.logo
203
+ )}
204
+ />
205
+ <Img
206
+ src={logoURL.dark}
207
+ width={48}
208
+ height={48}
209
+ alt={appName || localization.LOGO}
210
+ className={cn(
211
+ "hidden mx-auto mb-8 logo-dark",
212
+ classNames?.logo
213
+ )}
214
+ />
215
+ </>
216
+ ))}
217
+
218
+ <Heading
219
+ className={cn(
220
+ "m-0 mb-5 text-2xl font-semibold",
221
+ classNames?.title
222
+ )}
223
+ >
224
+ {organizationName
225
+ ? localization.YOU_RE_INVITED_TO_ORGANIZATION.replace(
226
+ "{organizationName}",
227
+ organizationName
228
+ )
229
+ : localization.YOU_RE_INVITED}
230
+ </Heading>
231
+
232
+ {organizationLogoURL &&
233
+ (typeof organizationLogoURL === "string" ? (
234
+ <Img
235
+ src={organizationLogoURL}
236
+ width={56}
237
+ height={56}
238
+ alt={organizationName || localization.ORGANIZATION_LOGO}
239
+ className={cn("mb-5 rounded-md", classNames?.logo)}
240
+ />
241
+ ) : (
242
+ <>
243
+ <Img
244
+ src={organizationLogoURL.light}
245
+ width={56}
246
+ height={56}
247
+ alt={organizationName || localization.ORGANIZATION_LOGO}
248
+ className={cn(
249
+ "mb-5 rounded-md logo-light",
250
+ classNames?.logo
251
+ )}
252
+ />
253
+ <Img
254
+ src={organizationLogoURL.dark}
255
+ width={56}
256
+ height={56}
257
+ alt={organizationName || localization.ORGANIZATION_LOGO}
258
+ className={cn(
259
+ "hidden mb-5 rounded-md logo-dark",
260
+ classNames?.logo
261
+ )}
262
+ />
263
+ </>
264
+ ))}
265
+
266
+ <Text
267
+ className={cn("m-0 text-sm font-normal", classNames?.content)}
268
+ >
269
+ {(() => {
270
+ let text = localization.INVITED_TO_JOIN_ORGANIZATION.replace(
271
+ "{appName}",
272
+ appName || ""
273
+ )
274
+ .replace("{organizationName}", organizationName || "")
275
+ .replace("{role}", role || "")
276
+
277
+ // If we have no inviter info, drop the parenthetical and name placeholders cleanly.
278
+ if (!inviterName && !inviterEmail) {
279
+ text = text
280
+ .replace("{inviterName} ({inviterEmail})", "Someone")
281
+ .replace("{inviterName}", "Someone")
282
+ .replace("({inviterEmail})", "")
283
+ }
284
+
285
+ const [beforeInviterName, afterInviterName] =
286
+ text.split("{inviterName}")
287
+
288
+ const renderInviterEmail = (segment: string) => {
289
+ const [beforeInviterEmail, afterInviterEmail] =
290
+ segment.split("{inviterEmail}")
291
+
292
+ if (!inviterEmail) {
293
+ return segment
294
+ .replace("({inviterEmail})", "")
295
+ .replace("{inviterEmail}", "")
296
+ .replace(/\s{2,}/g, " ")
297
+ .replace(" .", ".")
298
+ }
299
+
300
+ return (
301
+ <>
302
+ {beforeInviterEmail}
303
+ <Link
304
+ href={`mailto:${inviterEmail}`}
305
+ className="text-primary font-medium"
306
+ >
307
+ {inviterEmail}
308
+ </Link>
309
+ {afterInviterEmail}
310
+ </>
311
+ )
312
+ }
313
+
314
+ if (!inviterName) {
315
+ return renderInviterEmail(
316
+ text
317
+ .replace("{inviterName}", "")
318
+ .replace(/\s{2,}/g, " ")
319
+ .replace(" .", ".")
320
+ )
321
+ }
322
+
323
+ return (
324
+ <>
325
+ {beforeInviterName}
326
+ <span className="font-medium">{inviterName}</span>
327
+ {renderInviterEmail(afterInviterName ?? "")}
328
+ </>
329
+ )
330
+ })()}
331
+ </Text>
332
+
333
+ <Section className="my-6">
334
+ <Button
335
+ href={url}
336
+ className={cn(
337
+ "inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline",
338
+ classNames?.button
339
+ )}
340
+ >
341
+ {localization.ACCEPT_INVITATION}
342
+ </Button>
343
+ </Section>
344
+
345
+ <Text
346
+ className={cn(
347
+ "m-0 mb-3 text-xs text-muted-foreground",
348
+ classNames?.description
349
+ )}
350
+ >
351
+ {localization.OR_COPY_AND_PASTE_URL}
352
+ </Text>
353
+
354
+ <Link
355
+ className={cn(
356
+ "break-all text-xs text-primary",
357
+ classNames?.link
358
+ )}
359
+ href={url}
360
+ >
361
+ {url}
362
+ </Link>
363
+
364
+ <Hr
365
+ className={cn(
366
+ "my-6 w-full border border-solid border-border",
367
+ classNames?.separator
368
+ )}
369
+ />
370
+
371
+ {expirationHours || appName ? (
372
+ <Text
373
+ className={cn(
374
+ "m-0 mb-3 text-xs text-muted-foreground",
375
+ classNames?.description
376
+ )}
377
+ >
378
+ {expirationHours
379
+ ? localization.THIS_INVITATION_EXPIRES_IN_HOURS.replace(
380
+ "{expirationHours}",
381
+ expirationHours.toString()
382
+ )
383
+ : null}
384
+ {appName && (
385
+ <>
386
+ {expirationHours ? " " : ""}
387
+ {localization.EMAIL_SENT_BY.replace("{appName}", appName)}
388
+ </>
389
+ )}
390
+ </Text>
391
+ ) : null}
392
+
393
+ <Text
394
+ className={cn(
395
+ "m-0 text-xs text-muted-foreground",
396
+ classNames?.description
397
+ )}
398
+ >
399
+ {localization.IF_YOU_DIDNT_EXPECT_THIS_INVITATION}
400
+ </Text>
401
+
402
+ {poweredBy && (
403
+ <Text
404
+ className={cn(
405
+ "m-0 mt-4 text-center text-[11px] text-muted-foreground",
406
+ classNames?.poweredBy
407
+ )}
408
+ >
409
+ {(() => {
410
+ const [beforeBetterAuth, afterBetterAuth] =
411
+ localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}")
412
+
413
+ return (
414
+ <>
415
+ {beforeBetterAuth}
416
+ <Link
417
+ href="https://better-auth.com"
418
+ className={cn(
419
+ "text-primary underline",
420
+ classNames?.link
421
+ )}
422
+ >
423
+ better-auth
424
+ </Link>
425
+ {afterBetterAuth}
426
+ </>
427
+ )
428
+ })()}
429
+ </Text>
430
+ )}
431
+ </Section>
432
+ </Container>
433
+ </Body>
434
+ </Tailwind>
435
+ </Html>
436
+ )
437
+ }
438
+
439
+ OrganizationInvitationEmail.localization =
440
+ organizationInvitationEmailLocalization
441
+
442
+ OrganizationInvitationEmail.PreviewProps = {
443
+ url: "https://better-auth-ui.com/settings/organizations",
444
+ email: "m@example.com",
445
+ inviterName: "Jane Doe",
446
+ inviterEmail: "jane@example.com",
447
+ organizationName: "Acme Inc.",
448
+ role: "member",
449
+ appName: "Better Auth",
450
+ expirationHours: 48,
451
+ darkMode: true
452
+ } as OrganizationInvitationEmailProps
453
+
454
+ export default OrganizationInvitationEmail
@@ -0,0 +1,67 @@
1
+ import { authMutationKeys } from "@better-auth-ui/core"
2
+ import {
3
+ matchMutation,
4
+ matchQuery,
5
+ type QueryKey,
6
+ useQueryClient
7
+ } from "@tanstack/react-query"
8
+ import { useEffect } from "react"
9
+
10
+ type AuthMutationMeta = {
11
+ invalidates?: Array<QueryKey>
12
+ awaits?: Array<QueryKey>
13
+ }
14
+
15
+ export function MutationInvalidator() {
16
+ const queryClient = useQueryClient()
17
+
18
+ useEffect(() => {
19
+ const mutationCache = queryClient.getMutationCache()
20
+ const previousOnSuccess = mutationCache.config.onSuccess
21
+
22
+ mutationCache.config.onSuccess = async (
23
+ data,
24
+ variables,
25
+ onMutateResult,
26
+ mutation,
27
+ context
28
+ ) => {
29
+ await previousOnSuccess?.(
30
+ data,
31
+ variables,
32
+ onMutateResult,
33
+ mutation,
34
+ context
35
+ )
36
+
37
+ if (!matchMutation({ mutationKey: authMutationKeys.all }, mutation)) {
38
+ return
39
+ }
40
+
41
+ const { invalidates, awaits } = (mutation.meta ?? {}) as AuthMutationMeta
42
+
43
+ if (invalidates?.length) {
44
+ queryClient.invalidateQueries({
45
+ predicate: (query) =>
46
+ invalidates.some((queryKey) => matchQuery({ queryKey }, query))
47
+ })
48
+ }
49
+
50
+ if (awaits?.length) {
51
+ return queryClient.invalidateQueries(
52
+ {
53
+ predicate: (query) =>
54
+ awaits.some((queryKey) => matchQuery({ queryKey }, query))
55
+ },
56
+ { cancelRefetch: false }
57
+ )
58
+ }
59
+ }
60
+
61
+ return () => {
62
+ mutationCache.config.onSuccess = previousOnSuccess
63
+ }
64
+ }, [queryClient])
65
+
66
+ return null
67
+ }
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export * from "./hooks/use-auth-query"
12
12
  export type * from "./lib/auth-client"
13
13
  export * from "./lib/auth-plugin"
14
14
  export * from "./lib/provider-icons"
15
+ export * from "./lib/settings-tab"
15
16
  export * from "./mutations/api-key/create-api-key-mutation"
16
17
  export * from "./mutations/api-key/delete-api-key-mutation"
17
18
  export * from "./mutations/auth/request-password-reset-mutation"
@@ -25,6 +26,7 @@ export * from "./mutations/auth-mutation-options"
25
26
  export * from "./mutations/magic-link/sign-in-magic-link-mutation"
26
27
  export * from "./mutations/multi-session/revoke-multi-session-mutation"
27
28
  export * from "./mutations/multi-session/set-active-session-mutation"
29
+ export * from "./mutations/organization"
28
30
  export * from "./mutations/passkey/add-passkey-mutation"
29
31
  export * from "./mutations/passkey/delete-passkey-mutation"
30
32
  export * from "./mutations/passkey/sign-in-passkey-mutation"
@@ -41,6 +43,7 @@ export * from "./queries/api-key/list-api-keys-query"
41
43
  export * from "./queries/auth/session-query"
42
44
  export * from "./queries/auth-query-options"
43
45
  export * from "./queries/multi-session/list-device-sessions-query"
46
+ export * from "./queries/organization"
44
47
  export * from "./queries/passkey/list-passkeys-query"
45
48
  export * from "./queries/settings/account-info-query"
46
49
  export * from "./queries/settings/list-accounts-query"
@@ -3,6 +3,7 @@ import type { passkeyClient } from "@better-auth/passkey/client"
3
3
  import type {
4
4
  magicLinkClient,
5
5
  multiSessionClient,
6
+ organizationClient,
6
7
  usernameClient
7
8
  } from "better-auth/client/plugins"
8
9
  import type { createAuthClient } from "better-auth/react"
@@ -35,6 +36,12 @@ export type UsernameAuthClient = ReturnType<
35
36
  typeof createAuthClient<{ plugins: [ReturnType<typeof usernameClient>] }>
36
37
  >
37
38
 
39
+ export type OrganizationAuthClient = ReturnType<
40
+ typeof createAuthClient<{
41
+ plugins: [ReturnType<typeof organizationClient<object>>]
42
+ }>
43
+ >
44
+
38
45
  /**
39
46
  * Unwraps a Better Auth client method's `data` payload.
40
47
  *
@@ -1,5 +1,6 @@
1
1
  import type { AuthPluginBase, AuthView } from "@better-auth-ui/core"
2
2
  import type { ComponentType, ReactNode } from "react"
3
+ import type { SettingsTab } from "./settings-tab"
3
4
 
4
5
  export type { AuthPluginViewPaths } from "@better-auth-ui/core"
5
6
 
@@ -23,9 +24,17 @@ export type AccountCardProps = {
23
24
  children?: ReactNode
24
25
  }
25
26
 
27
+ /** Props for plugin-contributed cards under `/organization/...` settings. */
28
+ export type OrganizationCardProps = {
29
+ className?: string
30
+ children?: ReactNode
31
+ }
32
+
26
33
  /** Props for plugin-contributed items in the `UserButton` dropdown. */
27
34
  export type UserMenuItemProps = {
28
35
  className?: string
36
+ /** When true, the subtitle line (email when name/username is shown) is hidden. */
37
+ hideSubtitle?: boolean
29
38
  }
30
39
 
31
40
  /** Framework-agnostic slot component shapes. UI packages narrow these via `TComponents`. */
@@ -38,6 +47,8 @@ export type AuthPluginComponents = {
38
47
  securityCards?: ComponentType<SecurityCardProps>[]
39
48
  /** Rendered as cards inside account settings. */
40
49
  accountCards?: ComponentType<AccountCardProps>[]
50
+ /** Rendered as cards inside the active organization's settings. */
51
+ organizationCards?: ComponentType<OrganizationCardProps>[]
41
52
  /** Rendered as items inside the `UserButton` dropdown. */
42
53
  userMenuItems?: ComponentType<UserMenuItemProps>[]
43
54
  }
@@ -74,4 +85,10 @@ export type AuthPlugin<
74
85
  TComponents & {
75
86
  views?: AuthPluginViews<TAuthViewProps, TSettingsViewProps>
76
87
  fallbackViews?: AuthPluginFallbackViews<TAuthViewProps>
88
+ /**
89
+ * Tabs the plugin contributes to the settings page. Each entry is a
90
+ * {@link SettingsTab} (`view`, `label`, `component`). Read at runtime via
91
+ * `useAuthPlugin(plugin).settingsTabs`.
92
+ */
93
+ settingsTabs?: SettingsTab[]
77
94
  }
@@ -1,6 +1,12 @@
1
+ import type { apiKey } from "@better-auth/api-key"
1
2
  import type { passkey } from "@better-auth/passkey"
2
3
  import type { Auth } from "better-auth"
3
- import type { magicLink, multiSession, username } from "better-auth/plugins"
4
+ import type {
5
+ magicLink,
6
+ multiSession,
7
+ organization,
8
+ username
9
+ } from "better-auth/plugins"
4
10
 
5
11
  export type AuthServer = Pick<Auth, "api">
6
12
 
@@ -24,7 +30,17 @@ export type PasskeyAuthServer = Pick<
24
30
  "api"
25
31
  >
26
32
 
33
+ export type ApiKeyAuthServer = Pick<
34
+ Auth<{ plugins: [ReturnType<typeof apiKey>] }>,
35
+ "api"
36
+ >
37
+
27
38
  export type UsernameAuthServer = Pick<
28
39
  Auth<{ plugins: [ReturnType<typeof username>] }>,
29
40
  "api"
30
41
  >
42
+
43
+ export type OrganizationAuthServer = Pick<
44
+ Auth<{ plugins: [ReturnType<typeof organization>] }>,
45
+ "api"
46
+ >
@@ -0,0 +1,21 @@
1
+ import type { SettingsView } from "@better-auth-ui/core"
2
+ import type { ComponentType, ReactNode } from "react"
3
+
4
+ /**
5
+ * Tab contributions for the settings page.
6
+ *
7
+ * Plugins declare `settingsTabs` on their object to add extra tabs.
8
+ * Read at runtime via `useAuthPlugin(plugin).settingsTabs`.
9
+ */
10
+ export interface SettingsTab {
11
+ /**
12
+ * Settings view id — must match a key on `plugin.viewPaths.settings` and
13
+ * {@link SettingsView} (including keys added via `declare module` augmentation).
14
+ */
15
+ view: SettingsView
16
+ /** Display label */
17
+ label: ReactNode
18
+ /** Component rendered in the tab panel */
19
+ // biome-ignore lint/suspicious/noExplicitAny: any
20
+ component: ComponentType<any>
21
+ }