feat: enabled sso to bypass org sso

This commit is contained in:
Sheen Capadngan
2025-04-23 02:28:58 +08:00
parent b4e831d3e2
commit 75f1ce7b86
6 changed files with 77 additions and 31 deletions

View File

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

View File

@@ -166,17 +166,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"],
@@ -200,10 +207,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}`
);
}
return res.redirect(
@@ -217,18 +227,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,
@@ -289,10 +307,13 @@ 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.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}`
);
}
return res.redirect(
@@ -306,18 +327,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,
@@ -342,10 +371,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}`
);
}
return res.redirect(

View File

@@ -269,12 +269,14 @@ export const InitialStep = ({
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
const queryString = [
callbackPort && `callback_port=${callbackPort}`,
isAdmin && "is_admin_login=true"
]
.filter(Boolean)
.join("&");
window.open(
`/api/v1/sso/redirect/google${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.open(`/api/v1/sso/redirect/google${queryString ? `?${queryString}` : ""}`);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
@@ -291,13 +293,14 @@ export const InitialStep = ({
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
const queryString = [
callbackPort && `callback_port=${callbackPort}`,
isAdmin && "is_admin_login=true"
]
.filter(Boolean)
.join("&");
window.open(
`/api/v1/sso/redirect/github${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.open(`/api/v1/sso/redirect/github${queryString ? `?${queryString}` : ""}`);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
@@ -314,13 +317,14 @@ export const InitialStep = ({
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
const queryString = [
callbackPort && `callback_port=${callbackPort}`,
isAdmin && "is_admin_login=true"
]
.filter(Boolean)
.join("&");
window.open(
`/api/v1/sso/redirect/gitlab${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
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")({