| 72 | * Exported for its test only. |
| 73 | */ |
| 74 | export const authorizeTenant = ( |
| 75 | request: Request, |
| 76 | ): Effect.Effect< |
| 77 | string, |
| 78 | AdminUsersUnauthorized | AdminUsersForbidden, |
| 79 | WorkOSClient | ApiKeyService | UserStoreService | MemberDirectory | WorkOsMirror |
| 80 | > => |
| 81 | Effect.gen(function* () { |
| 82 | // (1) The bearer path. `resolveBearerAuth` (not `resolveApiKeyPrincipal`, |
| 83 | // which rejects org keys for the product plane) is what distinguishes an |
| 84 | // org key from a user key. |
| 85 | const bearer = yield* resolveBearerAuth(request).pipe( |
| 86 | // Every rejected-credential and infra failure collapses to one refusal: |
| 87 | // this plane must not report whether a key exists, belongs to another |
| 88 | // org, or merely lacks privilege. |
| 89 | Effect.catchCause(() => Effect.succeed(null)), |
| 90 | ); |
| 91 | if (bearer !== null) { |
| 92 | if (isPlatformAuth(bearer)) return bearer.organizationId; |
| 93 | // A user-scoped key authenticated fine but names one member; the platform |
| 94 | // plane has no honest way to serve it. |
| 95 | return yield* new AdminUsersForbidden(); |
| 96 | } |
| 97 | |
| 98 | // (2) The session path: an active admin membership in the selected org, |
| 99 | // read from the mirror. |
| 100 | const workos = yield* WorkOSClient; |
| 101 | const session = yield* workos |
| 102 | .authenticateRequest(request) |
| 103 | .pipe(Effect.catchCause(() => Effect.succeed(null))); |
| 104 | if (!session) return yield* new AdminUsersUnauthorized(); |
| 105 | |
| 106 | const selector = orgSelectorFromRequest(request) ?? session.organizationId; |
| 107 | if (!selector) return yield* new AdminUsersForbidden(); |
| 108 | // Re-checks membership against the mirror, so the org selector header can |
| 109 | // only ever name an org the caller already belongs to. That read requires |
| 110 | // an ACTIVE membership and reports its role as `memberRole`, so a pending |
| 111 | // admin invite never resolves and the admin gate is that one value — not |
| 112 | // a second read of the same row. |
| 113 | const org = yield* authorizeOrganizationSelector(session.userId, selector).pipe( |
| 114 | Effect.catchCause(() => Effect.succeed(null)), |
| 115 | ); |
| 116 | if (!org) return yield* new AdminUsersForbidden(); |
| 117 | if (org.memberRole !== "admin") return yield* new AdminUsersForbidden(); |
| 118 | return org.id; |
| 119 | }); |
| 120 | |
| 121 | /** |
| 122 | * Authorize, then run `body` against the tenant's platform view. |