Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/utils/integrations/providerOAuthCallbackAccess.server.ts
T

52 lines
1.8 KiB
TypeScript

import { runWithRlsContext } from "@hivegrid/db/rls-context.server";
import { TenantUserStatus } from "~/application/enums/tenants/TenantUserStatus";
import { db } from "~/utils/db.server";
import { getUserInfo } from "~/utils/session.server";
export type ProviderOAuthCallbackAccessFailureCode = "session_mismatch" | "membership_inactive";
export class ProviderOAuthCallbackAccessError extends Error {
readonly failureCode: ProviderOAuthCallbackAccessFailureCode;
constructor(failureCode: ProviderOAuthCallbackAccessFailureCode) {
super("Provider OAuth callback identity is no longer authorized");
this.name = "ProviderOAuthCallbackAccessError";
this.failureCode = failureCode;
}
}
/**
* Re-binds signed OAuth state to the current active web session and membership.
* The provider exchange and every subsequent local DB operation run only after
* these checks and inherit the exact tenant/user RLS context.
*/
export async function runAuthenticatedProviderOAuthCallback<T>(input: {
request: Request;
tenantId: string;
userId: string;
callback: () => Promise<T>;
}): Promise<T> {
const session = await getUserInfo(input.request);
if (!session.userId || session.userId !== input.userId) {
throw new ProviderOAuthCallbackAccessError("session_mismatch");
}
return runWithRlsContext({ tenantId: input.tenantId, userId: input.userId }, async () => {
const membership = await db.tenantUser.findFirst({
where: {
tenantId: input.tenantId,
userId: input.userId,
status: TenantUserStatus.ACTIVE,
tenant: { active: true },
user: { active: true },
},
select: { id: true },
});
if (!membership) {
throw new ProviderOAuthCallbackAccessError("membership_inactive");
}
return input.callback();
});
}