App Invite
Invite users to your application and allow them to sign up.
The App Invite plugin enables you to invite users to your application through email invitations. It supports two types of invitations:
- Personal Invitations: Targeted to specific email addresses, ensuring only the intended recipient can use the invitation
- Public Invitations: Can be used by multiple users, making it ideal for open sign-up scenarios
This plugin is particularly useful for invite-only applications.
Installation
Install the plugin
npm install @better-auth-extended/app-invite
Add the plugin to your auth config
To use the App Invite plugin, add it to your auth config.
import { betterAuth } from "better-auth";
import { appInvite } from "@better-auth-extended/app-invite";
export const auth = betterAuth({
// ... other config options
plugins: [
appInvite({
// required for personal invites
sendInvitationEmail: (data) => {
// ... send invitation to the user
},
}),
],
});
Add the client plugin
Include the App Invite client plugin in your authentication client instance.
import { createAuthClient } from "better-auth/client";
import { appInviteClient } from "@better-auth-extended/app-invite/client";
const authClient = createAuthClient({
plugins: [appInviteClient()],
});
Run migrations
This plugin adds an additional table to the database. Click here to see the schema
npx @better-auth/cli migrate
or generate
npx @better-auth/cli generate
Usage
To add members to the application, we first need to send an invitation to the user. The user will receive an email with the invitation link. Once the user accepts the invitation, they will be signed up to the application.
Setup Invitation Email
For personal invites to work we first need to provide sendInvitationEmail
to the better-auth
instance.
This function is responsible for sending the invitation email to the user.
You'll need to construct and send the invitation link to the user. The link should include the invitation ID,
which will be used with the acceptInvitation
function when the user clicks on it.
This is only required for personal invites. Sharing public invitations is up to the inviter.
import { betterAuth } from "better-auth";
import { appInvite } from "@better-auth-extended/app-invite";
import { sendAppInvitation } from "./email";
export const auth = betterAuth({
plugins: [
appInvite({
async sendInvitationEmail(data) {
const inviteLink = `https://example.com/accept-invitation/${data.id}`;
sendAppInvitation({
name: data.name,
email: data.email,
invitedByUsername: data.inviter.name,
invitedByEmail: data.inviter.email,
inviteLink,
});
},
}),
],
});
Send Invitation
To invite users to the app, you can use the invite
function provided by the server and client.
const { data, error } = await authClient.inviteUser({ email, resend, domainWhitelist,});
Prop | Description | Type |
---|---|---|
email? | The email address of the user to invite. Leave empty to create a public invitation. | string |
resend? | A boolean value that determines whether to resend the invitation email, if the user is already invited. Defaults to false | boolean |
domainWhitelist? | An optional comma-separated list of domains that allows public invitations to be accepted only from approved domains (e.g., example.com,*.example.org ). | string |
Accept Invitation
When a user receives an invitation email, they can click on the invitation link to accept the invitation. The link should include the invitation ID, which will be used to accept the invitation.
const { data, error } = await authClient.acceptInvitation({ invitationId, // required name, email, password, // required});
Prop | Description | Type |
---|---|---|
invitationId | The ID of the invitation to accept | string |
name? | The name of the user that accepts the invitation. (overriden if predefined in the invitation) | string |
email? | The email address of the user that accepts the invitation. Required for public invites. | string |
password | The password used to sign in after accepting the invitation. | string |
Update Invitation Status
To update the status of invitations you can use the acceptInvitation
, rejectInvitation
, cancelInvitation
function provided by the client. The functions take the invitation id as an argument.
const { data, error } = await authClient.cancelInvitation({ invitationId, // required});
Prop | Description | Type |
---|---|---|
invitationId | The ID of the invitation to cancel. | string |
This is only available for personal invites
const { data, error } = await authClient.rejectInvitation({ invitationId, // required});
Prop | Description | Type |
---|---|---|
invitationId | The ID of the invitation to reject. | string |
Get Invitation
To get an invitation you can use the getAppInvitation
function provided by the client. You need to provide the
invitation id as a query parameter.
const { data, error } = await authClient.getAppInvitation({ id, // required});
Prop | Description | Type |
---|---|---|
id | The ID of the invitation to retrieve. | string |
List Invitations
Allows a user to list all invitations issued by themselves. By default 100 invitations are returned.
const { data, error } = await authClient.listInvitations({ searchField, searchValue, searchOperator, limit, offset, sortBy, sortDirection, filterField, filterValue, filterOperator,});
Prop | Description | Type |
---|---|---|
searchField? | The field to search on, which can be email , name , or domainWhitelist . | string |
searchValue? | The value to search for. | string |
searchOperator? | The operator to use for the search. Can be contains , starts_with or ends_with | string |
limit? | The number of invitations to return. | number |
offset? | The number of invitations to skip. | number |
sortBy? | The field to sort the invitations by. | string |
sortDirection? | The direction to sort the invitations by. Defaults to asc . | string |
filterField? | The field to filter the invitations by. | string |
filterValue? | The value to filter the invitations by. | string |
filterOperator? | The operator to use for the filter. It can be eq , ne , lt , lte , gt , or gte . | string |
Schema
The plugin requires an additional table in the database.
Table Name: appInvitation
Field Name | Type | Key | Description |
---|---|---|---|
id | string | Unique identifier for each invitation | |
name | string | The name of the user | |
string | The email address of the user | ||
inviterId | string | The ID of the inviter | |
status | string | - | The status of the invitation |
domainWhitelist | string | A comma separated whitelist of domains for a public invitation | |
expiresAt | Date | Timestamp of when the invitation expires | |
createdAt | Date | - | Timestamp of when the invitation was created |
Options
canCreateInvitation: ((ctx: GenericEndpointContext) => Promise<boolean> | boolean | Promise<Permission> | Permission)
| boolean | Permission
- A function that determines whether a user can create an invitation. By default, it's true
. You can set it to false
to restrict users from creating invitations.
canCancelInvitation ((ctx: GenericEndpointContext, invite: AppInvitation) => Promise<boolean> | boolean | Promise<Permission> | Permission)
| boolean | Permission
- A function that determines whether a user can cancel invitations. By default, the user can only cancel invites they created. You can set it to false
to restrict users from canceling invitations.
Returning Permission
requires the admin plugin to be configured. If it's not set up, the request will fail.
Example:
canCancelInvitation: {
statement: "appInvite",
permissions: ["create"],
};
sendInvitationEmail: async (data) => Promise<void>
- A function that sends an invitation email to the user. This is only required for personal invitations.
invitationExpiresIn: number
- How long the invitation link is valid for in seconds. By default an invitation expires after 48 hours (2 days). Set it to null
to prevent invitations from expiring.
autoSignIn: boolean
- A boolean value that determines whether to prevent automatic sign-up when accepting an invitation. Defaults to false
.
cleanupExpiredInvitations: boolean
- Clean up expired invitations when a value is fetched. Default true
.
cleanupPersonalInvitesOnDecision: boolean
- Cleanup personal invitations when a decision is made. Default false
.
verifyEmailOnAccept: boolean
- Whether to verify email addresses when accepting invitations. Default true
.
resendExistingInvite: boolean
- Whether to resend existing invitations, instead of creating a new one. Default false
.
hooks.create.before: (ctx: GenericEndpointContext) => Promise<void> | void
- A function that runs before an invitation is created.
hooks.create.after: (ctx: GenericEndpointContext, invitation: AppInvitation) => Promise<void> | void
- A function that runs after an invitation was created.
hooks.accept.before: (ctx: GenericEndpointContext, userToCreate: Partial<User> & { email: string }) => Promise<{ user?: User; } | void> | { user?: User; } | void;
- A function that runs before an invitation is accepted.
hooks.accept.after: (ctx: GenericEndpointContext, data: { invitation: AppInvitation; user: User; }) => Promise<void> | void
- A function that runs after an invitation was accepted.
hooks.reject.before: (ctx: GenericEndpointContext, invitation: AppInvitation) => Promise<void> | void
- A function that runs before an invitation is rejected.
hooks.reject.after: (ctx: GenericEndpointContext, invitation: AppInvitation) => Promise<void> | void
- A function that runs after an invitation was rejected.
hooks.cancel.before: (ctx: GenericEndpointContext, invitation: AppInvitation) => Promise<void> | void
- A function that runs before an invitation is canceled.
hooks.cancel.after: (ctx: GenericEndpointContext, invitation: AppInvitation) => Promise<void> | void
- A function that runs after an invitation was canceled.
schema: The schema for the app-invite plugin. Allows you to infer additional fields for the user
and appInvitation
tables. This option is available in the client plugin as well.
allowUserToCreateInvitation: boolean
| ((user: User, type: "personal" | "public") => Promise<boolean> | boolean)
- A function that determines whether a user can invite others. By defaults it's true
. You can set it to false
to restrict users from creating invitations. (deprecated. use canCreateInvitation
instead.)
allowUserToCancelInvitation: (data: { user: User, invitation: AppInvitation }) => Promise<boolean> | boolean
- A function that determines whether a user can cancel invitations. By default the user can only cancel invites created by them. You can set it to false
to restrict users from canceling invitations. (deprecated. use canCancelInvitation
instead.)
How is this guide?