(userId: string, organizationId: string)
| 79 | // 403). |
| 80 | |
| 81 | export const authorizeOrganization = (userId: string, organizationId: string) => |
| 82 | Effect.gen(function* () { |
| 83 | const workos = yield* WorkOSClient; |
| 84 | const memberships = yield* workos.listUserMemberships(userId); |
| 85 | const active = memberships.data.find( |
| 86 | (m: { readonly organizationId: string; readonly status: string }) => |
| 87 | m.organizationId === organizationId && m.status === "active", |
| 88 | ); |
| 89 | if (!active) return null; |
| 90 | |
| 91 | const org = yield* resolveOrganization(organizationId); |
| 92 | // The membership row already names the caller's role — surface it |
| 93 | // normalized so identity resolution can bind the executor's workspace |
| 94 | // write permission without a second WorkOS call. WorkOS issues |
| 95 | // `admin` / `member`; anything unrecognized stays a plain member. |
| 96 | const roleSlug = (active as { readonly role?: { readonly slug?: string } }).role?.slug; |
| 97 | const memberRole: "admin" | "member" = roleSlug === "admin" ? "admin" : "member"; |
| 98 | return { ...org, memberRole }; |
| 99 | }); |
| 100 | |
| 101 | // --------------------------------------------------------------------------- |
| 102 | // Org SELECTOR — the URL is the scope authority, not the session. |
no test coverage detected