Template
1
0

feat: update auth setup

This commit is contained in:
2025-11-25 01:14:46 +01:00
parent 572d2f429a
commit a818f3135a
9 changed files with 20 additions and 142 deletions

View File

@@ -17,10 +17,6 @@ export class NavUserController extends Controller<{
} }
} }
authorize() {
zitadel.authorize();
}
signout() { signout() {
zitadel.signout(); zitadel.signout();
} }

View File

@@ -18,62 +18,11 @@ import { useController } from "@/libraries/controller.ts";
import { NavUserController } from "./nav-user.controller.ts"; import { NavUserController } from "./nav-user.controller.ts";
export function NavUser() { export function NavUser() {
const [{ user }, loading, { authorize, signout }] = useController(NavUserController); const [{ user }, loading, { signout }] = useController(NavUserController);
const { isMobile } = useSidebar(); const { isMobile } = useSidebar();
console.log({authorize})
if (loading === true || user === undefined) { if (loading === true || user === undefined) {
return ( return null;
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg grayscale">
<AvatarImage src="" alt="" />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">Guest</span>
<span className="text-muted-foreground truncate text-xs">guest@fixture.none</span>
</div>
<IconDotsVertical className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src="" alt="Guest" />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">Guest</span>
<span className="text-muted-foreground truncate text-xs">guest@fixture.none</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => authorize()}>
<IconLogout />
Sign in
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
} }
return ( return (
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>

View File

@@ -1,5 +1,6 @@
import { createRootRoute, createRoute, createRouter } from "@tanstack/react-router"; import { createRootRoute, createRoute, createRouter, redirect } from "@tanstack/react-router";
import { zitadel } from "./services/zitadel.ts";
import { AppView } from "./views/app.view.tsx"; import { AppView } from "./views/app.view.tsx";
import { CallbackView } from "./views/auth/callback.view.tsx"; import { CallbackView } from "./views/auth/callback.view.tsx";
import { LoginView } from "./views/auth/login.view.tsx"; import { LoginView } from "./views/auth/login.view.tsx";
@@ -22,6 +23,15 @@ const login = createRoute({
const app = createRoute({ const app = createRoute({
id: "app", id: "app",
getParentRoute: () => root, getParentRoute: () => root,
beforeLoad: async () => {
const user = await zitadel.userManager.getUser();
if (user === null) {
throw redirect({ to: "/login" });
}
if (user.expired === true) {
throw redirect({ to: "/login" });
}
},
component: AppView, component: AppView,
}); });

View File

@@ -2,7 +2,7 @@ import { createZitadelAuth, type ZitadelConfig } from "@zitadel/react";
const config: ZitadelConfig = { const config: ZitadelConfig = {
authority: "https://auth.valkyrjs.com", authority: "https://auth.valkyrjs.com",
client_id: "347982179092987909", client_id: "348172463709945862",
redirect_uri: "http://localhost:5173/callback", redirect_uri: "http://localhost:5173/callback",
post_logout_redirect_uri: "http://localhost:5173", post_logout_redirect_uri: "http://localhost:5173",
response_type: "code", response_type: "code",

View File

@@ -1,26 +0,0 @@
import { Controller } from "../libraries/controller.ts";
import { router } from "../router.tsx";
import { zitadel } from "../services/zitadel.ts";
export class AppController extends Controller<{
authenticated: boolean;
}> {
async onInit() {
return {
authenticated: await this.#getAuthenticatedState(),
};
}
async #getAuthenticatedState(): Promise<boolean> {
const user = await zitadel.userManager.getUser();
if (user === null) {
router.navigate({ to: "/login" });
return false;
}
return true;
}
signout() {
zitadel.signout();
}
}

View File

@@ -3,18 +3,8 @@ import { Outlet } from "@tanstack/react-router";
import { AppSidebar } from "@/components/app-sidebar.tsx"; import { AppSidebar } from "@/components/app-sidebar.tsx";
import { SiteHeader } from "@/components/site-header.tsx"; import { SiteHeader } from "@/components/site-header.tsx";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar.tsx"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar.tsx";
import { useController } from "@/libraries/controller.ts";
import { AppController } from "./app.controller.ts";
export function AppView() { export function AppView() {
const [{ authenticated }, loading] = useController(AppController);
if (loading === true) {
return <div>Loading ...</div>;
}
if (authenticated === false) {
return <div>Unauthenticated</div>;
}
return ( return (
<SidebarProvider <SidebarProvider
style={ style={

View File

@@ -1,24 +1,17 @@
import { GalleryVerticalEnd } from "lucide-react"; import { GalleryVerticalEnd } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldSeparator } from "@/components/ui/field"; import { Field, FieldDescription, FieldGroup, FieldSeparator } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { cn } from "@/libraries/utils"; import { cn } from "@/libraries/utils";
import { zitadel } from "@/services/zitadel.ts";
export function LoginForm({ export function LoginForm({ className, ...props }: React.ComponentProps<"div">) {
className,
passkey,
...props
}: { passkey: (email: string) => Promise<void> } & React.ComponentProps<"div">) {
return ( return (
<div className={cn("flex flex-col gap-6", className)} {...props}> <div className={cn("flex flex-col gap-6", className)} {...props}>
<form <form
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
const email = e.currentTarget.elements.namedItem("email"); zitadel.authorize();
if (email instanceof HTMLInputElement) {
passkey(email.value);
}
}} }}
> >
<FieldGroup> <FieldGroup>
@@ -35,11 +28,7 @@ export function LoginForm({
</FieldDescription> </FieldDescription>
</div> </div>
<Field> <Field>
<FieldLabel htmlFor="email">Email</FieldLabel> <Button type="submit">Login with Zitadel</Button>
<Input id="email" type="email" placeholder="m@example.com" required />
</Field>
<Field>
<Button type="submit">Login</Button>
</Field> </Field>
<FieldSeparator>Or</FieldSeparator> <FieldSeparator>Or</FieldSeparator>
<Field className="grid gap-4 sm:grid-cols-2"> <Field className="grid gap-4 sm:grid-cols-2">

View File

@@ -1,27 +0,0 @@
import { Controller } from "../../libraries/controller.ts";
export class LoginController extends Controller {
async passkey(email: string) {
const result = await fetch("https://auth.valkyrjs.com/v2/sessions", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
checks: {
user: {
loginName: email,
},
},
challenges: {
webAuthN: {
domain: "auth.valkyrjs.com",
userVerificationRequirement: "USER_VERIFICATION_REQUIREMENT_REQUIRED",
},
},
}),
});
console.log(await result.text());
}
}

View File

@@ -1,13 +1,10 @@
import { useController } from "../../libraries/controller.ts";
import { LoginForm } from "./components/login-form.tsx"; import { LoginForm } from "./components/login-form.tsx";
import { LoginController } from "./login.controller.ts";
export function LoginView() { export function LoginView() {
const [, , { passkey }] = useController(LoginController);
return ( return (
<div className="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10"> <div className="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div className="w-full max-w-sm"> <div className="w-full max-w-sm">
<LoginForm passkey={passkey} /> <LoginForm />
</div> </div>
</div> </div>
); );