copy and appearance updates
This commit is contained in:
parent
9f9f5d83c8
commit
0a70f1cbf2
@ -9,9 +9,15 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-image: url("/wordart.png");
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="react-root" class="h-full min-h-screen bg-muted/40"></div>
|
||||
<div id="react-root" class="h-full min-h-screen"></div>
|
||||
</body>
|
||||
|
||||
<script src="/index.js"></script>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 52 KiB |
@ -11,10 +11,10 @@ export function App() {
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/instant-setup" element={<InstantSetupPage />} />
|
||||
<Route path="/apps/:appId/sso" element={<SSOPage />} />
|
||||
<Route path="/" element={<Page />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/apps/:appId" element={<ViewAppPage />} />
|
||||
<Route path="/apps/:appId/sso" element={<SSOPage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
|
||||
29
src/components/ui/separator.tsx
Normal file
29
src/components/ui/separator.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import * as React from "react"
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Separator = React.forwardRef<
|
||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||
>(
|
||||
(
|
||||
{ className, orientation = "horizontal", decorative = true, ...props },
|
||||
ref
|
||||
) => (
|
||||
<SeparatorPrimitive.Root
|
||||
ref={ref}
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"shrink-0 bg-border",
|
||||
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
Separator.displayName = SeparatorPrimitive.Root.displayName
|
||||
|
||||
export { Separator }
|
||||
@ -27,6 +27,7 @@ import {
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import moment from "moment";
|
||||
import { clsx } from "clsx";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
const formSchema = z.object({
|
||||
email: z.string().min(1, { message: "Email is required." }),
|
||||
@ -82,118 +83,148 @@ export function SSOPage() {
|
||||
});
|
||||
|
||||
async function onSubmit(values: z.infer<typeof formSchema>, e: any) {
|
||||
const key = await window.crypto.subtle.importKey(
|
||||
"jwk",
|
||||
GLOBAL_NONSECURE_KEY,
|
||||
{
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
hash: "SHA-256",
|
||||
},
|
||||
true,
|
||||
["sign"],
|
||||
);
|
||||
setLoading(true);
|
||||
|
||||
const now = moment(new Date()).add(-1, "hour");
|
||||
const expire = moment(new Date()).add(1, "hour");
|
||||
setTimeout(async () => {
|
||||
const key = await window.crypto.subtle.importKey(
|
||||
"jwk",
|
||||
GLOBAL_NONSECURE_KEY,
|
||||
{
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
hash: "SHA-256",
|
||||
},
|
||||
true,
|
||||
["sign"],
|
||||
);
|
||||
|
||||
inputRef.current!.value = await encodeAssertion(key, {
|
||||
idpEntityId: `https://dummyidp.com/apps/${app.id}`,
|
||||
subjectId: `${values.email}@${app.requiredDomain}`,
|
||||
firstName: values.firstName,
|
||||
lastName: values.lastName,
|
||||
spEntityId: app.spEntityId,
|
||||
sessionId: sessionId,
|
||||
now: now.format(),
|
||||
expire: expire.format(),
|
||||
});
|
||||
inputRef.current!.form!.action = app.spAcsUrl;
|
||||
inputRef.current!.form!.submit();
|
||||
const now = moment(new Date()).add(-1, "hour");
|
||||
const expire = moment(new Date()).add(1, "hour");
|
||||
|
||||
inputRef.current!.value = await encodeAssertion(key, {
|
||||
idpEntityId: `https://dummyidp.com/apps/${app.id}`,
|
||||
subjectId: `${values.email}@${app.requiredDomain}`,
|
||||
firstName: values.firstName,
|
||||
lastName: values.lastName,
|
||||
spEntityId: app.spEntityId,
|
||||
sessionId: sessionId,
|
||||
now: now.format(),
|
||||
expire: expire.format(),
|
||||
});
|
||||
inputRef.current!.form!.action = app.spAcsUrl;
|
||||
inputRef.current!.form!.submit();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-screen h-screen">
|
||||
<form method="post">
|
||||
<input type="hidden" name="SAMLResponse" ref={inputRef} />
|
||||
</form>
|
||||
|
||||
<Card className="mx-auto max-w-xl">
|
||||
<CardHeader>
|
||||
<CardTitle>Log on</CardTitle>
|
||||
<CardDescription>
|
||||
Enter some details about who you want DummyIDP to log you in as.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="grid grid-cols-2 gap-4"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex">
|
||||
<Input className="rounded-r-none" {...field} />
|
||||
<span className="inline-flex text-sm items-center rounded-r-md border border-l-0 border-input px-3 text-muted-foreground">
|
||||
@{app.requiredDomain}
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
<div className="m-auto max-w-xl relative">
|
||||
{loading && (
|
||||
<div className="absolute bg-black/80 inset-0 flex justify-center items-center z-10 dark text-white">
|
||||
<img className="m-auto" alt="loading" src="/loading.gif" />
|
||||
</div>
|
||||
)}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Log on</CardTitle>
|
||||
<CardDescription>
|
||||
Enter some details about who you want DummyIDP to say you are.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="grid grid-cols-2 gap-4"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex">
|
||||
<Input className="rounded-r-none" {...field} />
|
||||
<span className="inline-flex text-sm items-center rounded-r-md border border-l-0 border-input px-3 text-muted-foreground">
|
||||
@{app.requiredDomain}
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="firstName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>First Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="lastName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Last Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="mt-4 col-span-2">
|
||||
<Button
|
||||
className={clsx(
|
||||
"w-full",
|
||||
email &&
|
||||
"bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 shadow-xl",
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
>
|
||||
Log on
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="firstName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>First Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="lastName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-1">
|
||||
<FormLabel>Last Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="mt-4 col-span-2">
|
||||
<Button
|
||||
className={clsx(
|
||||
"w-full",
|
||||
email &&
|
||||
"bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 shadow-xl",
|
||||
)}
|
||||
>
|
||||
Log on
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
<Separator className="my-8" />
|
||||
|
||||
<div className="text-xs text-muted-foreground space-y-2">
|
||||
<p>
|
||||
DummyIDP is a fake identity provider. It's a dummy stand-in for
|
||||
something like Okta, Google Workspace, or Microsoft Entra.
|
||||
</p>
|
||||
<p>
|
||||
In the real world, your customers would never see or interact
|
||||
with DummyIDP in any way. In the real world, your customers
|
||||
would see their own IDP, not this fake one.
|
||||
</p>
|
||||
<p>
|
||||
We made DummyIDP because there doesn't exist any free, no-hassle
|
||||
SAML Identity Provider out there that developers can test with.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user