diff --git a/apps/cm/.env.example b/apps/cm/.env.example index 5d9747b57..0da6c0f8a 100644 --- a/apps/cm/.env.example +++ b/apps/cm/.env.example @@ -1,10 +1,10 @@ # apps/cs/src/shared/config/env.ts 참고 +NEXT_PUBLIC_AUTH_LOGIN_REDIRECT_URL=/auth/login NEXT_PUBLIC_PORT=3001 NEXT_PUBLIC_PROXY_HOST=http://localhost:3001 NEXT_PUBLIC_SITE_ID=CM NEXT_PUBLIC_SUB_DOMAIN=http://localhost:13000 -# NEXT_PUBLIC_SERVER_API_URL=http://localhost:8000 -NEXT_PUBLIC_SERVER_API_URL=http://dev.onasys.co.kr:8000 +SERVER_API_URL=http://dev.onasys.co.kr:8000 NEXT_PUBLIC_LINK_BACKEND_API_HOST=http://rsone.iptime.org:10014 NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 diff --git a/apps/cm/app/(with-base-layout)/layout.tsx b/apps/cm/app/(with-base-layout)/layout.tsx index 481d8f30c..732769718 100644 --- a/apps/cm/app/(with-base-layout)/layout.tsx +++ b/apps/cm/app/(with-base-layout)/layout.tsx @@ -4,6 +4,7 @@ import { usePathname, useRouter } from "next/navigation"; import { useQueryClient } from "@tanstack/react-query"; import { NoLNBLayout } from "@common/components"; import type { MenuItem } from "@/entities/menu"; +import { authService } from "@/entities/auth"; import { useMenuStore } from "@/shared/lib"; import { PageLayout } from "@/shared/ui/page-layout"; @@ -26,6 +27,7 @@ export default function WithBaseLayout({ return ( diff --git a/apps/cm/middleware.ts b/apps/cm/middleware.ts index 241df1e74..bae4e6579 100644 --- a/apps/cm/middleware.ts +++ b/apps/cm/middleware.ts @@ -8,20 +8,16 @@ import { SITE_ID, } from "./src/shared/config"; -const serverApiUrl = - process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:3000"; +const serverApiUrl = process.env.SERVER_API_URL; - const serverApiUrl2 = - process.env.NEXT_PUBLIC_SERVER_API_URL2 ?? serverApiUrl; +const serverApiUrl2 = process.env.NEXT_PUBLIC_SERVER_API_URL2 ?? serverApiUrl; - const serverApiUrl3 = - process.env.NEXT_PUBLIC_SERVER_API_URL3 ?? serverApiUrl; +const serverApiUrl3 = process.env.NEXT_PUBLIC_SERVER_API_URL3 ?? serverApiUrl; - // const serverApiUrl4 = - // process.env.NEXT_PUBLIC_SERVER_API_URL4 ?? serverApiUrl; +// const serverApiUrl4 = +// process.env.NEXT_PUBLIC_SERVER_API_URL4 ?? serverApiUrl; - const serverApiUrl5 = - process.env.NEXT_PUBLIC_SERVER_API_URL5 ?? serverApiUrl; +const serverApiUrl5 = process.env.NEXT_PUBLIC_SERVER_API_URL5 ?? serverApiUrl; export function middleware(req: NextRequest) { const pathname = req.nextUrl.pathname; @@ -51,7 +47,6 @@ export function middleware(req: NextRequest) { }); } - if (pathname.startsWith("/server/sewerage-service")) { const requestHeaders = new Headers(req.headers); const token = req.cookies.get(CLAIM_NAME)?.value; @@ -173,7 +168,6 @@ export function middleware(req: NextRequest) { return NextResponse.rewrite(new URL(newUrl)); } - if (pathname === "/") { return NextResponse.redirect(new URL("/dashboard", req.url)); } @@ -184,11 +178,13 @@ export function middleware(req: NextRequest) { } export const config = { - matcher: ["/server/:path*", - "/attach-service/:path*", - "/user-service/:path*", - "/sewerage-service/:path*", - "/reuse-service/:path*", - "/operation-service/:path*", - "/"], + matcher: [ + "/server/:path*", + "/attach-service/:path*", + "/user-service/:path*", + "/sewerage-service/:path*", + "/reuse-service/:path*", + "/operation-service/:path*", + "/", + ], }; diff --git a/apps/cm/package.json b/apps/cm/package.json index 5eaca0435..ba980d55c 100644 --- a/apps/cm/package.json +++ b/apps/cm/package.json @@ -3,13 +3,14 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev -p 3001", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@common/assets": "workspace:*", + "@common/auth": "workspace:*", "@common/components": "workspace:*", "@common/config": "workspace:*", "@dnd-kit/core": "^6.3.1", diff --git a/apps/cm/src/entities/auth/api/auth-service.ts b/apps/cm/src/entities/auth/api/auth-service.ts new file mode 100644 index 000000000..a8fa70622 --- /dev/null +++ b/apps/cm/src/entities/auth/api/auth-service.ts @@ -0,0 +1,121 @@ +import { + ACCESS_TOKEN, + AUTH_USER_ID, + CLAIM_NAME, + ENDPOINT, +} from "@/shared/config"; +import { + type AuthPayload, + type AuthService, + getRemainingMs, +} from "@common/auth"; + +import { + clearRefreshTimer, + removeToken, + setToken, + setupRefreshTimer, + tokenManager, +} from "../lib/token-manager"; + +function resolveLoginError(message: string | undefined): string { + switch (message) { + case "Invalid Credentials": + return "아이디와 비밀번호가 일치하지 않습니다."; + case "User not found": + return "존재하지 않는 아이디입니다."; + case "User is not active": + return "활성화되지 않은 계정입니다."; + case "User is not verified": + return "이메일 인증이 필요합니다."; + default: + return message ?? "로그인에 실패했습니다."; + } +} + +function completeSession(accessToken: string, tokenId: string): void { + setToken(CLAIM_NAME, accessToken); + setToken(AUTH_USER_ID, tokenId); + + const remainingMs = getRemainingMs(accessToken); + if (remainingMs > 0) { + setupRefreshTimer(silentRefresh, remainingMs); + } +} + +async function login(payload: AuthPayload): Promise { + const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(resolveLoginError(data?.message)); + } + + const accessToken = data[ACCESS_TOKEN] as string | undefined; + const tokenId = data[AUTH_USER_ID] as string | undefined; + + if (!accessToken || !tokenId) { + throw new Error("인증 토큰을 받지 못했습니다."); + } + + completeSession(accessToken, tokenId); +} + +async function silentRefresh(): Promise { + console.log(`[silentRefresh] Called at ${new Date().toISOString()}`); + try { + const response = await fetch( + `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, + { method: "PUT" }, + ); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data?.message ?? "토큰 갱신에 실패했습니다."); + } + + const accessToken = data[ACCESS_TOKEN] as string | undefined; + const tokenId = data[AUTH_USER_ID] as string | undefined; + + if (!accessToken || !tokenId) { + throw new Error("갱신된 인증 토큰을 받지 못했습니다."); + } + + completeSession(accessToken, tokenId); + } catch (error) { + console.error( + `[silentRefresh] Failed at ${new Date().toISOString()}`, + error, + ); + tokenManager.clearAuthToken(); + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); + } + } +} + +async function logout(): Promise { + tokenManager.clearAuthToken(); + removeToken("site-id-key"); + removeToken("site-theme-key"); + + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("auth:logout")); + } +} + +export const authService: AuthService & { + completeSession: typeof completeSession; +} = { + login, + silentRefresh, + declineSessionExtend: clearRefreshTimer, + logout, + completeSession, +}; diff --git a/apps/cm/src/entities/auth/api/login-service.ts b/apps/cm/src/entities/auth/api/login-service.ts deleted file mode 100644 index c1968fe36..000000000 --- a/apps/cm/src/entities/auth/api/login-service.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { AxiosError } from "axios"; -import { - ENDPOINT, - JWT_EXPIRATION_TIME, - ACCESS_TOKEN, - AUTH_USER_ID, - CLAIM_NAME, -} from "@/shared/config"; -import type { LoginService, ILogin } from "../model/login-interface"; -import { tokenManager } from "../lib/token-manager"; - -export class LoginApiService implements LoginService { - private static instance: LoginApiService; - - private constructor() {} - - public static getInstance(): LoginApiService { - if (!LoginApiService.instance) { - LoginApiService.instance = new LoginApiService(); - } - return LoginApiService.instance; - } - - public async login(payload: ILogin): Promise { - try { - const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { - method: "POST", - body: JSON.stringify({ - userId: payload.userId, - password: payload.password, - }), - }); - - const data = await response.json(); - const accessToken = data[ACCESS_TOKEN]; - const tokenId = data[AUTH_USER_ID]; - - if (!accessToken || !tokenId) { - throw new Error("Missing required authentication tokens"); - } - - tokenManager.setToken(CLAIM_NAME, accessToken); - tokenManager.setToken(AUTH_USER_ID, tokenId); - tokenManager.setupRefreshTimer( - () => this.silentRefresh(), - JWT_EXPIRATION_TIME, - ); - } catch (error) { - if (error instanceof AxiosError) { - throw new Error(`Login failed: ${error.response?.data?.message}`); - } - throw new Error(`Login failed: ${error}`); - } - } - - public async silentRefresh(): Promise { - try { - const response = await fetch( - `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, - { method: "PUT" }, - ); - - const data = await response.json(); - const accessToken = data[ACCESS_TOKEN]; - const tokenId = data[AUTH_USER_ID]; - - if (!accessToken || !tokenId) { - throw new Error("Missing required authentication tokens"); - } - - tokenManager.setToken(CLAIM_NAME, accessToken); - tokenManager.setToken(AUTH_USER_ID, tokenId); - } catch (error) { - tokenManager.clearAuthToken(); - if (error instanceof AxiosError) { - throw new Error( - `Silent refresh failed: ${error.response?.data?.message}`, - ); - } - throw new Error(`Silent refresh failed: ${error}`); - } - } -} - -export const LoginApi = LoginApiService.getInstance(); diff --git a/apps/cm/src/entities/auth/index.ts b/apps/cm/src/entities/auth/index.ts index 97e142225..7f0d0081a 100644 --- a/apps/cm/src/entities/auth/index.ts +++ b/apps/cm/src/entities/auth/index.ts @@ -1,2 +1,3 @@ -export { LoginApi } from "./api/login-service"; +export { authService } from "./api/auth-service"; export { AuthWrapper } from "./lib/auth-wrapper"; +export { tokenManager } from "./lib/token-manager"; diff --git a/apps/cm/src/entities/auth/lib/auth-wrapper.tsx b/apps/cm/src/entities/auth/lib/auth-wrapper.tsx index 5366696f7..d1a2a48c3 100644 --- a/apps/cm/src/entities/auth/lib/auth-wrapper.tsx +++ b/apps/cm/src/entities/auth/lib/auth-wrapper.tsx @@ -2,44 +2,80 @@ import { useEffect } from "react"; import { useRouter } from "next/navigation"; + +import { isAxiosError } from "axios"; + import { client } from "@/shared/api"; +import { + AUTH_LOGIN_REDIRECT_URL, + AUTH_USER_ID, + CLAIM_NAME, + ENDPOINT, +} from "@/shared/config"; +import { useUserStore } from "@/shared/lib"; import type { IUserInfo } from "@/shared/store"; -import { getCookie, useUserStore } from "@/shared/lib"; +import { getRemainingMs } from "@common/auth"; import { isAtLoginRedirectUrl } from "@common/network"; -import { ENDPOINT, AUTH_USER_ID } from "@/shared/config"; + +import { authService } from "../api/auth-service"; + +import { getToken, setupRefreshTimer } from "./token-manager"; const AuthWrapper = ({ children }: { children: React.ReactNode }) => { const router = useRouter(); - const { userInfo, setUserInfo } = useUserStore((state) => state); - - async function authCheck() { - if (isAtLoginRedirectUrl("/auth/login")) { - return; - } - - const userId = getCookie(AUTH_USER_ID); - if (!userId) { - throw new Error("Missing required authentication tokens"); - } - - const response = await client.get( - `${ENDPOINT.USER_SERVICE.USERS}/info/me`, - ); - if (!userInfo) setUserInfo(response.data); - } + const userInfo = useUserStore((state) => state.userInfo); + const setUserInfo = useUserStore((state) => state.setUserInfo); useEffect(() => { - (async () => { - try { - await authCheck(); - } catch (error) { - console.error(error); - setUserInfo(null); - if (!isAtLoginRedirectUrl("/auth/login")) { - router.push("/auth/login"); - } + const token = getToken(CLAIM_NAME); + if (token) { + const remainingMs = getRemainingMs(token); + if (remainingMs > 0) { + console.log( + `[AuthWrapper] Refresh timer set: ${remainingMs}ms (${remainingMs / 1000}s)`, + ); + setupRefreshTimer(authService.silentRefresh, remainingMs); } - })(); + } + + if (isAtLoginRedirectUrl(AUTH_LOGIN_REDIRECT_URL)) return; + + async function authCheck() { + const userId = getToken(AUTH_USER_ID); + if (!userId) { + router.replace(AUTH_LOGIN_REDIRECT_URL); + return; + } + + if (userInfo) return; + + const response = await client.get( + `${ENDPOINT.USER_SERVICE.USERS}/info/me`, + ); + setUserInfo(response.data); + } + + authCheck().catch((error) => { + if (isAxiosError(error) && error.response?.status === 401) return; + + console.error(error); + setUserInfo(null); + if (!isAtLoginRedirectUrl(AUTH_LOGIN_REDIRECT_URL)) { + router.replace(AUTH_LOGIN_REDIRECT_URL); + } + }); + }, []); + + useEffect(() => { + function handleAuthLogout() { + setUserInfo(null); + if (!isAtLoginRedirectUrl(AUTH_LOGIN_REDIRECT_URL)) { + router.replace(AUTH_LOGIN_REDIRECT_URL); + } + } + + window.addEventListener("auth:logout", handleAuthLogout); + return () => window.removeEventListener("auth:logout", handleAuthLogout); }, []); return <>{children}; diff --git a/apps/cm/src/entities/auth/lib/token-manager.ts b/apps/cm/src/entities/auth/lib/token-manager.ts index 8fcf21984..78d24bd5e 100644 --- a/apps/cm/src/entities/auth/lib/token-manager.ts +++ b/apps/cm/src/entities/auth/lib/token-manager.ts @@ -1,84 +1,15 @@ -import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, REFRESH_TOKEN } from "@/shared/config"; -import { getCookie, setCookie, removeCookie } from "@/shared/lib"; +import { authConfig } from "@/shared/config/auth"; +import { createTokenManager } from "@common/auth"; -export class TokenManager { - private static instance: TokenManager; - private refreshTimer: NodeJS.Timeout | null = null; +export const tokenManager = createTokenManager(authConfig); - private constructor() {} - - public static getInstance(): TokenManager { - if (!TokenManager.instance) { - TokenManager.instance = new TokenManager(); - } - return TokenManager.instance; - } - - public getToken(key: string): string | undefined { - return getCookie(key); - } - - public setToken(key: string, value: string): void { - // 환경에 따른 도메인 설정 - const domain = this.getCookieDomain(); - - setCookie(key, value, { - secure: window.location.protocol === "https:", - sameSite: "lax", - domain: domain, - path: "/", // 모든 경로에서 접근 가능 - }); - } - - private getCookieDomain(): string | undefined { - const hostname = window.location.hostname; - - // 개발 환경에서는 localhost 도메인 사용 - if (hostname === "localhost" || hostname === "127.0.0.1") { - return undefined; // localhost에서는 도메인 설정하지 않음 - } - - // 운영 환경에서는 서브도메인을 포함한 최상위 도메인 사용 - if (hostname.includes("hasuon.or.kr")) { - return ".hasuon.or.kr"; - } - - // 기타 환경에서는 현재 호스트명 사용 - return hostname; - } - - public removeToken(key: string): void { - const domain = this.getCookieDomain(); - removeCookie(key, { domain, path: "/" }); - } - - public setupRefreshTimer( - callback: () => Promise, - expirationTime: number, - ): void { - if (this.refreshTimer) { - clearTimeout(this.refreshTimer); - } - - this.refreshTimer = setTimeout(() => { - callback().catch((error) => { - console.error("Silent refresh failed:", error); - this.clearAuthToken(); - }); - }, expirationTime - 60000); - } - - public clearAuthToken(): void { - if (this.refreshTimer) { - clearTimeout(this.refreshTimer); - this.refreshTimer = null; - } - - this.removeToken(CLAIM_NAME); - this.removeToken(AUTH_USER_ID); - this.removeToken(REFRESH_TOKEN); - this.removeToken(ACCESS_TOKEN); - } -} - -export const tokenManager = TokenManager.getInstance(); +export const { + getToken, + setToken, + removeToken, + setupRefreshTimer, + clearRefreshTimer, + getAccessToken, + getRefreshToken, + getUserId, +} = tokenManager; diff --git a/apps/cm/src/entities/auth/model/login-interface.ts b/apps/cm/src/entities/auth/model/login-interface.ts deleted file mode 100644 index 5aaf53044..000000000 --- a/apps/cm/src/entities/auth/model/login-interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface ILogin { - userId: string; - password: string; -} - -export interface LoginService { - login(payload: ILogin): Promise; - silentRefresh(): Promise; -} diff --git a/apps/cm/src/pages/auth/login/ui/OtpModal.tsx b/apps/cm/src/pages/auth/login/ui/OtpModal.tsx index 7c6aeb9af..39a343850 100644 --- a/apps/cm/src/pages/auth/login/ui/OtpModal.tsx +++ b/apps/cm/src/pages/auth/login/ui/OtpModal.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { ACCESS_TOKEN, AUTH_USER_ID, REFRESH_TOKEN, CLAIM_NAME } from "@/shared/config"; -import { tokenManager } from "@/entities/auth/lib/token-manager"; +import { ACCESS_TOKEN, AUTH_USER_ID, ENDPOINT } from "@/shared/config"; +import { authService } from "@/entities/auth"; import { Button, Dialog, @@ -33,7 +33,7 @@ export default function OtpModal({ onClose, onSuccess, open, onOpenChange }: Otp } // Proxy를 통해 OTP 인증 처리 - const response = await fetch(`/api/proxy/user-service/api/v1/auth/2fa/verify`, { + const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.OTP_VERIFY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ @@ -57,37 +57,19 @@ export default function OtpModal({ onClose, onSuccess, open, onOpenChange }: Otp console.log("Response data keys:", Object.keys(data)); console.log("Full response data:", JSON.stringify(data, null, 2)); - // 2FA 응답에서는 access-token만 있고 token-id는 없을 수 있음 - const accessToken = data[ACCESS_TOKEN] || data.accessToken || data.access_token || data.token || data['ss-token']; - - console.log("Extracted accessToken:", !!accessToken); - console.log("Token value - accessToken:", accessToken?.substring(0, 20) + "..."); - - // access-token이 있는 경우 저장 - if (accessToken) { - console.log("Saving access token from OTP verification"); - tokenManager.setToken(CLAIM_NAME, accessToken); // Authorization으로 저장 - - // 리프레시 토큰도 확인 - const refreshToken = data[REFRESH_TOKEN] || data.refreshToken || data.refresh_token; - if (refreshToken) { - console.log("Saving refresh token"); - tokenManager.setToken(REFRESH_TOKEN, refreshToken); - } - - // userId는 원래 로그인 응답에서 가져온 값 사용 - const originalUserId = data.userId; - if (originalUserId) { - console.log("Saving userId from original login response"); - tokenManager.setToken(AUTH_USER_ID, originalUserId); - } - - console.log("All tokens saved successfully"); - } else { - console.log("No access token in OTP response"); - console.log("Available data keys:", Object.keys(data)); - console.log("Data values:", Object.entries(data).map(([k, v]) => `${k}: ${typeof v === 'string' ? v.substring(0, 20) + '...' : v}`)); + const accessToken = + data[ACCESS_TOKEN] || + data.accessToken || + data.access_token || + data.token || + data["ss-token"]; + const tokenId = data[AUTH_USER_ID] || data.userId; + + if (!accessToken || !tokenId) { + throw new Error("인증 토큰을 받지 못했습니다."); } + + authService.completeSession(accessToken, tokenId); onSuccess(); } catch (error: unknown) { diff --git a/apps/cm/src/pages/auth/login/ui/login-page.tsx b/apps/cm/src/pages/auth/login/ui/login-page.tsx index f7f613086..d01bc4867 100644 --- a/apps/cm/src/pages/auth/login/ui/login-page.tsx +++ b/apps/cm/src/pages/auth/login/ui/login-page.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from "react"; import Image from "next/image"; import { images } from "@common/assets"; -import { LoginApi } from "@/entities/auth"; +import { authService } from "@/entities/auth"; import { ENDPOINT } from "@/shared/config"; import OtpModal from "./OtpModal"; import TwoFAModal from "./TwoFAModal"; @@ -96,7 +96,7 @@ function LoginPage() { } else { // 기본 로그인 - proxy를 통해 처리 console.log("Starting basic login..."); - await LoginApi.login({ userId, password }); + await authService.login({ userId, password }); window.location.href = "/dashboard"; } } catch (error: unknown) { diff --git a/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx b/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx index 9d5e9179a..fe8a7f6c8 100644 --- a/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx +++ b/apps/cm/src/pages/dashboard/ui/dashboard-page.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import Image from "next/image"; -import { tokenManager } from "@/entities/auth/lib/token-manager"; +import { authService, tokenManager } from "@/entities/auth"; import { client } from "@/shared/api"; import { ENDPOINT } from "@/shared/config"; import { KoreaBasinMap } from "../lib/korea-basin-map"; @@ -120,13 +120,11 @@ function DashboardPage() { fetchUserInfo(); }, [router]); - const handleLogout = () => { + const handleLogout = async () => { console.log("Logging out..."); - // localStorage 정리 localStorage.removeItem("tempToken"); - // tokenManager를 통해 쿠키 정리 - tokenManager.clearAuthToken(); - router.push("/auth/logout"); + await authService.logout(); + router.push("/auth/login"); }; const handleChangePassword = () => { diff --git a/apps/cm/src/shared/config/api.ts b/apps/cm/src/shared/config/api.ts index 3cdb96d0c..26d5b8e95 100644 --- a/apps/cm/src/shared/config/api.ts +++ b/apps/cm/src/shared/config/api.ts @@ -97,4 +97,3 @@ export const ENDPOINT = { }; -export const JWT_EXPIRATION_TIME = 1800000; diff --git a/apps/cm/src/shared/config/auth.ts b/apps/cm/src/shared/config/auth.ts new file mode 100644 index 000000000..f743b9f61 --- /dev/null +++ b/apps/cm/src/shared/config/auth.ts @@ -0,0 +1,17 @@ +import type { AuthConfig } from "@common/auth"; + +import { + ACCESS_TOKEN, + AUTH_USER_ID, + CLAIM_NAME, + COOKIE_DOMAIN, + REFRESH_TOKEN, +} from "./env"; + +export const authConfig: AuthConfig = { + authUserId: AUTH_USER_ID, + claimName: CLAIM_NAME, + cookieDomain: COOKIE_DOMAIN, + accessToken: ACCESS_TOKEN, + refreshToken: REFRESH_TOKEN, +}; diff --git a/apps/cm/src/shared/config/env.ts b/apps/cm/src/shared/config/env.ts index c6373724f..5e1392b4d 100644 --- a/apps/cm/src/shared/config/env.ts +++ b/apps/cm/src/shared/config/env.ts @@ -2,6 +2,8 @@ export const PORT = process.env.NEXT_PUBLIC_PORT || "3000"; export const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || `http://localhost:${PORT}`; export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL; +export const AUTH_LOGIN_REDIRECT_URL = + process.env.NEXT_PUBLIC_AUTH_LOGIN_REDIRECT_URL || "/auth/login"; export const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "CS"; export const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization"; export const ACCESS_TOKEN = @@ -9,3 +11,4 @@ export const ACCESS_TOKEN = export const REFRESH_TOKEN = process.env.NEXT_PUBLIC_REFRESH_TOKEN || "refresh-token"; export const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id"; +export const COOKIE_DOMAIN = process.env.NEXT_PUBLIC_COOKIE_DOMAIN; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d914bd2bc..bdd4c4d8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,10 +104,10 @@ importers: devDependencies: eslint: specifier: ^9.0.0 - version: 9.39.4(jiti@1.21.7) + version: 9.39.4(jiti@2.6.1) eslint-config-next: specifier: 15.3.8 - version: 15.3.8(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3) + version: 15.3.8(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) postcss: specifier: ^8.4.49 version: 8.5.8 @@ -235,6 +235,9 @@ importers: '@common/assets': specifier: workspace:* version: link:../../packages/common/assets + '@common/auth': + specifier: workspace:* + version: link:../../packages/common/auth '@common/components': specifier: workspace:* version: link:../../packages/common/components @@ -364,10 +367,10 @@ importers: version: 5.28.5 eslint: specifier: ^9.0.0 - version: 9.39.4(jiti@2.6.1) + version: 9.39.4(jiti@1.21.7) eslint-config-next: specifier: 15.3.8 - version: 15.3.8(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + version: 15.3.8(eslint@9.39.4(jiti@1.21.7))(typescript@5.9.3) postcss: specifier: ^8.4.49 version: 8.5.8