Nits + index page (#5)
This commit is contained in:
parent
8b9e028f09
commit
8937aac406
18
LICENSE
Normal file
18
LICENSE
Normal file
@ -0,0 +1,18 @@
|
||||
Copyright 2024 Codomain Data Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# DummyIDP
|
||||
|
||||
[DummyIDP](https://ssoready.com/docs/dummyidp) is a website you can use to test
|
||||
your application's SAML and SCIM support end-to-end. From your application's
|
||||
perspective, it's exactly like the identity provider ("IDP") your customers use,
|
||||
but unlike commercial IDPs there's no "input your email" or "talk to sales" step
|
||||
to use DummyIDP.
|
||||
|
||||
DummyIDP implements the "Identity Provider" side of the SAML and SCIM protocols.
|
||||
It is meant for use as a way to test your application's support for the "Service
|
||||
Provider" side of the SAML and SCIM protocols. See ["DummyIDP Security
|
||||
Posture"](https://ssoready.com/docs/dummyidp#dummyidp-security-posture) for
|
||||
details.
|
||||
|
||||
## Local development / self-hosting
|
||||
|
||||
DummyIDP is available for free online at https://dummyidp.com. You can also
|
||||
self-host it or hack on it locally.
|
||||
|
||||
DummyIDP is a Next.js application. It is deployed in production on Vercel. You
|
||||
can hack on it yourself by running:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
BIN
public/DummyIDP-Assertion.xml.png
Normal file
BIN
public/DummyIDP-Assertion.xml.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
BIN
public/SCIM-Request.http.png
Normal file
BIN
public/SCIM-Request.http.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@ -31,6 +31,7 @@ import { SimulateLoginButton } from "@/app/apps/[id]/SimulateLoginButton";
|
||||
import { SCIMSettingsForm } from "@/app/apps/[id]/SCIMSettingsForm";
|
||||
import { Metadata } from "next";
|
||||
import { INSECURE_PUBLIC_CERTIFICATE } from "@/lib/insecure-cert";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "App",
|
||||
@ -142,6 +143,15 @@ export default async function Page({ params }: { params: { id: string } }) {
|
||||
<CardTitle>
|
||||
SCIM Settings
|
||||
<DocsLink to="https://ssoready.com/docs/dummyidp#scim-settings" />
|
||||
{app.scimBaseUrl && app.scimBearerToken && (
|
||||
<Badge className="ml-4" variant="outline">
|
||||
Syncing
|
||||
<span className="ml-2 relative flex h-3 w-3">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
|
||||
</span>
|
||||
</Badge>
|
||||
)}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Settings for directory syncing over SCIM. Optional.
|
||||
|
||||
203
src/app/page.tsx
203
src/app/page.tsx
@ -1,50 +1,183 @@
|
||||
import CreateAppButton from "@/components/CreateAppButton";
|
||||
import CreateAppButton, {
|
||||
InlineCreateAppLink,
|
||||
} from "@/components/CreateAppButton";
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Link from "next/link";
|
||||
import { ChevronRightIcon } from "@radix-ui/react-icons";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="relative overflow-hidden">
|
||||
<div className="absolute inset-2 bottom-0 rounded-[2rem] ring-1 ring-inset ring-black/5 bg-[linear-gradient(115deg,var(--tw-gradient-stops))] from-splash-1 from-[28%] via-splash-2 via-[70%] to-splash-3 sm:bg-[linear-gradient(145deg,var(--tw-gradient-stops))]"></div>
|
||||
<>
|
||||
<div className="relative overflow-hidden">
|
||||
<div className="absolute inset-2 bottom-0 rounded-[2rem] ring-1 ring-inset ring-black/5 bg-[linear-gradient(115deg,var(--tw-gradient-stops))] from-splash-1 from-[28%] via-splash-2 via-[70%] to-splash-3 sm:bg-[linear-gradient(145deg,var(--tw-gradient-stops))]"></div>
|
||||
|
||||
<div className="relative px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-2xl lg:max-w-7xl">
|
||||
<div className="-ml-6 pt-12">
|
||||
<Navbar
|
||||
banner={
|
||||
<Link
|
||||
href="https://ssoready.com"
|
||||
className="flex items-center gap-1 rounded-full bg-purple-950/35 px-3 py-0.5 text-sm/6 font-medium text-white hover:bg-purple-950/30"
|
||||
<div className="relative px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-2xl lg:max-w-7xl">
|
||||
<div className="-ml-6 pt-12">
|
||||
<Navbar
|
||||
banner={
|
||||
<Link
|
||||
href="https://ssoready.com"
|
||||
className="flex items-center gap-1 rounded-full bg-purple-950/35 px-3 py-0.5 text-sm/6 font-medium text-white hover:bg-purple-950/30"
|
||||
>
|
||||
Implementing SAML? Check out SSOReady
|
||||
<ChevronRightIcon className="size-4" />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pb-24 pt-16 sm:pb-32 sm:pt-24 md:pb-48 md:pt-32">
|
||||
<h1 className="font-display text-balance text-6xl/[0.9] font-medium tracking-tight text-gray-950 sm:text-8xl/[0.8] md:text-9xl/[0.8]">
|
||||
SAML made easy
|
||||
</h1>
|
||||
<p className="mt-8 max-w-lg text-xl/7 font-medium text-gray-950/75 sm:text-2xl/8">
|
||||
DummyIDP lets you test SAML and SCIM without setting up a
|
||||
full-blown identity provider
|
||||
</p>
|
||||
<div className="mt-12 flex flex-col gap-x-6 gap-y-4 sm:flex-row">
|
||||
<CreateAppButton />
|
||||
<a
|
||||
className="relative inline-flex items-center justify-center px-4 py-[calc(theme(spacing.2)-1px)] rounded-full border border-transparent bg-white/15 shadow-md ring-1 ring-[#D15052]/15 after:absolute after:inset-0 after:rounded-full after:shadow-[inset_0_0_2px_1px_#ffffff4d] whitespace-nowrap text-base font-medium text-gray-950 disabled:bg-white/15 hover:bg-white/20 disabled:opacity-40"
|
||||
data-headlessui-state=""
|
||||
href="https://ssoready.com/docs/dummyidp"
|
||||
>
|
||||
Implementing SAML? Check out SSOReady
|
||||
<ChevronRightIcon className="size-4" />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pb-24 pt-16 sm:pb-32 sm:pt-24 md:pb-48 md:pt-32">
|
||||
<h1 className="font-display text-balance text-6xl/[0.9] font-medium tracking-tight text-gray-950 sm:text-8xl/[0.8] md:text-9xl/[0.8]">
|
||||
SAML made easy
|
||||
</h1>
|
||||
<p className="mt-8 max-w-lg text-xl/7 font-medium text-gray-950/75 sm:text-2xl/8">
|
||||
DummyIDP lets you test SAML and SCIM without setting up a
|
||||
full-blown identity provider
|
||||
</p>
|
||||
<div className="mt-12 flex flex-col gap-x-6 gap-y-4 sm:flex-row">
|
||||
<CreateAppButton />
|
||||
<a
|
||||
className="relative inline-flex items-center justify-center px-4 py-[calc(theme(spacing.2)-1px)] rounded-full border border-transparent bg-white/15 shadow-md ring-1 ring-[#D15052]/15 after:absolute after:inset-0 after:rounded-full after:shadow-[inset_0_0_2px_1px_#ffffff4d] whitespace-nowrap text-base font-medium text-gray-950 disabled:bg-white/15 hover:bg-white/20 disabled:opacity-40"
|
||||
data-headlessui-state=""
|
||||
href="https://ssoready.com/docs"
|
||||
>
|
||||
See docs
|
||||
</a>
|
||||
See docs
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 lg:px-8 py-32">
|
||||
<div className="mx-auto max-w-2xl lg:max-w-7xl">
|
||||
<h2 className="font-mono text-xs/5 font-semibold uppercase tracking-widest text-gray-500 data-[dark]:text-gray-400">
|
||||
Test SAML & SCIM with ease
|
||||
</h2>
|
||||
<h3 className="mt-2 max-w-3xl text-pretty text-4xl font-medium tracking-tighter text-gray-950 data-[dark]:text-white sm:text-6xl">
|
||||
Everything you need to test an Enterprise SSO implementation,
|
||||
without talking to sales
|
||||
</h3>
|
||||
|
||||
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-6">
|
||||
<BentoCard
|
||||
tall
|
||||
eyebrow="Working SAML IDP"
|
||||
title="Real SAML Assertions"
|
||||
description="DummyIDP behaves just like the SAML IDPs your customers use. Your app receives the same SAML protocol messages."
|
||||
graphic={
|
||||
<div className="h-80 bg-[url(/DummyIDP-Assertion.xml.png)] bg-cover" />
|
||||
}
|
||||
fade={["bottom"]}
|
||||
className="max-lg:rounded-t-4xl lg:col-span-3 lg:rounded-tl-4xl"
|
||||
/>
|
||||
<BentoCard
|
||||
tall
|
||||
eyebrow="Working SCIM Directory"
|
||||
title="Real SCIM HTTP Requests"
|
||||
description="DummyIDP sends you the same SCIM HTTP requests you'd get from your customer's IDP. Quickly verify your app's automatic user (de)provisioning logic."
|
||||
graphic={
|
||||
<div className="h-80 bg-[url(/SCIM-Request.http.png)] bg-cover" />
|
||||
}
|
||||
fade={["bottom"]}
|
||||
className="lg:col-span-3 lg:rounded-tr-4xl"
|
||||
/>
|
||||
<BentoCard
|
||||
eyebrow="Simple"
|
||||
title="No needless complexity"
|
||||
description={
|
||||
<span>
|
||||
No need to talk to sales or give us your email. Just{" "}
|
||||
<InlineCreateAppLink>
|
||||
<span className="underline underline-offset-2 decoration-gray-300">
|
||||
create an app
|
||||
</span>
|
||||
</InlineCreateAppLink>{" "}
|
||||
and get going.
|
||||
</span>
|
||||
}
|
||||
className="lg:col-span-2 lg:rounded-bl-4xl"
|
||||
/>
|
||||
<BentoCard
|
||||
eyebrow="Persistence"
|
||||
title="Keeps track of state"
|
||||
description="DummyIDP apps are long-lived. You can come back to your app any time."
|
||||
className="lg:col-span-2"
|
||||
/>
|
||||
<BentoCard
|
||||
eyebrow="FOSS"
|
||||
title="Free and open-source"
|
||||
description={
|
||||
<span>
|
||||
The DummyIDP.com service is free for anyone to use. You can
|
||||
self-host or{" "}
|
||||
<a
|
||||
className="underline underline-offset-2 decoration-gray-300"
|
||||
href="https://github.com/ssoready/dummyidp"
|
||||
>
|
||||
fork us on GitHub
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
}
|
||||
className="max-lg:rounded-b-4xl lg:col-span-2 lg:rounded-br-4xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BentoCard({
|
||||
className = "",
|
||||
eyebrow,
|
||||
title,
|
||||
description,
|
||||
graphic,
|
||||
fade = [],
|
||||
tall,
|
||||
}: {
|
||||
dark?: boolean;
|
||||
className?: string;
|
||||
eyebrow: React.ReactNode;
|
||||
title: React.ReactNode;
|
||||
description: React.ReactNode;
|
||||
graphic?: React.ReactNode;
|
||||
fade?: ("top" | "bottom")[];
|
||||
tall?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
className,
|
||||
"group relative flex flex-col overflow-hidden rounded-lg",
|
||||
"bg-white shadow-sm ring-1 ring-black/5",
|
||||
"data-[dark]:bg-gray-800 data-[dark]:ring-white/15",
|
||||
)}
|
||||
>
|
||||
<div className={clsx("relative shrink-0", tall ? "h-80" : "h-18")}>
|
||||
{graphic}
|
||||
{fade.includes("top") && (
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-white to-50% group-data-[dark]:from-gray-800 group-data-[dark]:from-[-25%]" />
|
||||
)}
|
||||
{fade.includes("bottom") && (
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-white to-50% group-data-[dark]:from-gray-800 group-data-[dark]:from-[-25%]" />
|
||||
)}
|
||||
</div>
|
||||
<div className="relative p-10">
|
||||
<h3 className="font-mono text-xs/5 font-semibold uppercase tracking-widest text-gray-500 data-[dark]:text-gray-400">
|
||||
{eyebrow}
|
||||
</h3>
|
||||
<p className="mt-1 text-2xl/8 font-medium tracking-tight text-gray-950 group-data-[dark]:text-white">
|
||||
{title}
|
||||
</p>
|
||||
<p className="mt-2 max-w-[600px] text-sm/6 text-gray-600 group-data-[dark]:text-gray-400">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,3 +23,15 @@ export default function CreateAppButton() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function InlineCreateAppLink({
|
||||
children,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<span className="cursor-pointer" onClick={() => createApp()}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ export function Footer() {
|
||||
</a>
|
||||
<a
|
||||
className="relative inline-flex items-center justify-center px-4 py-[calc(theme(spacing.2)-1px)] rounded-full border border-transparent bg-white/15 shadow-md ring-1 ring-[#D15052]/15 after:absolute after:inset-0 after:rounded-full after:shadow-[inset_0_0_2px_1px_#ffffff4d] whitespace-nowrap text-base font-medium text-gray-950 data-[disabled]:bg-white/15 hover:bg-white/20 disabled:opacity-40"
|
||||
href="https://ssoready.com/docs"
|
||||
href="https://ssoready.com/docs/dummyidp"
|
||||
>
|
||||
Read the docs
|
||||
</a>
|
||||
|
||||
@ -3,6 +3,7 @@ import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import wordmark from "@/wordmark.svg";
|
||||
import React from "react";
|
||||
import { InlineCreateAppLink } from "@/components/CreateAppButton";
|
||||
|
||||
const links = [
|
||||
{ href: "https://ssoready.com/docs/dummyidp", label: "Docs" },
|
||||
@ -23,6 +24,13 @@ function DesktopNav() {
|
||||
</Link>
|
||||
</PlusGridItem>
|
||||
))}
|
||||
<PlusGridItem className="relative flex">
|
||||
<InlineCreateAppLink>
|
||||
<div className="flex items-center px-4 py-3 text-base font-medium text-gray-950 bg-blend-multiply hover:bg-black/[2.5%]">
|
||||
Create a DummyIDP App
|
||||
</div>
|
||||
</InlineCreateAppLink>
|
||||
</PlusGridItem>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
36
src/components/ui/badge.tsx
Normal file
36
src/components/ui/badge.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-md border border-slate-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 dark:border-slate-800 dark:focus:ring-slate-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-slate-900 text-slate-50 shadow hover:bg-slate-900/80 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/80",
|
||||
secondary:
|
||||
"border-transparent bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80",
|
||||
destructive:
|
||||
"border-transparent bg-red-500 text-slate-50 shadow hover:bg-red-500/80 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/80",
|
||||
outline: "text-slate-950 dark:text-slate-50",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
Loading…
Reference in New Issue
Block a user