feat: update auth setup
This commit is contained in:
@@ -17,10 +17,6 @@ export class NavUserController extends Controller<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authorize() {
|
|
||||||
zitadel.authorize();
|
|
||||||
}
|
|
||||||
|
|
||||||
signout() {
|
signout() {
|
||||||
zitadel.signout();
|
zitadel.signout();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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={
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user