Skip to content
Closed
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
12 changes: 8 additions & 4 deletions ee/apps/den-api/src/orgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ async function acceptInvitation(invitation: InvitationRow, userId: UserId) {
const invitedMember = invitedMemberRows[0] ?? null
const existingMember = existingMemberRows[0] ?? null
let member = existingMember
let createdMember = false

if (!member && invitedMember) {
await db
Expand All @@ -446,6 +447,7 @@ async function acceptInvitation(invitation: InvitationRow, userId: UserId) {
userId,
role,
})
createdMember = true
}

if (invitation.teamId) {
Expand Down Expand Up @@ -477,7 +479,7 @@ async function acceptInvitation(invitation: InvitationRow, userId: UserId) {
.set({ status: "accepted" })
.where(eq(InvitationTable.id, invitation.id))

return member
return { member, createdMember }
}

export async function acceptInvitationForUser(input: {
Expand Down Expand Up @@ -514,11 +516,13 @@ export async function acceptInvitationForUser(input: {
throw new OrganizationEmailDomainRestrictionError(input.email, allowedEmailDomains ?? [])
}

const member = await acceptInvitation(invitation, input.userId)
await runPostOrganizationMemberChangeHooks({ organizationId: invitation.organizationId, memberId: member.id, change: "added" })
const accepted = await acceptInvitation(invitation, input.userId)
if (accepted.createdMember) {
await runPostOrganizationMemberChangeHooks({ organizationId: invitation.organizationId, memberId: accepted.member.id, change: "added" })
}
return {
invitation,
member,
member: accepted.member,
}
}

Expand Down
26 changes: 20 additions & 6 deletions ee/apps/den-api/src/routes/auth/desktop-handoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ const grantNotFoundSchema = z.object({
message: z.string(),
}).meta({ ref: "DesktopHandoffGrantNotFoundError" })

class DesktopHandoffBaseUrlError extends Error {
constructor(message: string) {
super(message)
this.name = "DesktopHandoffBaseUrlError"
}
}

function readSingleHeader(value: string | null) {
const first = value?.split(",")[0]?.trim() ?? ""
return first || null
Expand Down Expand Up @@ -90,7 +97,7 @@ function withDenProxyPath(origin: string) {
return url.toString().replace(/\/+$/, "")
}

function resolveDesktopDenBaseUrl(request: Request) {
export function resolveDesktopDenBaseUrl(request: Request) {
const originHeader = readSingleHeader(request.headers.get("origin"))
if (originHeader) {
try {
Expand All @@ -109,7 +116,7 @@ function resolveDesktopDenBaseUrl(request: Request) {
const protocol = forwardedProto ?? new URL(request.url).protocol.replace(/:$/, "")
const targetHost = forwardedHost ?? host
if (!targetHost) {
return "https://app.openworklabs.com/api/den"
throw new DesktopHandoffBaseUrlError("Desktop handoff requires a valid request host or forwarded host.")
}

const origin = `${protocol}://${targetHost}`
Expand All @@ -118,11 +125,10 @@ function resolveDesktopDenBaseUrl(request: Request) {
if (isWebAppHost(url.hostname)) {
return withDenProxyPath(url.origin)
}
return origin
} catch {
// Ignore invalid forwarded origins.
throw new DesktopHandoffBaseUrlError("Desktop handoff could not resolve a trusted Den base URL from request configuration.")
}

return origin
}

function buildOpenworkDeepLink(input: {
Expand Down Expand Up @@ -175,7 +181,15 @@ export function registerDesktopAuthRoutes<T extends { Variables: AuthContextVari
consumed_at: null,
})

const denBaseUrl = resolveDesktopDenBaseUrl(c.req.raw)
let denBaseUrl: string
try {
denBaseUrl = resolveDesktopDenBaseUrl(c.req.raw)
} catch (error) {
if (error instanceof DesktopHandoffBaseUrlError) {
return c.json({ error: "desktop_handoff_base_url_invalid", message: error.message }, 400)
}
throw error
}

return c.json({
grant,
Expand Down
Loading