diff --git a/.env b/.env
index 419107808db520055938c6abe10f00e8d77e20a7..d4d6490a51e02a07bf4d5aaed3a6d31a2cbcfae3 100644
--- a/.env
+++ b/.env
@@ -2,4 +2,10 @@
 DAY1_AUTH_HOST_API=http://localhost:8000
 
 # ASSETS
-DAY1_AUTH_ASSETS_API=
\ No newline at end of file
+DAY1_AUTH_ASSETS_API=
+
+# HOSTNAME
+DAY1_AUTH_HOSTNAME=locahost
+
+# PUBLIC_KEY_PATH
+DAY1_AUTH_PUBLIC_KEY_PATH=public.pem
diff --git a/.gitignore b/.gitignore
index aeb2efc94999298cf7c6c506c1316a74f79267a1..8e1c2b8df0b136e704c193c0128a7a83bbf2e475 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,6 @@
 
 # misc
 .DS_Store
-*.pem
 
 # debug
 npm-debug.log*
diff --git a/package.json b/package.json
index fdb1da828d1e071b617a18f6bae56db2608bb6ae..e0f0698dc41227b60cb4a551183c13d7b1600c0e 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,10 @@
     "@mui/lab": "^5.0.0-alpha.163",
     "@mui/material": "^5.14.20",
     "@hookform/resolvers": "^3.3.4",
+    "axios": "^1.6.7",
     "framer-motion": "^11.0.3",
+    "jose": "^5.2.2",
+    "js-cookie": "^3.0.5",
     "mapbox-gl": "^3.1.2",
     "next": "14.1.0",
     "notistack": "^3.0.1",
@@ -33,6 +36,7 @@
   },
   "devDependencies": {
     "typescript": "^5",
+    "@types/js-cookie": "^3.0.6",
     "@types/node": "^20",
     "@types/nprogress": "^0.2.3",
     "@types/react": "^18",
diff --git a/public.pem b/public.pem
new file mode 100644
index 0000000000000000000000000000000000000000..f0082d0c84a147953f4b8ecd8df35be0d0fad703
--- /dev/null
+++ b/public.pem
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw1w5JbiWaLNey50rnXJl
+GDik50s7ndz6bTPhCYb5YEmbpllp4SF2pwotxOI08vQbdvUWQXpS0yxa7iTRmMgD
+UAo4Lw1+CmEAXT/pNtHhN34wlqPfJghsN1DBqAKLO3nzze/nBg/j/uDYTvRbr/Ck
+XywR/YaRFqr9tia040g1ND5xQa73Aw3r9nGoHxc8tcP1ppajtM5gHtfHmhRiRWes
+ZdjStPbWfUl0924m4GzWrafnqVm54uWLXyFiMsK1X1bYdLVmpO/Nxk/FKoSWYsCf
+OK323WPntlH1k4KChQxYti+6ck8wb71stZRcOFRS64teQeAl6sThyRxahC2M9Rad
+iOcpfv4Z0oVAEU8xn6C+2mpFSbWTOxWF3Wh87okE92mzTrq/4L1YPN2hENrUYQ5y
+Zb+A+5p40Jyvz4LdKezd66xeax9PBGlviy5Ee40Kn39PhQyUmcSIiK+xZdzFVuGp
+2JEBH/8RGoVzODmRt198tlb2+12+BzMK6bjMHKo5MnmBsPqHXD5nCFkho8WZ2hca
+9L8nXmzwPB8JMjUu2mtzE3OxK/7OAzFZRfyulWvLZ8By9lLs2wIFmW6ztNJMV7Os
+J5DU+kCI7+y843Nh3mBiDFm31SZZX+QkWHYMv4OuXLMkiYfygELcoRB24NxHJVFF
+6Kx2LuvcqxXetpfWP3GhXFMCAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..25990aea65b5c75afbf0bd4325ce259a50c08e5d
--- /dev/null
+++ b/src/app/api/auth/login/route.ts
@@ -0,0 +1,91 @@
+import * as Yup from "yup";
+
+import ssrAxios, {endpoints, ErrorResponse, isErrorResponse} from "@/utils/server/axios";
+
+
+export const dynamic = 'force-dynamic';
+
+interface FormDataObject {
+    [key: string]: string | File | Blob | FormDataEntryValue;
+}
+
+export async function POST(request: Request) {
+    const formData = await request.formData();
+    const data: FormDataObject = {};
+    formData.forEach((value, key) => {
+        data[key] = value;
+    })
+
+    const loginSchema = Yup.object().shape({
+        email: Yup.string()
+            .required('Email is required')
+            .max(50, 'Email must be at most 50 characters')
+            .email('Email must be a valid email address'),
+        password: Yup.string()
+            .required('Password is required')
+            .min(8, 'Password must be at least 8 characters')
+            .max(20, 'Password must be at most 20 characters')
+            .matches(
+                /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,20}$/,
+                'Password must contain at least one letter, one number and one special character'
+            ),
+    });
+
+    // 检查是否有多余的字段
+    const extraFields = Object.keys(data).filter(key => !(key in (loginSchema.fields as any)));
+    if (extraFields.length > 0) {
+        return Response.json({
+            code: -1,
+            message: `Error: Unexpected fields found: ${extraFields.join(', ')}`,
+            jwt: "",
+        });
+    }
+
+    // 验证数据
+    loginSchema.validate(data).catch(error => {
+        return Response.json({
+            code: -1,
+            message: error.message,
+            jwt: "",
+        });
+    });
+
+    const URL = endpoints.login;
+    try {
+        const postData = new FormData();
+        postData.append('email', data['email']);
+        postData.append('password', data['password']);
+
+        const response = await ssrAxios.post(URL, postData, {
+            headers: {
+                "Content-Type": "multipart/form-data",
+            }
+        });
+
+        const message = response.data.code !== 0? "用户名或密码错误":"";
+
+        return Response.json({
+            code: response.data.code,
+            message: message,
+            jwt: response.data.jwt,
+        });
+    } catch(error) {
+        console.error("api/auth/login post form-data failed");
+        if (isErrorResponse(error)) {
+            const errorResponse = error as ErrorResponse;
+            console.error("Reason: " + errorResponse.name + ": " + errorResponse.message);
+            console.error("Request headers: " + JSON.stringify(errorResponse.config.headers));
+            console.error("Request baseURL: " + errorResponse.config.baseURL);
+            console.error("Request method: " + errorResponse.config.method);
+            console.error("Request url: " + errorResponse.config.url);
+        } else {
+            console.error("Error: ", error);
+        }
+
+        return Response.json({
+            code: -1,
+            message: "Error: Internal server failed",
+            jwt: "",
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index ecd5f937198188fb8455f33d5ca90b434e5c12f3..12ea10e2c57de8305825495358e0a2791f704c62 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,4 +1,5 @@
-import { LoginView } from '@/sections/auth';
+import { LoginView } from '@/views/login';
+import {Suspense} from "react";
 
 // ----------------------------------------------------------------------
 
@@ -7,5 +8,9 @@ export const metadata = {
 };
 
 export default function LoginPage() {
-  return <LoginView />;
+    return (
+        <Suspense>
+            <LoginView />
+        </Suspense>
+    );
 }
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 8bdd57f3316c5722f37cd80128d93930ac5be12a..fffbf271f9a3d51a59e612146655356ef37e07c9 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,4 +1,6 @@
+import {redirect} from "next/navigation";
 
+import JWTValidate from "@/utils/server/jwt-validate";
 
 // ----------------------------------------------------------------------
 
@@ -6,6 +8,10 @@ export const metadata = {
     title: 'Hinko.dev 默认页面',
 };
 
-export default function AppPage() {
-    return <div>测试页面</div>;
+export default async function AppPage() {
+    const valid = await JWTValidate();
+    if (valid)
+        return <div>成功页面</div>;
+    else
+        return redirect("/login");
 }
\ No newline at end of file
diff --git a/src/config-global.ts b/src/config-global.ts
index 3c90f6b4111da78b12f5bb0557ea9182b717bd5e..2440dac7f885a2e0e3d47e7a2ef050f85766a7e5 100644
--- a/src/config-global.ts
+++ b/src/config-global.ts
@@ -1,5 +1,9 @@
 // API
 // ----------------------------------------------------------------------
 
-export const HOST_API = process.env.DAY1_AUTH_HOST_API;
+export const HOST_API = process.env.DAY1_AUTH_HOST_API ?? "http://localhost:8000";
 export const ASSETS_API = process.env.DAY1_AUTH_ASSETS_API;
+
+export const HOSTNAME = process.env.DAY1_AUTH_HOSTNAME ?? "localhost";
+
+export const PUBLIC_KEY_PATH = process.env.DAY1_AUTH_PUBLIC_KEY_PATH ?? "public.pem";
diff --git a/src/sections/auth/login-view.tsx b/src/sections/auth/login-view.tsx
deleted file mode 100644
index 39198260e8a5f0ad5fd89237c935cfb168cf38c6..0000000000000000000000000000000000000000
--- a/src/sections/auth/login-view.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-'use client';
-
-import * as Yup from 'yup';
-import { useForm } from 'react-hook-form';
-import { yupResolver } from '@hookform/resolvers/yup';
-
-import Link from '@mui/material/Link';
-import Stack from '@mui/material/Stack';
-import IconButton from '@mui/material/IconButton';
-import Typography from '@mui/material/Typography';
-import LoadingButton from '@mui/lab/LoadingButton';
-import InputAdornment from '@mui/material/InputAdornment';
-
-import RouterLink from '@/components/router-link';
-
-import { useBoolean } from '@/hooks/use-boolean';
-
-import Iconify from '@/components/iconify';
-import FormProvider, { RHFTextField } from '@/components/hook-form';
-
-// ----------------------------------------------------------------------
-
-export default function ModernLoginView() {
-  const password = useBoolean();
-
-  const LoginSchema = Yup.object().shape({
-    email: Yup.string().required('Email is required').email('Email must be a valid email address'),
-    password: Yup.string().required('Password is required'),
-  });
-
-  const defaultValues = {
-    email: '',
-    password: '',
-  };
-
-  const methods = useForm({
-    resolver: yupResolver(LoginSchema),
-    defaultValues,
-  });
-
-  const {
-    handleSubmit,
-    formState: { isSubmitting },
-  } = methods;
-
-  const onSubmit = handleSubmit(async (data) => {
-    try {
-      await new Promise((resolve) => setTimeout(resolve, 500));
-      console.info('DATA', data);
-    } catch (error) {
-      console.error(error);
-    }
-  });
-
-  const renderHead = (
-    <Stack spacing={2} sx={{ mb: 5 }}>
-      <Typography variant="h4">Sign in to Hinko.dev</Typography>
-    </Stack>
-  );
-
-  const renderForm = (
-    <Stack spacing={2.5}>
-      <RHFTextField name="email" label="Email address" />
-
-      <RHFTextField
-        name="password"
-        label="Password"
-        type={password.value ? 'text' : 'password'}
-        InputProps={{
-          endAdornment: (
-            <InputAdornment position="end">
-              <IconButton onClick={password.onToggle} edge="end">
-                <Iconify icon={password.value ? 'solar:eye-bold' : 'solar:eye-closed-bold'} />
-              </IconButton>
-            </InputAdornment>
-          ),
-        }}
-      />
-
-      <LoadingButton
-        fullWidth
-        color="inherit"
-        size="large"
-        type="submit"
-        variant="contained"
-        loading={isSubmitting}
-        endIcon={<Iconify icon="eva:arrow-ios-forward-fill" />}
-        sx={{ justifyContent: 'space-between', pl: 2, pr: 1.5 }}
-      >
-        Login
-      </LoadingButton>
-    </Stack>
-  );
-
-  return (
-    <FormProvider methods={methods} onSubmit={onSubmit}>
-      {renderHead}
-
-      {renderForm}
-    </FormProvider>
-  );
-}
diff --git a/src/utils/client/axios.ts b/src/utils/client/axios.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0808a17ba0e37b72b1e26101a9328728f89bfdac
--- /dev/null
+++ b/src/utils/client/axios.ts
@@ -0,0 +1,35 @@
+import axios, {AxiosRequestConfig} from 'axios';
+
+// ----------------------------------------------------------------------
+
+const csrAxios = axios.create({
+    timeout: 5000,
+    headers: {
+        Accept: "application/json",
+        "Content-Type": "application/json",
+    }
+});
+
+// 错误拦截
+csrAxios.interceptors.response.use(
+    (res) => res,
+    (error) => Promise.reject(error)
+);
+
+export default csrAxios;
+
+// ----------------------------------------------------------------------
+
+export const fetcher = async (args: string | [string, AxiosRequestConfig]) => {
+    const [url, config] = Array.isArray(args) ? args : [args];
+
+    const res = await csrAxios.get(url, {...config});
+
+    return res.data;
+};
+
+// ----------------------------------------------------------------------
+
+export const endpoints = {
+    login: '/api/auth/login',
+};
diff --git a/src/utils/server/axios.ts b/src/utils/server/axios.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11f998410af0c9d51306ee22ddeacf360c60da2c
--- /dev/null
+++ b/src/utils/server/axios.ts
@@ -0,0 +1,83 @@
+import axios, {AxiosRequestConfig} from 'axios';
+
+import {HOST_API} from '@/config-global';
+
+// ----------------------------------------------------------------------
+
+export interface ErrorResponse {
+    name: string;
+    message: string;
+    config: {
+        headers: any;
+        baseURL: string;
+        method: string;
+        url: string;
+    }
+}
+
+function isErrorConfig(obj: any): boolean {
+    return obj
+        && typeof obj === 'object'
+        && 'headers' in obj
+        && 'baseURL' in obj
+        && 'method' in obj
+        && 'url' in obj;
+}
+
+export function isErrorResponse(obj: any): obj is ErrorResponse {
+    return obj
+        && typeof obj === 'object'
+        && 'name' in obj
+        && 'message' in obj
+        && 'config' in obj
+        && isErrorConfig(obj.config);
+}
+
+const ssrAxios = axios.create({
+    baseURL: HOST_API,
+    timeout: 5000,
+    headers: {
+        Accept: "application/json",
+        "Content-Type": "application/json",
+    }
+});
+
+// 错误拦截
+ssrAxios.interceptors.response.use(
+    (res) => res,
+    (error) => {
+        const errorResponse: ErrorResponse = {
+            name: error.name,
+            message: error.message,
+            config: {
+                headers: error.config.headers,
+                baseURL: error.config.baseURL,
+                method: error.config.method,
+                url: error.config.url,
+            }
+        };
+        return Promise.reject(errorResponse);
+    }
+);
+
+export default ssrAxios;
+
+// ----------------------------------------------------------------------
+
+export const fetcher = async (args: string | [string, AxiosRequestConfig]) => {
+    const [url, config] = Array.isArray(args) ? args : [args];
+
+    try {
+        const res = await ssrAxios.get(url, {...config});
+        const data = res.data;
+        return {data, undefined};
+    } catch (error) {
+        return {undefined, error};
+    }
+};
+
+// ----------------------------------------------------------------------
+
+export const endpoints = {
+    login: '/api/auth/login',
+};
diff --git a/src/utils/server/jwt-validate.ts b/src/utils/server/jwt-validate.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09ca786ccc400e24dda1c38d4682d6c92915a666
--- /dev/null
+++ b/src/utils/server/jwt-validate.ts
@@ -0,0 +1,45 @@
+import {cookies} from "next/headers";
+import * as jose from "jose";
+import fs from 'fs';
+import {PUBLIC_KEY_PATH} from "@/config-global";
+
+// ----------------------------------------------------------------------
+
+export default async function JWTValidate() {
+
+    const authorization = cookies().get('auth.jwt');
+
+    if (authorization === undefined)
+        return false;
+    const jwt = authorization.value;
+
+    const publicKeyString: string = fs.readFileSync(PUBLIC_KEY_PATH, 'utf8');
+
+    const alg = 'RS512';
+
+    const publicKey = await jose.importSPKI(publicKeyString, alg);
+
+    try {
+        const {payload, protectedHeader} = await jose.jwtVerify(jwt, publicKey, {
+            subject: 'sso.hinko.dev',
+        });
+
+        if (typeof payload.name !== "string")
+            return false;
+
+        if (typeof payload.admin !== "boolean")
+            return false;
+
+        const currentTimestamp = new Date().getTime() / 1000;
+        if (typeof payload.nbf !== "number")
+            return false;
+        if (typeof payload.exp !== "number")
+            return false;
+
+    } catch (error) {
+        console.error("error: " + error);
+        return false;
+    }
+
+    return true;
+}
\ No newline at end of file
diff --git a/src/sections/auth/index.ts b/src/views/login/index.ts
similarity index 100%
rename from src/sections/auth/index.ts
rename to src/views/login/index.ts
diff --git a/src/views/login/login-view.tsx b/src/views/login/login-view.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..057e5dae279d3f6a05febbc60abb80a1d4a46441
--- /dev/null
+++ b/src/views/login/login-view.tsx
@@ -0,0 +1,142 @@
+'use client';
+
+import Cookies from "js-cookie";
+import {useRouter, useSearchParams} from "next/navigation";
+import {useState} from "react";
+import * as Yup from 'yup';
+import { useForm } from 'react-hook-form';
+import { yupResolver } from '@hookform/resolvers/yup';
+
+import Alert from "@mui/material/Alert";
+import Stack from '@mui/material/Stack';
+import IconButton from '@mui/material/IconButton';
+import Typography from '@mui/material/Typography';
+import LoadingButton from '@mui/lab/LoadingButton';
+import InputAdornment from '@mui/material/InputAdornment';
+
+import { useBoolean } from '@/hooks/use-boolean';
+
+import Iconify from '@/components/iconify';
+import FormProvider, { RHFTextField } from '@/components/hook-form';
+import csrAxios, {endpoints} from "@/utils/client/axios";
+import {HOSTNAME} from "@/config-global";
+
+
+// ----------------------------------------------------------------------
+
+export default function LoginView() {
+    const router = useRouter();
+
+    const [errorMsg, setErrorMsg] = useState('');
+
+    const searchParams = useSearchParams();
+
+    const redirectURL = searchParams.get('redirect_url');
+
+    const password = useBoolean();
+
+    const LoginSchema = Yup.object().shape({
+        email: Yup.string().required('Email is required').email('Email must be a valid email address'),
+        password: Yup.string().required('Password is required'),
+    });
+
+    const defaultValues = {
+        email: '',
+        password: '',
+    };
+
+    const methods = useForm({
+        resolver: yupResolver(LoginSchema),
+        defaultValues,
+    });
+
+    const {
+        reset,
+        handleSubmit,
+        formState: { isSubmitting },
+    } = methods;
+
+    const onSubmit = handleSubmit(async (data) => {
+        try {
+            const URL = endpoints.login;
+
+            const response = await csrAxios.post(URL, data, {
+                headers: {
+                    "Content-Type": "multipart/form-data",
+                }
+            });
+            if (response.data.code == 0) {
+                const in30m = 1/48;
+                Cookies.set("auth.jwt", response.data.jwt, {
+                    domain: HOSTNAME,
+                    expires: in30m,
+                    path: '/'
+                });
+                router.push(redirectURL || '/');
+            } else {
+                reset();
+                setErrorMsg(response.data.message);
+            }
+        } catch (error) {
+            console.error(error);
+            reset();
+            setErrorMsg(typeof error === 'string' ? error : "未知错误");
+        }
+    });
+
+    const renderHead = (
+        <Stack spacing={2} sx={{ mb: 5 }}>
+            <Typography variant="h4">Sign in to Hinko.dev</Typography>
+        </Stack>
+    );
+
+    const renderForm = (
+        <Stack spacing={2.5}>
+            <RHFTextField name="email" label="Email address" />
+
+            <RHFTextField
+                name="password"
+                label="Password"
+                type={password.value ? 'text' : 'password'}
+                InputProps={{
+                    endAdornment: (
+                        <InputAdornment position="end">
+                            <IconButton onClick={password.onToggle} edge="end">
+                                <Iconify icon={password.value ? 'solar:eye-bold' : 'solar:eye-closed-bold'} />
+                            </IconButton>
+                        </InputAdornment>
+                    ),
+                }}
+            />
+
+            <LoadingButton
+                fullWidth
+                color="inherit"
+                size="large"
+                type="submit"
+                variant="contained"
+                loading={isSubmitting}
+                endIcon={<Iconify icon="eva:arrow-ios-forward-fill" />}
+                sx={{ justifyContent: 'space-between', pl: 2, pr: 1.5 }}
+            >
+                Login
+            </LoadingButton>
+        </Stack>
+    );
+
+    return (
+        <>
+            {renderHead}
+
+            {!!errorMsg && (
+                <Alert severity="error" sx={{ mb: 3}}>
+                    {errorMsg}
+                </Alert>
+            )}
+
+            <FormProvider methods={methods} onSubmit={onSubmit}>
+                {renderForm}
+            </FormProvider>
+        </>
+    );
+}
diff --git a/yarn.lock b/yarn.lock
index 5770ed59e59f92ba8d383d9ab5654f5d59946624..e86d6599244653506d0a7569c48847977ee4effb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -525,6 +525,11 @@
   dependencies:
     tslib "^2.4.0"
 
+"@types/js-cookie@^3.0.6":
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.6.tgz#a04ca19e877687bd449f5ad37d33b104b71fdf95"
+  integrity sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -822,6 +827,11 @@ asynciterator.prototype@^1.0.0:
   dependencies:
     has-symbols "^1.0.3"
 
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
 available-typed-arrays@^1.0.6, available-typed-arrays@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
@@ -834,6 +844,15 @@ axe-core@=4.7.0:
   resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
   integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
 
+axios@^1.6.7:
+  version "1.6.7"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7"
+  integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==
+  dependencies:
+    follow-redirects "^1.15.4"
+    form-data "^4.0.0"
+    proxy-from-env "^1.1.0"
+
 axobject-query@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
@@ -976,6 +995,13 @@ color-name@~1.1.4:
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -1075,6 +1101,11 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
     has-property-descriptors "^1.0.0"
     object-keys "^1.1.1"
 
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
 dequal@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
@@ -1559,6 +1590,11 @@ flatted@^3.2.9:
   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
   integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
 
+follow-redirects@^1.15.4:
+  version "1.15.5"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
+  integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==
+
 for-each@^0.3.3:
   version "0.3.3"
   resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
@@ -1574,6 +1610,15 @@ foreground-child@^3.1.0:
     cross-spawn "^7.0.0"
     signal-exit "^4.0.1"
 
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
 framer-motion@^11.0.3:
   version "11.0.6"
   resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.0.6.tgz#97c3076d9d718486eb533b58b88db800139d5fc3"
@@ -2049,6 +2094,16 @@ jackspeak@^2.3.5:
   optionalDependencies:
     "@pkgjs/parseargs" "^0.11.0"
 
+jose@^5.2.2:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/jose/-/jose-5.2.2.tgz#b91170e9ba6dbe609b0c0a86568f9a1fbe4335c0"
+  integrity sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==
+
+js-cookie@^3.0.5:
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
+  integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -2228,6 +2283,18 @@ micromatch@^4.0.4:
     braces "^3.0.2"
     picomatch "^2.3.1"
 
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+  version "2.1.35"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
 minimatch@9.0.3, minimatch@^9.0.1:
   version "9.0.3"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
@@ -2541,6 +2608,11 @@ protocol-buffers-schema@^3.3.1:
   resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03"
   integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==
 
+proxy-from-env@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
 punycode@^2.1.0:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"