Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions packages/auth/src/lib/accept-invitation-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,18 @@ export const acceptInvitationEndpoint = {
});

if (!existingMember) {
await db.insert(members).values({
organizationId: invitation.organization.id,
userId: user.id,
role: invitation.role ?? "member",
// Dynamic import: this plugin needs to call the organization plugin's
// addMember API to trigger billing hooks (beforeAddMember/afterAddMember).
// server.ts imports this file as a plugin, so a static import would be circular.
// The import resolves at request time when all modules are fully initialized.
const { auth } = await import("../server");
await auth.api.addMember({
body: {
organizationId: invitation.organization.id,
userId: user.id,
role:
(invitation.role as "member" | "owner" | "admin") ?? "member",
},
});
Comment on lines +136 to 148
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

No error handling around auth.api.addMember — unhandled throw leaves partial state.

If addMember fails, the session and cookie are already set (lines 92-119) and the user lands in a broken state: logged in, session points to the org, but they aren't a member. Consider wrapping the call and returning a meaningful error, or at minimum logging context before re-throwing.

🤖 Prompt for AI Agents
In `@packages/auth/src/lib/accept-invitation-endpoint.ts` around lines 136 - 148,
The call to auth.api.addMember is unprotected, so if it throws the user remains
logged-in but not actually added to the organization; wrap the await
import("../server") / auth.api.addMember invocation in a try/catch inside
accept-invitation-endpoint.ts, catch errors from auth.api.addMember, log
contextual information (invitation.id, invitation.organization.id, user.id) and
then either rollback the session/cookie you set earlier (clear session and
delete the auth cookie) before returning a handled error response or re-throw a
wrapped error with that context so the caller can present a meaningful message;
ensure you reference auth.api.addMember and the session/cookie teardown logic
currently run earlier in this handler when implementing the rollback.

}

Expand Down
12 changes: 6 additions & 6 deletions packages/trpc/src/router/organization/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,15 +320,15 @@ export const organizationRouter = {
userId: z.string().uuid(),
}),
)
.mutation(async ({ input }) => {
const [member] = await db
.insert(members)
.values({
.mutation(async ({ ctx, input }) => {
const member = await ctx.auth.api.addMember({
body: {
organizationId: input.organizationId,
userId: input.userId,
role: "member",
})
.returning();
},
headers: ctx.headers,
});
return member;
}),

Expand Down