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
+1
View File
@@ -115,6 +115,7 @@ declare module "@fastify/request-context" {
declare module "fastify" { declare module "fastify" {
interface Session { interface Session {
callbackPort: string; callbackPort: string;
isAdminLogin: boolean;
} }
interface FastifyRequest { interface FastifyRequest {
+41 -9
View File
@@ -166,17 +166,24 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
schema: { schema: {
querystring: z.object({ querystring: z.object({
callback_port: z.string().optional() callback_port: z.string().optional(),
is_admin_login: z
.string()
.optional()
.transform((val) => val === "true")
}) })
}, },
preValidation: [ preValidation: [
async (req, res) => { 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 // ensure fresh session state per login attempt
await req.session.regenerate(); await req.session.regenerate();
if (callbackPort) { if (callbackPort) {
req.session.set("callbackPort", callbackPort); req.session.set("callbackPort", callbackPort);
} }
if (isAdminLogin) {
req.session.set("isAdminLogin", isAdminLogin);
}
return ( return (
passport.authenticate("google", { passport.authenticate("google", {
scope: ["profile", "email"], scope: ["profile", "email"],
@@ -200,10 +207,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference // this is due to zod type difference
}) as never, }) as never,
handler: async (req, res) => { handler: async (req, res) => {
const isAdminLogin = req.session.get("isAdminLogin");
await req.session.destroy(); await req.session.destroy();
if (req.passportUser.isUserCompleted) { if (req.passportUser.isUserCompleted) {
return res.redirect( 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( return res.redirect(
@@ -217,18 +227,26 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
schema: { schema: {
querystring: z.object({ querystring: z.object({
callback_port: z.string().optional() callback_port: z.string().optional(),
is_admin_login: z
.string()
.optional()
.transform((val) => val === "true")
}) })
}, },
preValidation: [ preValidation: [
async (req, res) => { 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 // ensure fresh session state per login attempt
await req.session.regenerate(); await req.session.regenerate();
if (callbackPort) { if (callbackPort) {
req.session.set("callbackPort", callbackPort); req.session.set("callbackPort", callbackPort);
} }
if (isAdminLogin) {
req.session.set("isAdminLogin", isAdminLogin);
}
return ( return (
passport.authenticate("github", { passport.authenticate("github", {
session: false, session: false,
@@ -289,10 +307,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// this is due to zod type difference // this is due to zod type difference
}) as any, }) as any,
handler: async (req, res) => { handler: async (req, res) => {
const isAdminLogin = req.session.get("isAdminLogin");
await req.session.destroy(); await req.session.destroy();
if (req.passportUser.isUserCompleted) { if (req.passportUser.isUserCompleted) {
return res.redirect( 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( return res.redirect(
@@ -306,18 +327,26 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
schema: { schema: {
querystring: z.object({ querystring: z.object({
callback_port: z.string().optional() callback_port: z.string().optional(),
is_admin_login: z
.string()
.optional()
.transform((val) => val === "true")
}) })
}, },
preValidation: [ preValidation: [
async (req, res) => { 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 // ensure fresh session state per login attempt
await req.session.regenerate(); await req.session.regenerate();
if (callbackPort) { if (callbackPort) {
req.session.set("callbackPort", callbackPort); req.session.set("callbackPort", callbackPort);
} }
if (isAdminLogin) {
req.session.set("isAdminLogin", isAdminLogin);
}
return ( return (
passport.authenticate("gitlab", { passport.authenticate("gitlab", {
session: false, session: false,
@@ -342,10 +371,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as any, }) as any,
handler: async (req, res) => { handler: async (req, res) => {
const isAdminLogin = req.session.get("isAdminLogin");
await req.session.destroy(); await req.session.destroy();
if (req.passportUser.isUserCompleted) { if (req.passportUser.isUserCompleted) {
return res.redirect( 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( return res.redirect(
@@ -269,12 +269,14 @@ export const InitialStep = ({
variant="outline_bg" variant="outline_bg"
onClick={() => { onClick={() => {
const callbackPort = queryParams.get("callback_port"); const callbackPort = queryParams.get("callback_port");
const queryString = [
callbackPort && `callback_port=${callbackPort}`,
isAdmin && "is_admin_login=true"
]
.filter(Boolean)
.join("&");
window.open( window.open(`/api/v1/sso/redirect/google${queryString ? `?${queryString}` : ""}`);
`/api/v1/sso/redirect/google${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close(); window.close();
}} }}
className="h-10 w-full bg-mineshaft-600" className="h-10 w-full bg-mineshaft-600"
@@ -291,13 +293,14 @@ export const InitialStep = ({
variant="outline_bg" variant="outline_bg"
onClick={() => { onClick={() => {
const callbackPort = queryParams.get("callback_port"); const callbackPort = queryParams.get("callback_port");
const queryString = [
callbackPort && `callback_port=${callbackPort}`,
isAdmin && "is_admin_login=true"
]
.filter(Boolean)
.join("&");
window.open( window.open(`/api/v1/sso/redirect/github${queryString ? `?${queryString}` : ""}`);
`/api/v1/sso/redirect/github${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close(); window.close();
}} }}
className="h-10 w-full bg-mineshaft-600" className="h-10 w-full bg-mineshaft-600"
@@ -314,13 +317,14 @@ export const InitialStep = ({
variant="outline_bg" variant="outline_bg"
onClick={() => { onClick={() => {
const callbackPort = queryParams.get("callback_port"); const callbackPort = queryParams.get("callback_port");
const queryString = [
callbackPort && `callback_port=${callbackPort}`,
isAdmin && "is_admin_login=true"
]
.filter(Boolean)
.join("&");
window.open( window.open(`/api/v1/sso/redirect/gitlab${queryString ? `?${queryString}` : ""}`);
`/api/v1/sso/redirect/gitlab${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close(); window.close();
}} }}
className="h-10 w-full bg-mineshaft-600" className="h-10 w-full bg-mineshaft-600"
@@ -27,9 +27,16 @@ type Props = {
email: string; email: string;
password: string; password: string;
setPassword: (password: string) => void; 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 [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); 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 // case: user has orgs, so we navigate the user to select an org
if (userOrgs.length > 0) { if (userOrgs.length > 0) {
navigateToSelectOrganization(callbackPort); navigateToSelectOrganization(callbackPort, isAdminLogin);
} }
// case: no orgs found, so we navigate the user to create an org // case: no orgs found, so we navigate the user to create an org
else { 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 // case: user has orgs, so we navigate the user to select an org
if (userOrgs.length > 0) { if (userOrgs.length > 0) {
navigateToSelectOrganization(callbackPort); navigateToSelectOrganization(callbackPort, isAdminLogin);
} }
// case: no orgs found, so we navigate the user to create an org // case: no orgs found, so we navigate the user to create an org
else { else {
@@ -249,7 +256,7 @@ export const PasswordStep = ({ providerAuthToken, email, password, setPassword }
const userOrgs = await fetchOrganizations(); const userOrgs = await fetchOrganizations();
if (userOrgs.length > 0) { if (userOrgs.length > 0) {
navigateToSelectOrganization(); navigateToSelectOrganization(undefined, isAdminLogin);
} else { } else {
await navigateUserToOrg(navigate); await navigateUserToOrg(navigate);
} }
@@ -34,6 +34,7 @@ export const LoginSsoPage = () => {
email={username} email={username}
password={password} password={password}
setPassword={setPassword} setPassword={setPassword}
isAdminLogin={search.isAdminLogin}
/> />
); );
default: default:
@@ -5,7 +5,8 @@ import { z } from "zod";
import { LoginSsoPage } from "./LoginSsoPage"; import { LoginSsoPage } from "./LoginSsoPage";
const LoginSSOQueryParamsSchema = z.object({ 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")({ export const Route = createFileRoute("/_restrict-login-signup/login/sso")({