fix: select sub-org handler

This commit is contained in:
Piyush Gupta
2025-11-27 21:26:15 +05:30
parent 2a86b73c5f
commit 5464729c04
5 changed files with 113 additions and 198 deletions
@@ -2693,7 +2693,7 @@ interface SelectSubOrganizationEvent {
metadata: {
organizationId: string;
organizationName: string;
parentOrganizationId: string;
rootOrganizationId: string;
};
}
+29 -79
View File
@@ -43,10 +43,15 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
rateLimit: authRateLimit
},
schema: {
body: z.object({
organizationId: z.string().trim(),
userAgent: z.enum(["cli"]).optional()
}),
body: z
.object({
organizationId: z.string().trim().optional(),
subOrganizationId: z.string().trim().optional(),
userAgent: z.enum(["cli"]).optional()
})
.refine((body) => Boolean(body.organizationId || body.subOrganizationId), {
message: "organizationId or subOrganizationId is required"
}),
response: {
200: z.object({
token: z.string(),
@@ -57,12 +62,25 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
},
handler: async (req, res) => {
const cfg = getConfig();
const tokens = await server.services.login.selectOrganization({
userAgent: req.body.userAgent ?? req.headers["user-agent"],
authJwtToken: req.headers.authorization,
organizationId: req.body.organizationId,
ipAddress: req.realIp
});
let tokens;
const targetOrgId = req.body.subOrganizationId ?? req.body.organizationId ?? "";
if (req.body.subOrganizationId) {
tokens = await server.services.login.selectSubOrganization({
userAgent: req.body.userAgent ?? req.headers["user-agent"],
authJwtToken: req.headers.authorization,
subOrganizationId: req.body.subOrganizationId,
ipAddress: req.realIp
});
} else {
tokens = await server.services.login.selectOrganization({
userAgent: req.body.userAgent ?? req.headers["user-agent"],
authJwtToken: req.headers.authorization,
organizationId: req.body.organizationId as string,
ipAddress: req.realIp
});
}
if (tokens.isMfaEnabled) {
return {
@@ -75,7 +93,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
const githubOauthAccessToken = req.cookies[INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN];
if (githubOauthAccessToken) {
await server.services.githubOrgSync
.syncUserGroups(req.body.organizationId, tokens.user.userId, githubOauthAccessToken)
.syncUserGroups(targetOrgId, tokens.user.userId, githubOauthAccessToken)
.finally(() => {
void res.setCookie(INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN, "", {
httpOnly: true,
@@ -108,74 +126,6 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
}
});
server.route({
method: "POST",
url: "/select-sub-organization",
config: {
rateLimit: authRateLimit
},
schema: {
body: z.object({
subOrganizationId: z.string().trim(),
userAgent: z.enum(["cli"]).optional()
}),
response: {
200: z.object({
token: z.string(),
isMfaEnabled: z.boolean(),
mfaMethod: z.string().optional(),
subOrganization: z
.object({
id: z.string(),
name: z.string(),
slug: z.string()
})
.optional()
})
}
},
handler: async (req, res) => {
const cfg = getConfig();
const result = await server.services.login.selectSubOrganization({
userAgent: req.body.userAgent ?? req.headers["user-agent"],
authJwtToken: req.headers.authorization,
subOrganizationId: req.body.subOrganizationId,
ipAddress: req.realIp
});
if (result.isMfaEnabled) {
return {
token: result.mfa as string,
isMfaEnabled: true,
mfaMethod: result.mfaMethod
};
}
void res.setCookie("jid", result.refresh, {
httpOnly: true,
path: "/",
sameSite: "strict",
secure: cfg.HTTPS_ENABLED
});
addAuthOriginDomainCookie(res);
void res.cookie("infisical-project-assume-privileges", "", {
httpOnly: true,
path: "/",
sameSite: "strict",
secure: cfg.HTTPS_ENABLED,
maxAge: 0
});
return {
token: result.access,
isMfaEnabled: false,
subOrganization: result.subOrganization
};
}
});
server.route({
method: "POST",
url: "/login2",
+41 -27
View File
@@ -725,39 +725,58 @@ export const authLoginServiceFactory = ({
authJwtToken = authJwtToken.replace("Bearer ", "");
const decodedToken = crypto.jwt().verify(authJwtToken, cfg.AUTH_SECRET) as AuthModeJwtTokenPayload;
if (!decodedToken.authMethod) throw new UnauthorizedError({ name: "Auth method not found on existing token" });
if (!decodedToken.organizationId)
throw new BadRequestError({ message: "No organization selected in current token" });
const user = await userDAL.findUserEncKeyByUserId(decodedToken.userId);
if (!user) throw new BadRequestError({ message: "User not found", name: "Find user from token" });
// Check user membership in the sub-organization
const userSubOrgMembership = await membershipUserDAL.findOne({
actorUserId: user.id,
scopeOrgId: subOrganizationId,
scope: AccessScope.Organization,
status: OrgMembershipStatus.Accepted
});
// Fetch the sub-organization
const subOrg = await orgDAL.findById(subOrganizationId);
if (!subOrg) {
throw new BadRequestError({ message: `Sub-organization with ID ${subOrganizationId} not found` });
}
// Verify this is actually a sub-organization of the current root org
if (subOrg.rootOrgId !== decodedToken.organizationId && subOrg.id !== decodedToken.organizationId) {
if (!userSubOrgMembership) {
throw new ForbiddenRequestError({
message: "Sub-organization does not belong to the current organization"
message: `User does not have access to the sub-organization named ${subOrg.name}`
});
}
// Check user membership in the sub-organization
const orgMembership = await membershipUserDAL.findOne({
actorUserId: user.id,
scopeOrgId: subOrganizationId,
scope: AccessScope.Organization
});
const subOrgmembershipRole = await membershipRoleDAL.findOne({ membershipId: userSubOrgMembership.id });
if (!orgMembership) {
throw new ForbiddenRequestError({ message: "User is not a member of this sub-organization" });
// Check if authEnforced is true and the current auth method is not an enforced method
if (
subOrg.authEnforced &&
!isAuthMethodSaml(decodedToken.authMethod) &&
decodedToken.authMethod !== AuthMethod.OIDC &&
!(subOrg.bypassOrgAuthEnabled && subOrgmembershipRole.role === OrgMembershipRole.Admin)
) {
throw new BadRequestError({
message: "Login with the auth method required by your organization."
});
}
if (!orgMembership.isActive) {
throw new ForbiddenRequestError({ message: "User membership in sub-organization is inactive" });
if (subOrg.googleSsoAuthEnforced && decodedToken.authMethod !== AuthMethod.GOOGLE) {
const canBypass = subOrg.bypassOrgAuthEnabled && subOrgmembershipRole.role === OrgMembershipRole.Admin;
if (!canBypass) {
throw new ForbiddenRequestError({
message: "Google SSO is enforced for this organization. Please use Google SSO to login.",
error: "GoogleSsoEnforced"
});
}
}
if (decodedToken.authMethod === AuthMethod.GOOGLE) {
await orgDAL.updateById(subOrg.id, {
googleSsoAuthLastUsed: new Date()
});
}
// Check MFA requirements for the sub-organization
@@ -797,8 +816,8 @@ export const authLoginServiceFactory = ({
user,
userAgent,
ip: ipAddress,
organizationId: decodedToken.organizationId, // Keep root org ID
subOrganizationId, // Add sub-org ID
...(subOrg.rootOrgId && { organizationId: subOrg.rootOrgId }),
subOrganizationId,
isMfaVerified: decodedToken.isMfaVerified,
mfaMethod: decodedToken.mfaMethod
});
@@ -823,7 +842,7 @@ export const authLoginServiceFactory = ({
metadata: {
organizationId: subOrganizationId,
organizationName: subOrg.name,
parentOrganizationId: decodedToken.organizationId
rootOrganizationId: subOrg.rootOrgId ?? ""
}
}
});
@@ -831,12 +850,7 @@ export const authLoginServiceFactory = ({
return {
...tokens,
user,
isMfaEnabled: false,
subOrganization: {
id: subOrg.id,
name: subOrg.name,
slug: subOrg.slug
}
isMfaEnabled: false
};
};