From 2f1caa7b03804346f12214f1ca330881804b9764 Mon Sep 17 00:00:00 2001 From: kyy Date: Wed, 11 Feb 2026 17:31:36 +0900 Subject: [PATCH] =?UTF-8?q?OIDC=20=EC=9D=B8=EC=A6=9D=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8/=EC=BD=9C?= =?UTF-8?q?=EB=B0=B1=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devfront/src/app/routes.tsx | 30 ++++++++++++++----- .../src/features/auth/AuthCallbackPage.tsx | 19 ++++++++++++ devfront/src/features/auth/AuthGuard.tsx | 20 +++++++++++++ devfront/src/features/auth/LoginPage.tsx | 23 ++++++++++++++ devfront/src/features/auth/authApi.ts | 22 ++++++++++++++ devfront/src/lib/auth.ts | 20 +++++++++++++ devfront/src/main.tsx | 12 +++++--- 7 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 devfront/src/features/auth/AuthCallbackPage.tsx create mode 100644 devfront/src/features/auth/AuthGuard.tsx create mode 100644 devfront/src/features/auth/LoginPage.tsx create mode 100644 devfront/src/features/auth/authApi.ts create mode 100644 devfront/src/lib/auth.ts diff --git a/devfront/src/app/routes.tsx b/devfront/src/app/routes.tsx index 0bbba406..7da4c279 100644 --- a/devfront/src/app/routes.tsx +++ b/devfront/src/app/routes.tsx @@ -1,5 +1,8 @@ import { Navigate, createBrowserRouter } from "react-router-dom"; import AppLayout from "../components/layout/AppLayout"; +import AuthCallbackPage from "../features/auth/AuthCallbackPage"; +import AuthGuard from "../features/auth/AuthGuard"; +import LoginPage from "../features/auth/LoginPage"; import ClientConsentsPage from "../features/clients/ClientConsentsPage"; import ClientDetailsPage from "../features/clients/ClientDetailsPage"; import ClientGeneralPage from "../features/clients/ClientGeneralPage"; @@ -7,16 +10,29 @@ import ClientsPage from "../features/clients/ClientsPage"; export const router = createBrowserRouter( [ + { + path: "/login", + element: , + }, + { + path: "/callback", + element: , + }, { path: "/", - element: , + element: , children: [ - { index: true, element: }, - { path: "clients", element: }, - { path: "clients/new", element: }, - { path: "clients/:id", element: }, - { path: "clients/:id/consents", element: }, - { path: "clients/:id/settings", element: }, + { + element: , + children: [ + { index: true, element: }, + { path: "clients", element: }, + { path: "clients/new", element: }, + { path: "clients/:id", element: }, + { path: "clients/:id/consents", element: }, + { path: "clients/:id/settings", element: }, + ], + }, ], }, ], diff --git a/devfront/src/features/auth/AuthCallbackPage.tsx b/devfront/src/features/auth/AuthCallbackPage.tsx new file mode 100644 index 00000000..638a7924 --- /dev/null +++ b/devfront/src/features/auth/AuthCallbackPage.tsx @@ -0,0 +1,19 @@ +import { useEffect } from "react"; +import { useAuth } from "react-oidc-context"; +import { useNavigate } from "react-router-dom"; + +export default function AuthCallbackPage() { + const auth = useAuth(); + const navigate = useNavigate(); + + useEffect(() => { + if (auth.isAuthenticated) { + navigate("/", { replace: true }); + } else if (auth.error) { + console.error("Auth Error:", auth.error); + navigate("/login", { replace: true }); + } + }, [auth.isAuthenticated, auth.error, navigate]); + + return
Loading Auth...
; +} diff --git a/devfront/src/features/auth/AuthGuard.tsx b/devfront/src/features/auth/AuthGuard.tsx new file mode 100644 index 00000000..50abe838 --- /dev/null +++ b/devfront/src/features/auth/AuthGuard.tsx @@ -0,0 +1,20 @@ +import { useAuth } from "react-oidc-context"; +import { Navigate, Outlet } from "react-router-dom"; + +export default function AuthGuard() { + const auth = useAuth(); + + if (auth.isLoading) { + return
Loading...
; + } + + if (auth.error) { + return
Auth Error: {auth.error.message}
; + } + + if (!auth.isAuthenticated) { + return ; + } + + return ; +} diff --git a/devfront/src/features/auth/LoginPage.tsx b/devfront/src/features/auth/LoginPage.tsx new file mode 100644 index 00000000..212448ee --- /dev/null +++ b/devfront/src/features/auth/LoginPage.tsx @@ -0,0 +1,23 @@ +import { useAuth } from "react-oidc-context"; + +export default function LoginPage() { + const auth = useAuth(); + + const handleLogin = () => { + auth.signinRedirect(); + }; + + return ( +
+
+

DevFront Login

+ +
+
+ ); +} diff --git a/devfront/src/features/auth/authApi.ts b/devfront/src/features/auth/authApi.ts new file mode 100644 index 00000000..9fa27955 --- /dev/null +++ b/devfront/src/features/auth/authApi.ts @@ -0,0 +1,22 @@ +import apiClient from "../../lib/apiClient"; + +export interface Tenant { + id: string; + name: string; + slug: string; +} + +export interface UserProfile { + id: string; + email: string; + name: string; + role: string; + companyCode?: string; + tenantId?: string; + tenant?: Tenant; +} + +export async function fetchMe() { + const { data } = await apiClient.get("/user/me"); + return data; +} diff --git a/devfront/src/lib/auth.ts b/devfront/src/lib/auth.ts new file mode 100644 index 00000000..c9582c9c --- /dev/null +++ b/devfront/src/lib/auth.ts @@ -0,0 +1,20 @@ +import { UserManager, WebStorageStateStore } from "oidc-client-ts"; +import type { AuthProviderProps } from "react-oidc-context"; + +export const oidcConfig: AuthProviderProps = { + authority: import.meta.env.VITE_OIDC_AUTHORITY || "http://localhost:3000/api/v1/auth/oidc", // Backend Proxy URL + client_id: import.meta.env.VITE_OIDC_CLIENT_ID || "devfront-client", + redirect_uri: `${window.location.origin}/callback`, + response_type: "code", + scope: "openid offline_access profile email", // offline_access for refresh token + post_logout_redirect_uri: window.location.origin, + userStore: new WebStorageStateStore({ store: window.localStorage }), + automaticSilentRenew: true, +}; + +export const userManager = new UserManager({ + ...oidcConfig, + authority: oidcConfig.authority || "", + client_id: oidcConfig.client_id || "", + redirect_uri: oidcConfig.redirect_uri || "", +}); diff --git a/devfront/src/main.tsx b/devfront/src/main.tsx index e311b5b9..b718dd50 100644 --- a/devfront/src/main.tsx +++ b/devfront/src/main.tsx @@ -1,11 +1,13 @@ import { QueryClientProvider } from "@tanstack/react-query"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; +import { AuthProvider } from "react-oidc-context"; import { RouterProvider } from "react-router-dom"; import { queryClient } from "./app/queryClient"; import { router } from "./app/routes"; import { Toaster } from "./components/ui/toaster"; import "./index.css"; +import { oidcConfig } from "./lib/auth"; const rootElement = document.getElementById("root"); @@ -15,9 +17,11 @@ if (!rootElement) { createRoot(rootElement).render( - - - - + + + + + + , );