Merge pull request #3471 from Infisical/feat/add-support-for-org-sso-bypass-for-sso

feat: enabled sso (google, gitlab, github) to bypass org sso
This commit is contained in:
Sheen
2025-04-28 22:35:53 +08:00
committed by GitHub
6 changed files with 90 additions and 29 deletions

View File

@@ -118,6 +118,7 @@ declare module "@fastify/request-context" {
declare module "fastify" {
interface Session {
callbackPort: string;
isAdminLogin: boolean;
}
interface FastifyRequest {

View File

@@ -168,17 +168,24 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
method: "GET",
schema: {
querystring: z.object({
callback_port: z.string().optional()
callback_port: z.string().optional(),
is_admin_login: z
.string()
.optional()
.transform((val) => val === "true")
})
},
preValidation: [
async (req, res) => {
const { callback_port: callbackPort } = req.query;
const { callback_port: callbackPort, is_admin_login: isAdminLogin } = req.query;
// ensure fresh session state per login attempt
await req.session.regenerate();
if (callbackPort) {
req.session.set("callbackPort", callbackPort);
}
if (isAdminLogin) {
req.session.set("isAdminLogin", isAdminLogin);
}
return (
passport.authenticate("google", {
scope: ["profile", "email"],
@@ -202,10 +209,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference
}) as never,
handler: async (req, res) => {
const isAdminLogin = req.session.get("isAdminLogin");
await req.session.destroy();
if (req.passportUser.isUserCompleted) {
return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}`
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}${
isAdminLogin ? `&isAdminLogin=${isAdminLogin}` : ""
}`
);
}
return res.redirect(
@@ -219,18 +229,26 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
method: "GET",
schema: {
querystring: z.object({
callback_port: z.string().optional()
callback_port: z.string().optional(),
is_admin_login: z
.string()
.optional()
.transform((val) => val === "true")
})
},
preValidation: [
async (req, res) => {
const { callback_port: callbackPort } = req.query;
const { callback_port: callbackPort, is_admin_login: isAdminLogin } = req.query;
// ensure fresh session state per login attempt
await req.session.regenerate();
if (callbackPort) {
req.session.set("callbackPort", callbackPort);
}
if (isAdminLogin) {
req.session.set("isAdminLogin", isAdminLogin);
}
return (
passport.authenticate("github", {
session: false,
@@ -291,6 +309,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference
}) as any,
handler: async (req, res) => {
const isAdminLogin = req.session.get("isAdminLogin");
await req.session.destroy();
if (req.passportUser.externalProviderAccessToken) {
@@ -305,7 +324,9 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
if (req.passportUser.isUserCompleted) {
return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}`
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}${
isAdminLogin ? `&isAdminLogin=${isAdminLogin}` : ""
}`
);
}
return res.redirect(
@@ -319,18 +340,26 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
method: "GET",
schema: {
querystring: z.object({
callback_port: z.string().optional()
callback_port: z.string().optional(),
is_admin_login: z
.string()
.optional()
.transform((val) => val === "true")
})
},
preValidation: [
async (req, res) => {
const { callback_port: callbackPort } = req.query;
const { callback_port: callbackPort, is_admin_login: isAdminLogin } = req.query;
// ensure fresh session state per login attempt
await req.session.regenerate();
if (callbackPort) {
req.session.set("callbackPort", callbackPort);
}
if (isAdminLogin) {
req.session.set("isAdminLogin", isAdminLogin);
}
return (
passport.authenticate("gitlab", {
session: false,
@@ -355,10 +384,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as any,
handler: async (req, res) => {
const isAdminLogin = req.session.get("isAdminLogin");
await req.session.destroy();
if (req.passportUser.isUserCompleted) {
return res.redirect(
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}`
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}${
isAdminLogin ? `&isAdminLogin=${isAdminLogin}` : ""
}`
);
}
return res.redirect(

View File

@@ -269,12 +269,19 @@ export const InitialStep = ({
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
const searchParams = new URLSearchParams();
window.open(
`/api/v1/sso/redirect/google${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
if (callbackPort) {
searchParams.append("callback_port", callbackPort);
}
if (isAdmin) {
searchParams.append("is_admin_login", "true");
}
const queryString = searchParams.toString();
window.open(`/api/v1/sso/redirect/google${queryString ? `?${queryString}` : ""}`);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
@@ -291,13 +298,19 @@ export const InitialStep = ({
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
const searchParams = new URLSearchParams();
window.open(
`/api/v1/sso/redirect/github${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
if (callbackPort) {
searchParams.append("callback_port", callbackPort);
}
if (isAdmin) {
searchParams.append("is_admin_login", "true");
}
const queryString = searchParams.toString();
window.open(`/api/v1/sso/redirect/github${queryString ? `?${queryString}` : ""}`);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
@@ -314,13 +327,19 @@ export const InitialStep = ({
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
const searchParams = new URLSearchParams();
window.open(
`/api/v1/sso/redirect/gitlab${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
if (callbackPort) {
searchParams.append("callback_port", callbackPort);
}
if (isAdmin) {
searchParams.append("is_admin_login", "true");
}
const queryString = searchParams.toString();
window.open(`/api/v1/sso/redirect/gitlab${queryString ? `?${queryString}` : ""}`);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"

View File

@@ -27,9 +27,16 @@ type Props = {
email: string;
password: string;
setPassword: (password: string) => void;
isAdminLogin?: boolean;
};
export const PasswordStep = ({ providerAuthToken, email, password, setPassword }: Props) => {
export const PasswordStep = ({
providerAuthToken,
email,
password,
setPassword,
isAdminLogin
}: Props) => {
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation();
const navigate = useNavigate();
@@ -114,7 +121,7 @@ export const PasswordStep = ({ providerAuthToken, email, password, setPassword }
// case: user has orgs, so we navigate the user to select an org
if (userOrgs.length > 0) {
navigateToSelectOrganization(callbackPort);
navigateToSelectOrganization(callbackPort, isAdminLogin);
}
// case: no orgs found, so we navigate the user to create an org
else {
@@ -216,7 +223,7 @@ export const PasswordStep = ({ providerAuthToken, email, password, setPassword }
// case: user has orgs, so we navigate the user to select an org
if (userOrgs.length > 0) {
navigateToSelectOrganization(callbackPort);
navigateToSelectOrganization(callbackPort, isAdminLogin);
}
// case: no orgs found, so we navigate the user to create an org
else {
@@ -249,7 +256,7 @@ export const PasswordStep = ({ providerAuthToken, email, password, setPassword }
const userOrgs = await fetchOrganizations();
if (userOrgs.length > 0) {
navigateToSelectOrganization();
navigateToSelectOrganization(undefined, isAdminLogin);
} else {
await navigateUserToOrg(navigate);
}

View File

@@ -34,6 +34,7 @@ export const LoginSsoPage = () => {
email={username}
password={password}
setPassword={setPassword}
isAdminLogin={search.isAdminLogin}
/>
);
default:

View File

@@ -5,7 +5,8 @@ import { z } from "zod";
import { LoginSsoPage } from "./LoginSsoPage";
const LoginSSOQueryParamsSchema = z.object({
token: z.string()
token: z.string(),
isAdminLogin: z.boolean().optional().catch(false)
});
export const Route = createFileRoute("/_restrict-login-signup/login/sso")({