show list of idp on login

This commit is contained in:
miloschwartz
2025-04-18 17:45:59 -04:00
parent b4fda6a1f6
commit 8fa719181a
9 changed files with 156 additions and 70 deletions

View File

@@ -22,7 +22,7 @@ type ValidateOidcTokenParams = {
code: string | undefined;
expectedState: string | undefined;
stateCookie: string | undefined;
idp: {name: string};
idp: { name: string };
};
export default function ValidateOidcToken(props: ValidateOidcTokenParams) {
@@ -64,11 +64,10 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) {
await new Promise((resolve) => setTimeout(resolve, 100));
if (redirectUrl.startsWith("http")) {
window.location.href = res.data.data.redirectUrl; // TODO: validate this to make sure it's safe
window.location.href = res.data.data.redirectUrl; // this is validated by the parent using this component
} else {
router.push(res.data.data.redirectUrl);
}
} catch (e) {
setError(formatAxiosError(e, "Error validating OIDC token"));
} finally {
@@ -103,8 +102,12 @@ export default function ValidateOidcToken(props: ValidateOidcTokenParams) {
<Alert variant="destructive" className="w-full">
<AlertCircle className="h-5 w-5" />
<AlertDescription className="flex flex-col space-y-2">
<span>There was a problem connecting to {props.idp.name}. Please contact your administrator.</span>
<span className="text-xs text-muted-foreground">{error}</span>
<span>
There was a problem connecting to{" "}
{props.idp.name}. Please contact your
administrator.
</span>
<span className="text-xs">{error}</span>
</AlertDescription>
</Alert>
)}

View File

@@ -8,7 +8,7 @@ import {
CardTitle
} from "@/components/ui/card";
import { createApiClient } from "@app/lib/api";
import LoginForm from "@app/components/LoginForm";
import LoginForm, { LoginFormIDP } from "@app/components/LoginForm";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
@@ -17,10 +17,12 @@ import { cleanRedirect } from "@app/lib/cleanRedirect";
type DashboardLoginFormProps = {
redirect?: string;
idps?: LoginFormIDP[];
};
export default function DashboardLoginForm({
redirect
redirect,
idps
}: DashboardLoginFormProps) {
const router = useRouter();
// const api = createApiClient(useEnvContext());
@@ -51,12 +53,15 @@ export default function DashboardLoginForm({
<h1 className="text-2xl font-bold mt-1">
Welcome to Pangolin
</h1>
<p className="text-sm text-muted-foreground">Log in to get started</p>
<p className="text-sm text-muted-foreground">
Log in to get started
</p>
</div>
</CardHeader>
<CardContent>
<LoginForm
redirect={redirect}
idps={idps}
onLogin={() => {
if (redirect) {
const safe = cleanRedirect(redirect);

View File

@@ -6,6 +6,9 @@ import DashboardLoginForm from "./DashboardLoginForm";
import { Mail } from "lucide-react";
import { pullEnv } from "@app/lib/pullEnv";
import { cleanRedirect } from "@app/lib/cleanRedirect";
import db from "@server/db";
import { idp } from "@server/db/schemas";
import { LoginFormIDP } from "@app/components/LoginForm";
export const dynamic = "force-dynamic";
@@ -31,6 +34,12 @@ export default async function Page(props: {
redirectUrl = cleanRedirect(searchParams.redirect as string);
}
const idps = await db.select().from(idp);
const loginIdps = idps.map((idp) => ({
idpId: idp.idpId,
name: idp.name
})) as LoginFormIDP[];
return (
<>
{isInvite && (
@@ -48,7 +57,7 @@ export default async function Page(props: {
</div>
)}
<DashboardLoginForm redirect={redirectUrl} />
<DashboardLoginForm redirect={redirectUrl} idps={loginIdps} />
{(!signUpDisabled || isInvite) && (
<p className="text-center text-muted-foreground mt-4">

View File

@@ -33,7 +33,7 @@ import { useRouter } from "next/navigation";
import { Alert, AlertDescription } from "@app/components/ui/alert";
import { formatAxiosError } from "@app/lib/api";
import { AxiosResponse } from "axios";
import LoginForm from "@app/components/LoginForm";
import LoginForm, { LoginFormIDP } from "@app/components/LoginForm";
import {
AuthWithPasswordResponse,
AuthWithWhitelistResponse
@@ -81,6 +81,7 @@ type ResourceAuthPortalProps = {
id: number;
};
redirect: string;
idps?: LoginFormIDP[];
};
export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
@@ -490,6 +491,7 @@ export default function ResourceAuthPortal(props: ResourceAuthPortalProps) {
className={`${numMethods <= 1 ? "mt-0" : ""}`}
>
<LoginForm
idps={props.idps}
redirect={props.redirect}
onLogin={async () =>
await handleSSOAuth()

View File

@@ -13,6 +13,9 @@ import ResourceNotFound from "./ResourceNotFound";
import ResourceAccessDenied from "./ResourceAccessDenied";
import AccessToken from "./AccessToken";
import { pullEnv } from "@app/lib/pullEnv";
import { LoginFormIDP } from "@app/components/LoginForm";
import db from "@server/db";
import { idp } from "@server/db/schemas";
export default async function ResourceAuthPage(props: {
params: Promise<{ resourceId: number }>;
@@ -84,7 +87,6 @@ export default async function ResourceAuthPage(props: {
redirect(redirectUrl);
}
// convert the dashboard token into a resource session token
let userIsUnauthorized = false;
if (user && authInfo.sso) {
@@ -128,6 +130,12 @@ export default async function ResourceAuthPage(props: {
);
}
const idps = await db.select().from(idp);
const loginIdps = idps.map((idp) => ({
idpId: idp.idpId,
name: idp.name
})) as LoginFormIDP[];
return (
<>
{userIsUnauthorized && isSSOOnly ? (
@@ -148,6 +156,7 @@ export default async function ResourceAuthPage(props: {
id: authInfo.resourceId
}}
redirect={redirectUrl}
idps={loginIdps}
/>
</div>
)}