From 14158d1c729f33862c40f68ced7a884376b2c284 Mon Sep 17 00:00:00 2001 From: Dipendra Upreti Date: Thu, 8 Feb 2024 18:08:31 +0545 Subject: [PATCH] feat(user): tenant owner can sign in to its tenant app --- .../emailPasswordSignIn.ts | 48 +++++++++++++++---- .../supertokens/utils/isTenantOwnerEmail.ts | 41 ++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 packages/multi-tenant/src/supertokens/utils/isTenantOwnerEmail.ts diff --git a/packages/multi-tenant/src/supertokens/recipes/third-party-email-password/emailPasswordSignIn.ts b/packages/multi-tenant/src/supertokens/recipes/third-party-email-password/emailPasswordSignIn.ts index 4a0084cb3..c359ef1f9 100644 --- a/packages/multi-tenant/src/supertokens/recipes/third-party-email-password/emailPasswordSignIn.ts +++ b/packages/multi-tenant/src/supertokens/recipes/third-party-email-password/emailPasswordSignIn.ts @@ -1,7 +1,10 @@ import { formatDate } from "@dzangolab/fastify-slonik"; -import { UserService } from "@dzangolab/fastify-user"; +import { ROLE_USER, UserService } from "@dzangolab/fastify-user"; +import UserRoles from "supertokens-node/recipe/userroles"; +import { ROLE_TENANT_OWNER } from "../../../constants"; import Email from "../../utils/email"; +import isTenantOwnerEmail from "../../utils/isTenantOwnerEmail"; import type { AuthUser, @@ -20,11 +23,21 @@ const emailPasswordSignIn = ( const { config, log, slonik } = fastify; return async (input) => { - input.email = Email.addTenantPrefix( - config, - input.email, - input.userContext.tenant - ); + if ( + input.userContext.tenant && + !(await isTenantOwnerEmail( + config, + slonik, + input.email, + input.userContext.tenant + )) + ) { + input.email = Email.addTenantPrefix( + config, + input.email, + input.userContext.tenant + ); + } const originalResponse = await originalImplementation.emailPasswordSignIn( input @@ -40,12 +53,29 @@ const emailPasswordSignIn = ( UserUpdateInput >(config, slonik, input.userContext.dbSchema); - const user = await userService.findById(originalResponse.user.id); + let user = await userService.findById(originalResponse.user.id); if (!user) { - log.error(`User record not found for userId ${originalResponse.user.id}`); + const { roles } = await UserRoles.getRolesForUser( + originalResponse.user.id + ); + + if (input.userContext.tenant && roles.includes(ROLE_TENANT_OWNER)) { + if (input.userContext.tenant) { + await UserRoles.addRoleToUser(originalResponse.user.id, ROLE_USER); + } + + user = (await userService.create({ + id: originalResponse.user.id, + email: originalResponse.user.email, + })) as User & QueryResultRow; + } else { + log.error( + `User record not found for userId ${originalResponse.user.id}` + ); - return { status: "WRONG_CREDENTIALS_ERROR" }; + return { status: "WRONG_CREDENTIALS_ERROR" }; + } } user.lastLoginAt = Date.now(); diff --git a/packages/multi-tenant/src/supertokens/utils/isTenantOwnerEmail.ts b/packages/multi-tenant/src/supertokens/utils/isTenantOwnerEmail.ts new file mode 100644 index 000000000..b5b492330 --- /dev/null +++ b/packages/multi-tenant/src/supertokens/utils/isTenantOwnerEmail.ts @@ -0,0 +1,41 @@ +import { UserService } from "@dzangolab/fastify-user"; +import humps from "humps"; + +import getMultiTenantConfig from "../../lib/getMultiTenantConfig"; + +import type { Tenant } from "../../types/tenant"; +import type { ApiConfig } from "@dzangolab/fastify-config"; +import type { Database } from "@dzangolab/fastify-slonik"; +import type { + User, + UserCreateInput, + UserUpdateInput, +} from "@dzangolab/fastify-user"; +import type { QueryResultRow } from "slonik"; + +const isTenantOwnerEmail = async ( + config: ApiConfig, + database: Database, + email: string, + tenant: Tenant +) => { + const userService = new UserService< + User & QueryResultRow, + UserCreateInput, + UserUpdateInput + >(config, database); + + const multiTenantConfig = getMultiTenantConfig(config); + + const owner = await userService.findById( + tenant[humps.camelize(multiTenantConfig.table.columns.ownerId)] + ); + + if (owner) { + return email === owner.email; + } + + return false; +}; + +export default isTenantOwnerEmail;