@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.
- package/dist/components/auth/auth-context.d.ts +3 -0
- package/dist/components/auth/email/email-localization.d.ts +11 -3
- package/dist/components/auth/email/index.d.ts +1 -0
- package/dist/components/auth/email/organization-invitation.d.ts +121 -0
- package/dist/components/mutation-invalidator.d.ts +1 -0
- package/dist/email.js +1348 -11
- package/dist/hooks/auth/use-authenticate.d.ts +1 -1
- package/dist/hooks/auth/use-user.d.ts +12 -12
- package/dist/hooks/use-auth-query.d.ts +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2469 -79
- package/dist/lib/auth-client.d.ts +4 -1
- package/dist/lib/auth-plugin.d.ts +16 -0
- package/dist/lib/auth-server.d.ts +8 -1
- package/dist/lib/settings-tab.d.ts +19 -0
- package/dist/mutations/api-key/create-api-key-mutation.d.ts +6 -4
- package/dist/mutations/api-key/delete-api-key-mutation.d.ts +7 -3
- package/dist/mutations/auth/sign-in-email-mutation.d.ts +5 -5
- package/dist/mutations/auth/sign-out-mutation.d.ts +6 -4
- package/dist/mutations/auth/sign-up-email-mutation.d.ts +5 -5
- package/dist/mutations/multi-session/revoke-multi-session-mutation.d.ts +5 -5
- package/dist/mutations/multi-session/set-active-session-mutation.d.ts +5 -6
- package/dist/mutations/organization/accept-invitation-mutation.d.ts +43 -0
- package/dist/mutations/organization/cancel-invitation-mutation.d.ts +25 -0
- package/dist/mutations/organization/check-slug-mutation.d.ts +21 -0
- package/dist/mutations/organization/create-organization-mutation.d.ts +39 -0
- package/dist/mutations/organization/delete-organization-mutation.d.ts +21 -0
- package/dist/mutations/organization/index.d.ts +12 -0
- package/dist/mutations/organization/invite-member-mutation.d.ts +43 -0
- package/dist/mutations/organization/leave-organization-mutation.d.ts +65 -0
- package/dist/mutations/organization/reject-invitation-mutation.d.ts +31 -0
- package/dist/mutations/organization/remove-member-mutation.d.ts +35 -0
- package/dist/mutations/organization/set-active-organization-mutation.d.ts +71 -0
- package/dist/mutations/organization/update-member-role-mutation.d.ts +31 -0
- package/dist/mutations/organization/update-organization-mutation.d.ts +21 -0
- package/dist/mutations/passkey/add-passkey-mutation.d.ts +5 -5
- package/dist/mutations/passkey/delete-passkey-mutation.d.ts +5 -5
- package/dist/mutations/passkey/sign-in-passkey-mutation.d.ts +5 -5
- package/dist/mutations/settings/change-email-mutation.d.ts +6 -5
- package/dist/mutations/settings/revoke-session-mutation.d.ts +5 -5
- package/dist/mutations/settings/unlink-account-mutation.d.ts +5 -5
- package/dist/mutations/settings/update-user-mutation.d.ts +5 -5
- package/dist/mutations/username/sign-in-username-mutation.d.ts +5 -5
- package/dist/plugins/captcha-plugin.d.ts +2 -2
- package/dist/plugins.js +27 -2
- package/dist/queries/api-key/list-api-keys-query.d.ts +17 -8
- package/dist/queries/auth/session-query.d.ts +1 -1
- package/dist/queries/multi-session/list-device-sessions-query.d.ts +5 -5
- package/dist/queries/organization/active-organization-query.d.ts +39 -0
- package/dist/queries/organization/full-organization-query.d.ts +37 -0
- package/dist/queries/organization/has-permission-query.d.ts +21 -0
- package/dist/queries/organization/index.d.ts +7 -0
- package/dist/queries/organization/list-invitations-query.d.ts +29 -0
- package/dist/queries/organization/list-members-query.d.ts +61 -0
- package/dist/queries/organization/list-organizations-query.d.ts +22 -0
- package/dist/queries/organization/list-user-invitations-query.d.ts +29 -0
- package/dist/queries/passkey/list-passkeys-query.d.ts +5 -5
- package/dist/queries/settings/account-info-query.d.ts +1 -1
- package/dist/queries/settings/list-accounts-query.d.ts +1 -1
- package/dist/queries/settings/list-sessions-query.d.ts +1 -1
- package/dist/server/queries/api-key/list-api-keys-query.d.ts +83 -0
- package/dist/server/queries/multi-session/list-device-sessions-query.d.ts +4 -4
- package/dist/server/queries/organization/active-organization-query.d.ts +61 -0
- package/dist/server/queries/organization/full-organization-query.d.ts +42 -0
- package/dist/server/queries/organization/has-permission-query.d.ts +66 -0
- package/dist/server/queries/organization/list-invitations-query.d.ts +34 -0
- package/dist/server/queries/organization/list-members-query.d.ts +66 -0
- package/dist/server/queries/organization/list-organizations-query.d.ts +27 -0
- package/dist/server/queries/organization/list-user-invitations-query.d.ts +34 -0
- package/dist/server/queries/passkey/list-passkeys-query.d.ts +4 -4
- package/dist/server.d.ts +8 -0
- package/dist/server.js +131 -7
- package/dist/use-auth-plugin-ClcP84Uz.js +80 -0
- package/package.json +19 -16
- package/src/components/auth/auth-context.tsx +7 -0
- package/src/components/auth/auth-provider.tsx +19 -31
- package/src/components/auth/email/email-localization.tsx +5 -3
- package/src/components/auth/email/index.ts +1 -0
- package/src/components/auth/email/organization-invitation.tsx +454 -0
- package/src/components/mutation-invalidator.tsx +67 -0
- package/src/index.ts +3 -0
- package/src/lib/auth-client.ts +7 -0
- package/src/lib/auth-plugin.ts +17 -0
- package/src/lib/auth-server.ts +17 -1
- package/src/lib/settings-tab.ts +21 -0
- package/src/mutations/api-key/create-api-key-mutation.ts +34 -19
- package/src/mutations/api-key/delete-api-key-mutation.ts +36 -19
- package/src/mutations/auth/sign-in-email-mutation.ts +18 -22
- package/src/mutations/auth/sign-out-mutation.ts +19 -13
- package/src/mutations/auth/sign-up-email-mutation.ts +18 -22
- package/src/mutations/multi-session/revoke-multi-session-mutation.ts +31 -20
- package/src/mutations/multi-session/set-active-session-mutation.ts +25 -44
- package/src/mutations/organization/accept-invitation-mutation.ts +71 -0
- package/src/mutations/organization/cancel-invitation-mutation.ts +67 -0
- package/src/mutations/organization/check-slug-mutation.ts +55 -0
- package/src/mutations/organization/create-organization-mutation.ts +71 -0
- package/src/mutations/organization/delete-organization-mutation.ts +71 -0
- package/src/mutations/organization/index.ts +12 -0
- package/src/mutations/organization/invite-member-mutation.ts +81 -0
- package/src/mutations/organization/leave-organization-mutation.ts +74 -0
- package/src/mutations/organization/reject-invitation-mutation.ts +64 -0
- package/src/mutations/organization/remove-member-mutation.ts +67 -0
- package/src/mutations/organization/set-active-organization-mutation.ts +142 -0
- package/src/mutations/organization/update-member-role-mutation.ts +81 -0
- package/src/mutations/organization/update-organization-mutation.ts +84 -0
- package/src/mutations/passkey/add-passkey-mutation.ts +28 -17
- package/src/mutations/passkey/delete-passkey-mutation.ts +27 -17
- package/src/mutations/passkey/sign-in-passkey-mutation.ts +18 -21
- package/src/mutations/settings/change-email-mutation.ts +22 -18
- package/src/mutations/settings/delete-user-mutation.ts +2 -2
- package/src/mutations/settings/revoke-session-mutation.ts +24 -19
- package/src/mutations/settings/unlink-account-mutation.ts +24 -17
- package/src/mutations/settings/update-user-mutation.ts +18 -30
- package/src/mutations/username/is-username-available-mutation.ts +2 -2
- package/src/mutations/username/sign-in-username-mutation.ts +20 -23
- package/src/queries/api-key/list-api-keys-query.ts +20 -8
- package/src/queries/multi-session/list-device-sessions-query.ts +2 -2
- package/src/queries/organization/active-organization-query.ts +140 -0
- package/src/queries/organization/full-organization-query.ts +113 -0
- package/src/queries/organization/has-permission-query.ts +124 -0
- package/src/queries/organization/index.ts +7 -0
- package/src/queries/organization/list-invitations-query.ts +133 -0
- package/src/queries/organization/list-members-query.ts +133 -0
- package/src/queries/organization/list-organizations-query.ts +122 -0
- package/src/queries/organization/list-user-invitations-query.ts +122 -0
- package/src/queries/passkey/list-passkeys-query.ts +2 -2
- package/src/server/queries/api-key/list-api-keys-query.ts +94 -0
- package/src/server/queries/multi-session/list-device-sessions-query.ts +2 -2
- package/src/server/queries/organization/active-organization-query.ts +92 -0
- package/src/server/queries/organization/full-organization-query.ts +64 -0
- package/src/server/queries/organization/has-permission-query.ts +66 -0
- package/src/server/queries/organization/list-invitations-query.ts +81 -0
- package/src/server/queries/organization/list-members-query.ts +75 -0
- package/src/server/queries/organization/list-organizations-query.ts +67 -0
- package/src/server/queries/organization/list-user-invitations-query.ts +68 -0
- package/src/server/queries/passkey/list-passkeys-query.ts +2 -2
- package/src/server.ts +8 -0
- package/dist/components/auth/auth-provider.js +0 -44
- package/dist/components/auth/email/email-changed.js +0 -156
- package/dist/components/auth/email/email-localization.js +0 -19
- package/dist/components/auth/email/email-styles.js +0 -104
- package/dist/components/auth/email/email-verification.js +0 -140
- package/dist/components/auth/email/index.js +0 -9
- package/dist/components/auth/email/magic-link.js +0 -141
- package/dist/components/auth/email/new-device.js +0 -223
- package/dist/components/auth/email/otp-email.js +0 -129
- package/dist/components/auth/email/password-changed.js +0 -159
- package/dist/components/auth/email/reset-password.js +0 -140
- package/dist/components/auth/fetch-options-provider.js +0 -30
- package/dist/components/icons/apple.js +0 -17
- package/dist/components/icons/atlassian.js +0 -39
- package/dist/components/icons/cognito.js +0 -20
- package/dist/components/icons/discord.js +0 -17
- package/dist/components/icons/dropbox.js +0 -17
- package/dist/components/icons/facebook.js +0 -20
- package/dist/components/icons/figma.js +0 -35
- package/dist/components/icons/github.js +0 -17
- package/dist/components/icons/gitlab.js +0 -31
- package/dist/components/icons/google.js +0 -40
- package/dist/components/icons/huggingface.js +0 -39
- package/dist/components/icons/index.js +0 -35
- package/dist/components/icons/kakao.js +0 -17
- package/dist/components/icons/kick.js +0 -21
- package/dist/components/icons/line.js +0 -17
- package/dist/components/icons/linear.js +0 -17
- package/dist/components/icons/linkedin.js +0 -34
- package/dist/components/icons/microsoft.js +0 -31
- package/dist/components/icons/naver.js +0 -17
- package/dist/components/icons/notion.js +0 -17
- package/dist/components/icons/paybin.js +0 -33
- package/dist/components/icons/paypal.js +0 -31
- package/dist/components/icons/polar.js +0 -25
- package/dist/components/icons/railway.js +0 -17
- package/dist/components/icons/reddit.js +0 -22
- package/dist/components/icons/roblox.js +0 -18
- package/dist/components/icons/salesforce.js +0 -17
- package/dist/components/icons/slack.js +0 -31
- package/dist/components/icons/spotify.js +0 -17
- package/dist/components/icons/tiktok.js +0 -24
- package/dist/components/icons/twitch.js +0 -44
- package/dist/components/icons/vercel.js +0 -17
- package/dist/components/icons/vk.js +0 -17
- package/dist/components/icons/wechat.js +0 -17
- package/dist/components/icons/x.js +0 -17
- package/dist/components/icons/zoom.js +0 -70
- package/dist/components/settings/account/theme-preview.js +0 -289
- package/dist/hooks/auth/use-authenticate.js +0 -23
- package/dist/hooks/auth/use-user.js +0 -11
- package/dist/hooks/use-auth-mutation.js +0 -11
- package/dist/hooks/use-auth-plugin.js +0 -10
- package/dist/hooks/use-auth-query.js +0 -15
- package/dist/lib/provider-icons.js +0 -76
- package/dist/lib/utils.js +0 -8
- package/dist/mutations/api-key/create-api-key-mutation.js +0 -29
- package/dist/mutations/api-key/delete-api-key-mutation.js +0 -29
- package/dist/mutations/auth/request-password-reset-mutation.js +0 -24
- package/dist/mutations/auth/reset-password-mutation.js +0 -24
- package/dist/mutations/auth/send-verification-email-mutation.js +0 -24
- package/dist/mutations/auth/sign-in-email-mutation.js +0 -29
- package/dist/mutations/auth/sign-in-social-mutation.js +0 -24
- package/dist/mutations/auth/sign-out-mutation.js +0 -28
- package/dist/mutations/auth/sign-up-email-mutation.js +0 -29
- package/dist/mutations/auth-mutation-options.js +0 -19
- package/dist/mutations/magic-link/sign-in-magic-link-mutation.js +0 -24
- package/dist/mutations/multi-session/revoke-multi-session-mutation.js +0 -29
- package/dist/mutations/multi-session/set-active-session-mutation.js +0 -31
- package/dist/mutations/passkey/add-passkey-mutation.js +0 -29
- package/dist/mutations/passkey/delete-passkey-mutation.js +0 -29
- package/dist/mutations/passkey/sign-in-passkey-mutation.js +0 -29
- package/dist/mutations/settings/change-email-mutation.js +0 -29
- package/dist/mutations/settings/change-password-mutation.js +0 -24
- package/dist/mutations/settings/delete-user-mutation.js +0 -24
- package/dist/mutations/settings/link-social-mutation.js +0 -24
- package/dist/mutations/settings/revoke-session-mutation.js +0 -29
- package/dist/mutations/settings/unlink-account-mutation.js +0 -29
- package/dist/mutations/settings/update-user-mutation.js +0 -35
- package/dist/mutations/username/is-username-available-mutation.js +0 -24
- package/dist/mutations/username/sign-in-username-mutation.js +0 -29
- package/dist/plugins/captcha-plugin.js +0 -29
- package/dist/queries/api-key/list-api-keys-query.js +0 -31
- package/dist/queries/auth/session-query.js +0 -30
- package/dist/queries/auth-query-options.js +0 -17
- package/dist/queries/multi-session/list-device-sessions-query.js +0 -31
- package/dist/queries/passkey/list-passkeys-query.js +0 -31
- package/dist/queries/settings/account-info-query.js +0 -31
- package/dist/queries/settings/list-accounts-query.js +0 -31
- package/dist/queries/settings/list-sessions-query.js +0 -31
- package/dist/server/queries/auth/session-query.js +0 -13
- package/dist/server/queries/multi-session/list-device-sessions-query.js +0 -12
- package/dist/server/queries/passkey/list-passkeys-query.js +0 -12
- package/dist/server/queries/settings/account-info-query.js +0 -12
- package/dist/server/queries/settings/list-accounts-query.js +0 -12
- 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"
|
package/src/lib/auth-client.ts
CHANGED
|
@@ -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
|
*
|
package/src/lib/auth-plugin.ts
CHANGED
|
@@ -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
|
}
|
package/src/lib/auth-server.ts
CHANGED
|
@@ -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 {
|
|
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
|
+
}
|