feat: v2 managed organizations (#19341)

* refactor: allow non unique PlatformBilling customerId

* feat: add PlatformBilling managed organizations fields

* feat: ManagedOrganization table

* refactor: bill overdue based on teamId

* refactor: restructure organizations module

* wip: organizations endpoints

* Revert "wip: organizations endpoints"

This reverts commit 0e9e66fc74f31da436f12930c1b6597c650ed621.

* refactor: unique index for managed organizations

* wip: organizations endpoints

* wip: create managed organization

* remove unecessary membership check because we have guard

* feat: create managed org

* feat: get managed org and orgs

* feat: update and delete managed orgs

* wip: api key logic

* feat: allow variable api key length

* feat: refresh managed org api key

* feat: create managed org OAuth clients using api key

* finish merge main

* chore: bump platform libraries

* tests: fix and add more tests to api-auth.strategy.e2e

* refactor: dont request managed org slug and handle metadata as object

* revert: billing service and repository update overdue based on sub and customer ids

* refactor: v2 OAuth client permissions (#19501)

* refactor: v2 permissions as string array

* refactor: frontend work with permissions as string

* tests: test '*' permissions

* fix:managed org creator have profile & test can fetch oauth client

* fix: tests

* fix: OAuthClientCard on frontend

* fix: tests
This commit is contained in:
Lauris Skraucis
2025-02-25 18:10:46 -07:00
committed by GitHub
parent e4ab8d87c4
commit a44bcafcf2
192 changed files with 9103 additions and 619 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
import { ApiKeyModule } from "@/modules/api-key/api-key.module";
import { ApiKeysModule } from "@/modules/api-keys/api-keys.module";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { NextAuthGuard } from "@/modules/auth/guards/next-auth/next-auth.guard";
import { ApiAuthStrategy } from "@/modules/auth/strategies/api-auth/api-auth.strategy";
@@ -17,7 +17,7 @@ import { PassportModule } from "@nestjs/passport";
imports: [
PassportModule,
RedisModule,
ApiKeyModule,
ApiKeysModule,
UsersModule,
MembershipsModule,
TokensModule,
@@ -0,0 +1,12 @@
import { createParamDecorator, ExecutionContext, UnauthorizedException } from "@nestjs/common";
export const GetOrgId = createParamDecorator((_: unknown, ctx: ExecutionContext): number => {
const request = ctx.switchToHttp().getRequest();
const organizationId = request.organizationId;
if (!organizationId) {
throw new UnauthorizedException("GetOrgId decorator: Organization ID not found");
}
return organizationId;
});
@@ -1,5 +1,5 @@
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { RedisService } from "@/modules/redis/redis.service";
import { createMock } from "@golevelup/ts-jest";
import { ExecutionContext } from "@nestjs/common";
@@ -1,7 +1,7 @@
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { ApiAuthGuardUser } from "@/modules/auth/strategies/api-auth/api-auth.strategy";
import { PlatformPlanType } from "@/modules/billing/types";
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { RedisService } from "@/modules/redis/redis.service";
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
@@ -1,4 +1,4 @@
import { OrganizationsMembershipRepository } from "@/modules/organizations/repositories/organizations-membership.repository";
import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository";
import {
Injectable,
CanActivate,
@@ -1,6 +1,6 @@
import { MembershipRoles } from "@/modules/auth/decorators/roles/membership-roles.decorator";
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
import { OrganizationsService } from "@/modules/organizations/services/organizations.service";
import { OrganizationsService } from "@/modules/organizations/index/organizations.service";
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
@@ -20,10 +20,10 @@ export class OrganizationRolesGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user: UserWithProfile = request.user;
const organizationId = user ? this.usersService.getUserMainOrgId(user) : null;
const organizationId = this.getOrganizationId(context);
if (!user || !organizationId) {
throw new ForbiddenException("No organization associated with the user.");
throw new ForbiddenException("OrganizationRolesGuard - No organization associated with the user.");
}
await this.isPlatform(organizationId);
@@ -40,13 +40,25 @@ export class OrganizationRolesGuard implements CanActivate {
async isPlatform(organizationId: number) {
const isPlatform = await this.organizationsService.isPlatform(organizationId);
if (!isPlatform) {
throw new ForbiddenException("Organization is not a platform (SHP).");
throw new ForbiddenException("OrganizationRolesGuard - Organization is not a platform (SHP).");
}
}
getOrganizationId(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const user: UserWithProfile = request.user;
const authMethodOrganizationId = request.organizationId;
if (authMethodOrganizationId) return authMethodOrganizationId;
const userOrganizationId = user ? this.usersService.getUserMainOrgId(user) : null;
return userOrganizationId;
}
isMembershipAccepted(accepted: boolean) {
if (!accepted) {
throw new ForbiddenException(`User has not accepted membership in the organization.`);
throw new ForbiddenException(
`OrganizationRolesGuard - User has not accepted membership in the organization.`
);
}
}
@@ -57,7 +69,9 @@ export class OrganizationRolesGuard implements CanActivate {
const hasRequiredRole = allowedRoles.includes(membershipRole);
if (!hasRequiredRole) {
throw new ForbiddenException(`User must have one of the roles: ${allowedRoles.join(", ")}.`);
throw new ForbiddenException(
`OrganizationRolesGuard - User must have one of the roles: ${allowedRoles.join(", ")}.`
);
}
}
}
@@ -1,4 +1,4 @@
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { RedisService } from "@/modules/redis/redis.service";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@@ -0,0 +1,43 @@
import { ManagedOrganizationsRepository } from "@/modules/organizations/organizations/managed-organizations.repository";
import {
Injectable,
CanActivate,
ExecutionContext,
ForbiddenException,
NotFoundException,
} from "@nestjs/common";
import { Request } from "express";
import { Team } from "@calcom/prisma/client";
@Injectable()
export class IsManagedOrgInManagerOrg implements CanActivate {
constructor(private managedOrganizationsRepository: ManagedOrganizationsRepository) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request & { managedOrganization: Team }>();
const managedOrgId: string = request.params.managedOrganizationId;
const managerOrgId: string = request.params.orgId;
if (!managerOrgId) {
throw new ForbiddenException("No manager org id found in request params.");
}
if (!managedOrgId) {
throw new ForbiddenException("No managed org id found in request params.");
}
const managedOrganization = await this.managedOrganizationsRepository.getByManagerManagedIds(
Number(managerOrgId),
Number(managedOrgId)
);
if (!managedOrganization) {
throw new NotFoundException(
`Managed organization with id=${managedOrgId} within manager organization with id=${managerOrgId} not found.`
);
}
return true;
}
}
@@ -1,4 +1,4 @@
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { RedisService } from "@/modules/redis/redis.service";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@@ -1,5 +1,5 @@
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsWebhooksRepository } from "@/modules/organizations/repositories/organizations-webhooks.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { OrganizationsWebhooksRepository } from "@/modules/organizations/webhooks/organizations-webhooks.repository";
import { RedisService } from "@/modules/redis/redis.service";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@@ -1,4 +1,4 @@
import { OrganizationsTeamsRepository } from "@/modules/organizations/repositories/organizations-teams.repository";
import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository";
import {
Injectable,
CanActivate,
@@ -1,4 +1,4 @@
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@@ -1,4 +1,4 @@
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@@ -1,5 +1,6 @@
import appConfig from "@/config/app";
import { ApiKeyRepository } from "@/modules/api-key/api-key-repository";
import { AuthMethods } from "@/lib/enums/auth-methods";
import { ApiKeysRepository } from "@/modules/api-keys/api-keys-repository";
import { DeploymentsRepository } from "@/modules/deployments/deployments.repository";
import { DeploymentsService } from "@/modules/deployments/deployments.service";
import { JwtService } from "@/modules/jwt/jwt.service";
@@ -9,6 +10,7 @@ import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { ProfilesModule } from "@/modules/profiles/profiles.module";
import { TokensRepository } from "@/modules/tokens/tokens.repository";
import { UsersService } from "@/modules/users/services/users.service";
import { UsersRepository } from "@/modules/users/users.repository";
import { ExecutionContext, HttpException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
@@ -16,6 +18,7 @@ import { ConfigModule } from "@nestjs/config";
import { JwtService as NestJwtService } from "@nestjs/jwt";
import { Test, TestingModule } from "@nestjs/testing";
import { PlatformOAuthClient, Team, User } from "@prisma/client";
import { createRequest } from "node-mocks-http";
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture";
@@ -27,6 +30,7 @@ import { randomString } from "test/utils/randomString";
import { X_CAL_CLIENT_ID, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
import { ApiAuthGuardRequest } from "./api-auth.strategy";
import { ApiAuthStrategy } from "./api-auth.strategy";
describe("ApiAuthStrategy", () => {
@@ -35,7 +39,9 @@ describe("ApiAuthStrategy", () => {
let tokensRepositoryFixture: TokensRepositoryFixture;
let teamRepositoryFixture: TeamRepositoryFixture;
let organization: Team;
let organizationTwo: Team;
let oAuthClient: PlatformOAuthClient;
let oAuthClientTwo: PlatformOAuthClient;
let apiKeysRepositoryFixture: ApiKeysRepositoryFixture;
let oAuthClientRepositoryFixture: OAuthClientRepositoryFixture;
let profilesRepositoryFixture: ProfileRepositoryFixture;
@@ -65,7 +71,8 @@ describe("ApiAuthStrategy", () => {
ConfigService,
OAuthFlowService,
UsersRepository,
ApiKeyRepository,
UsersService,
ApiKeysRepository,
DeploymentsService,
OAuthClientRepository,
PrismaReadService,
@@ -84,7 +91,11 @@ describe("ApiAuthStrategy", () => {
teamRepositoryFixture = new TeamRepositoryFixture(module);
oAuthClientRepositoryFixture = new OAuthClientRepositoryFixture(module);
profilesRepositoryFixture = new ProfileRepositoryFixture(module);
organization = await teamRepositoryFixture.create({ name: `api-auth-organization-${randomString()}` });
organization = await teamRepositoryFixture.create({ name: `api-auth-organization-1-${randomString()}` });
organizationTwo = await teamRepositoryFixture.create({
name: `api-auth-organization-2-${randomString()}`,
});
validApiKeyUser = await userRepositoryFixture.create({
email: validApiKeyEmail,
});
@@ -103,6 +114,13 @@ describe("ApiAuthStrategy", () => {
organization: { connect: { id: organization.id } },
});
await profilesRepositoryFixture.create({
uid: "asd-asd",
username: validOAuthEmail,
user: { connect: { id: validOAuthUser.id } },
organization: { connect: { id: organizationTwo.id } },
});
const data = {
logo: "logo-url",
name: "name",
@@ -110,6 +128,7 @@ describe("ApiAuthStrategy", () => {
permissions: 32,
};
oAuthClient = await oAuthClientRepositoryFixture.create(organization.id, data, "secret");
oAuthClientTwo = await oAuthClientRepositoryFixture.create(organizationTwo.id, data, "secret");
});
describe("authenticate with strategy", () => {
@@ -119,25 +138,68 @@ describe("ApiAuthStrategy", () => {
oAuthClient.id
);
const user = await strategy.accessTokenStrategy(accessToken);
const mockRequest = createRequest() as ApiAuthGuardRequest;
mockRequest.authMethod = AuthMethods.ACCESS_TOKEN;
mockRequest.organizationId = null;
const user = await strategy.accessTokenStrategy(accessToken, mockRequest);
expect(user).toBeDefined();
expect(user?.id).toEqual(validAccessTokenUser.id);
});
it("should return user associated with valid api key", async () => {
it("should return user associated with valid api key and correctly set organizationId for org1", async () => {
const now = new Date();
now.setDate(now.getDate() + 1);
const { keyString } = await apiKeysRepositoryFixture.createApiKey(validApiKeyUser.id, now);
const { keyString } = await apiKeysRepositoryFixture.createApiKey(
validApiKeyUser.id,
now,
organization.id
);
const user = await strategy.apiKeyStrategy(keyString);
const mockRequest = createRequest() as ApiAuthGuardRequest;
mockRequest.authMethod = AuthMethods.API_KEY;
mockRequest.organizationId = null;
const user = await strategy.apiKeyStrategy(keyString, mockRequest);
expect(user).toBeDefined();
expect(user?.id).toEqual(validApiKeyUser.id);
expect(mockRequest.organizationId).toEqual(organization.id);
});
it("should return user associated with valid api key and correctly set organizationId for org2", async () => {
const now = new Date();
now.setDate(now.getDate() + 1);
const { keyString } = await apiKeysRepositoryFixture.createApiKey(
validApiKeyUser.id,
now,
organizationTwo.id
);
const mockRequest = createRequest() as ApiAuthGuardRequest;
mockRequest.authMethod = AuthMethods.API_KEY;
mockRequest.organizationId = null;
const user = await strategy.apiKeyStrategy(keyString, mockRequest);
expect(user).toBeDefined();
expect(user?.id).toEqual(validApiKeyUser.id);
expect(mockRequest.organizationId).toEqual(organizationTwo.id);
});
it("should return user associated with valid OAuth client", async () => {
const user = await strategy.oAuthClientStrategy(oAuthClient.id, oAuthClient.secret);
const mockRequest = createRequest() as ApiAuthGuardRequest;
mockRequest.authMethod = AuthMethods.OAUTH_CLIENT;
mockRequest.organizationId = null;
const user = await strategy.oAuthClientStrategy(oAuthClient.id, oAuthClient.secret, mockRequest);
expect(user).toBeDefined();
expect(user.id).toEqual(validOAuthUser.id);
expect(mockRequest.organizationId).toEqual(organization.id);
});
it("should return user associated with valid OAuth client for org2", async () => {
const mockRequest = createRequest() as ApiAuthGuardRequest;
mockRequest.authMethod = AuthMethods.OAUTH_CLIENT;
mockRequest.organizationId = null;
const user = await strategy.oAuthClientStrategy(oAuthClientTwo.id, oAuthClientTwo.secret, mockRequest);
expect(user).toBeDefined();
expect(user.id).toEqual(validOAuthUser.id);
expect(mockRequest.organizationId).toEqual(organizationTwo.id);
});
it("should throw 401 if api key is invalid", async () => {
@@ -2,12 +2,13 @@ import { hashAPIKey, isApiKey, stripApiKey } from "@/lib/api-key";
import { AuthMethods } from "@/lib/enums/auth-methods";
import { isOriginAllowed } from "@/lib/is-origin-allowed/is-origin-allowed";
import { BaseStrategy } from "@/lib/passport/strategies/types";
import { ApiKeyRepository } from "@/modules/api-key/api-key-repository";
import { ApiKeysRepository } from "@/modules/api-keys/api-keys-repository";
import { DeploymentsService } from "@/modules/deployments/deployments.service";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
import { ProfilesRepository } from "@/modules/profiles/profiles.repository";
import { TokensRepository } from "@/modules/tokens/tokens.repository";
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile, UsersRepository } from "@/modules/users/users.repository";
import { Injectable, InternalServerErrorException, UnauthorizedException } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
@@ -18,7 +19,11 @@ import { getToken } from "next-auth/jwt";
import { INVALID_ACCESS_TOKEN, X_CAL_CLIENT_ID, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
export type ApiAuthGuardUser = UserWithProfile & { isSystemAdmin: boolean };
export type ApiAuthGuardRequest = Request & {
authMethod: AuthMethods;
organizationId: number | null;
user: ApiAuthGuardUser;
};
export const NO_AUTH_PROVIDED_MESSAGE =
"No authentication method provided. Either pass an API key as 'Bearer' header or OAuth client credentials as 'x-cal-secret-key' and 'x-cal-client-id' headers";
@Injectable()
@@ -29,14 +34,15 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
private readonly oauthFlowService: OAuthFlowService,
private readonly tokensRepository: TokensRepository,
private readonly userRepository: UsersRepository,
private readonly apiKeyRepository: ApiKeyRepository,
private readonly apiKeyRepository: ApiKeysRepository,
private readonly oauthRepository: OAuthClientRepository,
private readonly profilesRepository: ProfilesRepository
private readonly profilesRepository: ProfilesRepository,
private readonly usersService: UsersService
) {
super();
}
async authenticate(request: Request & { authMethod: AuthMethods }) {
async authenticate(request: ApiAuthGuardRequest) {
try {
const { params } = request;
const oAuthClientSecret = request.get(X_CAL_SECRET_KEY);
@@ -45,7 +51,7 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
if (oAuthClientId && oAuthClientSecret) {
request.authMethod = AuthMethods["OAUTH_CLIENT"];
return await this.authenticateOAuthClient(oAuthClientId, oAuthClientSecret);
return await this.authenticateOAuthClient(oAuthClientId, oAuthClientSecret, request);
}
if (bearerToken) {
@@ -53,7 +59,7 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
request.authMethod = isApiKey(bearerToken, this.config.get<string>("api.apiKeyPrefix") ?? "cal_")
? AuthMethods["API_KEY"]
: AuthMethods["ACCESS_TOKEN"];
return await this.authenticateBearerToken(bearerToken, requestOrigin);
return await this.authenticateBearerToken(bearerToken, request, requestOrigin);
}
const nextAuthSecret = this.config.get("next.authSecret", { infer: true });
@@ -61,7 +67,7 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
if (nextAuthToken) {
request.authMethod = AuthMethods["NEXT_AUTH"];
return await this.authenticateNextAuth(nextAuthToken);
return await this.authenticateNextAuth(nextAuthToken, request);
}
throw new UnauthorizedException(NO_AUTH_PROVIDED_MESSAGE);
@@ -75,8 +81,8 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
}
}
async authenticateNextAuth(token: { email?: string | null }) {
const user = await this.nextAuthStrategy(token);
async authenticateNextAuth(token: { email?: string | null }, request: ApiAuthGuardRequest) {
const user = await this.nextAuthStrategy(token, request);
return this.success(this.getSuccessUser(user));
}
@@ -87,12 +93,16 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
};
}
async authenticateOAuthClient(oAuthClientId: string, oAuthClientSecret: string) {
const user = await this.oAuthClientStrategy(oAuthClientId, oAuthClientSecret);
async authenticateOAuthClient(
oAuthClientId: string,
oAuthClientSecret: string,
request: ApiAuthGuardRequest
) {
const user = await this.oAuthClientStrategy(oAuthClientId, oAuthClientSecret, request);
return this.success(this.getSuccessUser(user));
}
async oAuthClientStrategy(oAuthClientId: string, oAuthClientSecret: string) {
async oAuthClientStrategy(oAuthClientId: string, oAuthClientSecret: string, request: ApiAuthGuardRequest) {
const client = await this.oauthRepository.getOAuthClient(oAuthClientId);
if (!client) {
@@ -115,14 +125,20 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
throw new UnauthorizedException("No user associated with the provided OAuth client");
}
request.organizationId = client.organizationId;
return user;
}
async authenticateBearerToken(authString: string, requestOrigin: string | undefined) {
async authenticateBearerToken(
authString: string,
request: ApiAuthGuardRequest,
requestOrigin: string | undefined
) {
try {
const user = isApiKey(authString, this.config.get<string>("api.apiKeyPrefix") ?? "cal_")
? await this.apiKeyStrategy(authString)
: await this.accessTokenStrategy(authString, requestOrigin);
? await this.apiKeyStrategy(authString, request)
: await this.accessTokenStrategy(authString, request, requestOrigin);
if (!user) {
return this.error(new UnauthorizedException("No user associated with the provided token"));
@@ -139,7 +155,7 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
}
}
async apiKeyStrategy(apiKey: string) {
async apiKeyStrategy(apiKey: string, request: ApiAuthGuardRequest) {
const isLicenseValid = await this.deploymentsService.checkLicense();
if (!isLicenseValid) {
throw new UnauthorizedException("Invalid or missing CALCOM_LICENSE_KEY environment variable");
@@ -163,10 +179,12 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
}
const user: UserWithProfile | null = await this.userRepository.findByIdWithProfile(apiKeyOwnerId);
request.organizationId = keyData.teamId;
return user;
}
async accessTokenStrategy(accessToken: string, origin?: string) {
async accessTokenStrategy(accessToken: string, request: ApiAuthGuardRequest, origin?: string) {
const accessTokenValid = await this.oauthFlowService.validateAccessToken(accessToken);
if (!accessTokenValid) {
throw new UnauthorizedException(INVALID_ACCESS_TOKEN);
@@ -190,10 +208,17 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
}
const user: UserWithProfile | null = await this.userRepository.findByIdWithProfile(ownerId);
if (!user) {
throw new UnauthorizedException("User associated with the authentication api key not found.");
}
const organizationId = this.usersService.getUserMainOrgId(user) as number;
request.organizationId = organizationId;
return user;
}
async nextAuthStrategy(token: { email?: string | null }) {
async nextAuthStrategy(token: { email?: string | null }, request: ApiAuthGuardRequest) {
if (!token.email) {
throw new UnauthorizedException("Email not found in the authentication token.");
}
@@ -202,6 +227,8 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
if (!user) {
throw new UnauthorizedException("User associated with the authentication token email not found.");
}
const organizationId = this.usersService.getUserMainOrgId(user) as number;
request.organizationId = organizationId;
return user;
}