diff --git a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts index 5da92edd66..e4a3d6493b 100644 --- a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts +++ b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/actions.ts @@ -29,7 +29,8 @@ export async function listInvitations(teamId: string) { })); } -export async function inviteUser(teamId: string, email: string, callbackUrl: string) { +export async function inviteUser(teamId: string, email: string, origin: string) { + const callbackUrl = new URL(stackServerApp.urls.teamInvitation, origin).toString(); const user = await stackServerApp.getUser(); const team = await user?.getTeam(teamId); if (!team) { diff --git a/apps/e2e/tests/backend/endpoints/api/v1/team-invitations.test.ts b/apps/e2e/tests/backend/endpoints/api/v1/team-invitations.test.ts index fe01b128ef..c52f1932f7 100644 --- a/apps/e2e/tests/backend/endpoints/api/v1/team-invitations.test.ts +++ b/apps/e2e/tests/backend/endpoints/api/v1/team-invitations.test.ts @@ -525,3 +525,33 @@ it("errors with item_quantity_insufficient_amount when accepting invite without } `); }); + +it("should error when untrusted callback URL is provided", async ({ expect }) => { + const { teamId } = await Team.create(); + const receiveMailbox = createMailbox(); + + backendContext.set({ userAuth: null }); + const sendTeamInvitationResponse = await niceBackendFetch("/api/v1/team-invitations/send-code", { + method: "POST", + accessType: "server", + body: { + email: receiveMailbox.emailAddress, + team_id: teamId, + callback_url: "https://malicious.com/callback", + }, + }); + + expect(sendTeamInvitationResponse).toMatchInlineSnapshot(` + NiceResponse { + "status": 400, + "body": { + "code": "REDIRECT_URL_NOT_WHITELISTED", + "error": "Redirect URL not whitelisted. Did you forget to add this domain to the trusted domains list on the Stack Auth dashboard?", + }, + "headers": Headers { + "x-stack-known-error": "REDIRECT_URL_NOT_WHITELISTED", +