mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-29 05:15:12 +02:00
Add support for basic auth on FE
This commit is contained in:
11
web/src/app/auth/lib.ts
Normal file
11
web/src/app/auth/lib.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export async function requestEmailVerification(email: string) {
|
||||
return await fetch("/api/auth/request-verify-token", {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
}),
|
||||
});
|
||||
}
|
113
web/src/app/auth/login/EmailPasswordForm.tsx
Normal file
113
web/src/app/auth/login/EmailPasswordForm.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import { TextFormField } from "@/components/admin/connectors/Field";
|
||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||
import { basicLogin, basicSignup } from "@/lib/user";
|
||||
import { Button } from "@tremor/react";
|
||||
import { Form, Formik } from "formik";
|
||||
import { useRouter } from "next/navigation";
|
||||
import * as Yup from "yup";
|
||||
import { requestEmailVerification } from "../lib";
|
||||
import { useState } from "react";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
|
||||
export function EmailPasswordForm({
|
||||
isSignup = false,
|
||||
shouldVerify,
|
||||
}: {
|
||||
isSignup?: boolean;
|
||||
shouldVerify?: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { popup, setPopup } = usePopup();
|
||||
const [isWorking, setIsWorking] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isWorking && <Spinner />}
|
||||
{popup}
|
||||
<Formik
|
||||
initialValues={{
|
||||
email: "",
|
||||
password: "",
|
||||
}}
|
||||
validationSchema={Yup.object().shape({
|
||||
email: Yup.string().email().required(),
|
||||
password: Yup.string().required(),
|
||||
})}
|
||||
onSubmit={async (values) => {
|
||||
if (isSignup) {
|
||||
// login is fast, no need to show a spinner
|
||||
setIsWorking(true);
|
||||
const response = await basicSignup(values.email, values.password);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorDetail = (await response.json()).detail;
|
||||
|
||||
let errorMsg = "Unknown error";
|
||||
if (errorDetail === "REGISTER_USER_ALREADY_EXISTS") {
|
||||
errorMsg =
|
||||
"An account already exists with the specified email.";
|
||||
}
|
||||
setPopup({
|
||||
type: "error",
|
||||
message: `Failed to sign up - ${errorMsg}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const loginResponse = await basicLogin(values.email, values.password);
|
||||
if (loginResponse.ok) {
|
||||
if (isSignup && shouldVerify) {
|
||||
await requestEmailVerification(values.email);
|
||||
router.push("/auth/waiting-on-verification");
|
||||
} else {
|
||||
router.push("/");
|
||||
}
|
||||
} else {
|
||||
setIsWorking(false);
|
||||
const errorDetail = (await loginResponse.json()).detail;
|
||||
|
||||
let errorMsg = "Unknown error";
|
||||
if (errorDetail === "LOGIN_BAD_CREDENTIALS") {
|
||||
errorMsg = "Invalid email or password";
|
||||
}
|
||||
setPopup({
|
||||
type: "error",
|
||||
message: `Failed to login - ${errorMsg}`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({ isSubmitting, values }) => (
|
||||
<Form>
|
||||
<TextFormField
|
||||
name="email"
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="email@yourcompany.com"
|
||||
/>
|
||||
|
||||
<TextFormField
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
placeholder="**************"
|
||||
/>
|
||||
|
||||
<div className="flex">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="mx-auto w-full"
|
||||
>
|
||||
{isSignup ? "Sign Up" : "Log In"}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -7,9 +7,11 @@ import {
|
||||
AuthTypeMetadata,
|
||||
} from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getWebVersion, getBackendVersion } from "@/lib/version";
|
||||
import Image from "next/image";
|
||||
import { SignInButton } from "./SignInButton";
|
||||
import { EmailPasswordForm } from "./EmailPasswordForm";
|
||||
import { Card, Title, Text } from "@tremor/react";
|
||||
import Link from "next/link";
|
||||
|
||||
const Page = async ({
|
||||
searchParams,
|
||||
@@ -32,24 +34,17 @@ const Page = async ({
|
||||
console.log(`Some fetch failed for the login page - ${e}`);
|
||||
}
|
||||
|
||||
let web_version: string | null = null;
|
||||
let backend_version: string | null = null;
|
||||
try {
|
||||
[web_version, backend_version] = await Promise.all([
|
||||
getWebVersion(),
|
||||
getBackendVersion(),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Version info fetch failed for the login page - ${e}`);
|
||||
}
|
||||
|
||||
// simply take the user to the home page if Auth is disabled
|
||||
if (authTypeMetadata?.authType === "disabled") {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
// if user is already logged in, take them to the main app page
|
||||
if (currentUser && currentUser.is_active && currentUser.is_verified) {
|
||||
if (currentUser && currentUser.is_active) {
|
||||
if (authTypeMetadata?.requiresVerification && !currentUser.is_verified) {
|
||||
return redirect("/auth/waiting-on-verification");
|
||||
}
|
||||
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
@@ -77,20 +72,38 @@ const Page = async ({
|
||||
<div className="h-16 w-16 mx-auto">
|
||||
<Image src="/logo.png" alt="Logo" width="1419" height="1520" />
|
||||
</div>
|
||||
<h2 className="text-center text-xl text-strong font-bold mt-6">
|
||||
Log In to Danswer
|
||||
</h2>
|
||||
{authUrl && authTypeMetadata && (
|
||||
<SignInButton
|
||||
authorizeUrl={authUrl}
|
||||
authType={authTypeMetadata?.authType}
|
||||
/>
|
||||
<>
|
||||
<h2 className="text-center text-xl text-strong font-bold mt-6">
|
||||
Log In to Danswer
|
||||
</h2>
|
||||
|
||||
<SignInButton
|
||||
authorizeUrl={authUrl}
|
||||
authType={authTypeMetadata?.authType}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{authTypeMetadata?.authType === "basic" && (
|
||||
<Card className="mt-4 w-96">
|
||||
<div className="flex">
|
||||
<Title className="mb-2 mx-auto font-bold">
|
||||
Log In to Danswer
|
||||
</Title>
|
||||
</div>
|
||||
<EmailPasswordForm />
|
||||
<div className="flex">
|
||||
<Text className="mt-4 mx-auto">
|
||||
Don't have an account?{" "}
|
||||
<Link href="/auth/signup" className="text-link font-medium">
|
||||
Create an account
|
||||
</Link>
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="fixed bottom-4 right-4 z-50 text-slate-400 p-2">
|
||||
VERSION w{web_version} b{backend_version}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
83
web/src/app/auth/signup/page.tsx
Normal file
83
web/src/app/auth/signup/page.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
import { User } from "@/lib/types";
|
||||
import {
|
||||
getCurrentUserSS,
|
||||
getAuthTypeMetadataSS,
|
||||
AuthTypeMetadata,
|
||||
} from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import { EmailPasswordForm } from "../login/EmailPasswordForm";
|
||||
import { Card, Title, Text } from "@tremor/react";
|
||||
import Link from "next/link";
|
||||
|
||||
const Page = async () => {
|
||||
// catch cases where the backend is completely unreachable here
|
||||
// without try / catch, will just raise an exception and the page
|
||||
// will not render
|
||||
let authTypeMetadata: AuthTypeMetadata | null = null;
|
||||
let currentUser: User | null = null;
|
||||
try {
|
||||
[authTypeMetadata, currentUser] = await Promise.all([
|
||||
getAuthTypeMetadataSS(),
|
||||
getCurrentUserSS(),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Some fetch failed for the login page - ${e}`);
|
||||
}
|
||||
|
||||
// simply take the user to the home page if Auth is disabled
|
||||
if (authTypeMetadata?.authType === "disabled") {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
// if user is already logged in, take them to the main app page
|
||||
if (currentUser && currentUser.is_active) {
|
||||
if (!authTypeMetadata?.requiresVerification || currentUser.is_verified) {
|
||||
return redirect("/");
|
||||
}
|
||||
return redirect("/auth/waiting-on-verification");
|
||||
}
|
||||
|
||||
// only enable this page if basic login is enabled
|
||||
if (authTypeMetadata?.authType !== "basic") {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="absolute top-10x w-full">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div>
|
||||
<div className="h-16 w-16 mx-auto">
|
||||
<Image src="/logo.png" alt="Logo" width="1419" height="1520" />
|
||||
</div>
|
||||
<Card className="mt-4 w-96">
|
||||
<div className="flex">
|
||||
<Title className="mb-2 mx-auto font-bold">
|
||||
Sign Up for Danswer
|
||||
</Title>
|
||||
</div>
|
||||
<EmailPasswordForm
|
||||
isSignup
|
||||
shouldVerify={authTypeMetadata?.requiresVerification}
|
||||
/>
|
||||
|
||||
<div className="flex">
|
||||
<Text className="mt-4 mx-auto">
|
||||
Already have an account?{" "}
|
||||
<Link href="/auth/login" className="text-link font-medium">
|
||||
Log In
|
||||
</Link>
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
80
web/src/app/auth/verify-email/Verify.tsx
Normal file
80
web/src/app/auth/verify-email/Verify.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { Text } from "@tremor/react";
|
||||
import { RequestNewVerificationEmail } from "../waiting-on-verification/RequestNewVerificationEmail";
|
||||
import { User } from "@/lib/types";
|
||||
|
||||
export function Verify({ user }: { user: User | null }) {
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
|
||||
const [error, setError] = useState("");
|
||||
|
||||
async function verify() {
|
||||
const token = searchParams.get("token");
|
||||
if (!token) {
|
||||
setError(
|
||||
"Missing verification token. Try requesting a new verification email."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch("/api/auth/verify", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
router.push("/");
|
||||
} else {
|
||||
const errorDetail = (await response.json()).detail;
|
||||
setError(
|
||||
`Failed to verify your email - ${errorDetail}. Please try requesting a new verification email.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
verify();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="absolute top-10x w-full">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div>
|
||||
<div className="h-16 w-16 mx-auto animate-pulse">
|
||||
<Image src="/logo.png" alt="Logo" width="1419" height="1520" />
|
||||
</div>
|
||||
|
||||
{!error ? (
|
||||
<Text className="mt-2">Verifying your email...</Text>
|
||||
) : (
|
||||
<div>
|
||||
<Text className="mt-2">{error}</Text>
|
||||
|
||||
{user && (
|
||||
<div className="text-center">
|
||||
<RequestNewVerificationEmail email={user.email}>
|
||||
<Text className="mt-2 text-link">
|
||||
Get new verification email
|
||||
</Text>
|
||||
</RequestNewVerificationEmail>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
30
web/src/app/auth/verify-email/page.tsx
Normal file
30
web/src/app/auth/verify-email/page.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
AuthTypeMetadata,
|
||||
getAuthTypeMetadataSS,
|
||||
getCurrentUserSS,
|
||||
} from "@/lib/userSS";
|
||||
import { Verify } from "./Verify";
|
||||
import { User } from "@/lib/types";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function Page() {
|
||||
// catch cases where the backend is completely unreachable here
|
||||
// without try / catch, will just raise an exception and the page
|
||||
// will not render
|
||||
let authTypeMetadata: AuthTypeMetadata | null = null;
|
||||
let currentUser: User | null = null;
|
||||
try {
|
||||
[authTypeMetadata, currentUser] = await Promise.all([
|
||||
getAuthTypeMetadataSS(),
|
||||
getCurrentUserSS(),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Some fetch failed for the login page - ${e}`);
|
||||
}
|
||||
|
||||
if (!authTypeMetadata?.requiresVerification || currentUser?.is_verified) {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
return <Verify user={currentUser} />;
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||
import { requestEmailVerification } from "../lib";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
import { useState } from "react";
|
||||
|
||||
export function RequestNewVerificationEmail({
|
||||
children,
|
||||
email,
|
||||
}: {
|
||||
children: JSX.Element | string;
|
||||
email: string;
|
||||
}) {
|
||||
const { popup, setPopup } = usePopup();
|
||||
const [isRequestingVerification, setIsRequestingVerification] =
|
||||
useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
className="text-link"
|
||||
onClick={async () => {
|
||||
setIsRequestingVerification(true);
|
||||
const response = await requestEmailVerification(email);
|
||||
setIsRequestingVerification(false);
|
||||
|
||||
if (response.ok) {
|
||||
setPopup({
|
||||
type: "success",
|
||||
message: "A new verification email has been sent!",
|
||||
});
|
||||
} else {
|
||||
const errorDetail = (await response.json()).detail;
|
||||
setPopup({
|
||||
type: "error",
|
||||
message: `Failed to send a new verification email - ${errorDetail}`,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isRequestingVerification && <Spinner />}
|
||||
{popup}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
69
web/src/app/auth/waiting-on-verification/page.tsx
Normal file
69
web/src/app/auth/waiting-on-verification/page.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
AuthTypeMetadata,
|
||||
getAuthTypeMetadataSS,
|
||||
getCurrentUserSS,
|
||||
} from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
import { User } from "@/lib/types";
|
||||
import { Text } from "@tremor/react";
|
||||
import { RequestNewVerificationEmail } from "./RequestNewVerificationEmail";
|
||||
|
||||
export default async function Page() {
|
||||
// catch cases where the backend is completely unreachable here
|
||||
// without try / catch, will just raise an exception and the page
|
||||
// will not render
|
||||
let authTypeMetadata: AuthTypeMetadata | null = null;
|
||||
let currentUser: User | null = null;
|
||||
try {
|
||||
[authTypeMetadata, currentUser] = await Promise.all([
|
||||
getAuthTypeMetadataSS(),
|
||||
getCurrentUserSS(),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(`Some fetch failed for the login page - ${e}`);
|
||||
}
|
||||
|
||||
if (!currentUser) {
|
||||
if (authTypeMetadata?.authType === "disabled") {
|
||||
return redirect("/");
|
||||
}
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
if (!authTypeMetadata?.requiresVerification || currentUser.is_verified) {
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="absolute top-10x w-full">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div>
|
||||
<div className="h-16 w-16 mx-auto">
|
||||
<Image src="/logo.png" alt="Logo" width="1419" height="1520" />
|
||||
</div>
|
||||
|
||||
<div className="flex">
|
||||
<Text className="text-center font-medium text-lg mt-6 w-108">
|
||||
Hey <i>{currentUser.email}</i> - it looks like you haven't
|
||||
verified your email yet.
|
||||
<br />
|
||||
Check your inbox for an email from us to get started!
|
||||
<br />
|
||||
<br />
|
||||
If you don't see anything, click{" "}
|
||||
<RequestNewVerificationEmail email={currentUser.email}>
|
||||
here
|
||||
</RequestNewVerificationEmail>{" "}
|
||||
to request a new email.
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
@@ -1,4 +1,8 @@
|
||||
import { getAuthDisabledSS, getCurrentUserSS } from "@/lib/userSS";
|
||||
import {
|
||||
AuthTypeMetadata,
|
||||
getAuthTypeMetadataSS,
|
||||
getCurrentUserSS,
|
||||
} from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import { fetchSS } from "@/lib/utilsSS";
|
||||
import { Connector, DocumentSet, User, ValidSources } from "@/lib/types";
|
||||
@@ -31,7 +35,7 @@ export default async function ChatPage({
|
||||
const currentChatId = chatId ? parseInt(chatId) : null;
|
||||
|
||||
const tasks = [
|
||||
getAuthDisabledSS(),
|
||||
getAuthTypeMetadataSS(),
|
||||
getCurrentUserSS(),
|
||||
fetchSS("/manage/connector"),
|
||||
fetchSS("/manage/document-set"),
|
||||
@@ -45,7 +49,7 @@ export default async function ChatPage({
|
||||
// catch cases where the backend is completely unreachable here
|
||||
// without try / catch, will just raise an exception and the page
|
||||
// will not render
|
||||
let results: (User | Response | boolean | null)[] = [
|
||||
let results: (User | Response | AuthTypeMetadata | null)[] = [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -59,7 +63,7 @@ export default async function ChatPage({
|
||||
} catch (e) {
|
||||
console.log(`Some fetch failed for the main search page - ${e}`);
|
||||
}
|
||||
const authDisabled = results[0] as boolean;
|
||||
const authTypeMetadata = results[0] as AuthTypeMetadata;
|
||||
const user = results[1] as User | null;
|
||||
const connectorsResponse = results[2] as Response | null;
|
||||
const documentSetsResponse = results[3] as Response | null;
|
||||
@@ -67,10 +71,15 @@ export default async function ChatPage({
|
||||
const chatSessionsResponse = results[5] as Response | null;
|
||||
const chatSessionMessagesResponse = results[6] as Response | null;
|
||||
|
||||
const authDisabled = authTypeMetadata.authType === "disabled";
|
||||
if (!authDisabled && !user) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
if (!user?.is_verified && authTypeMetadata.requiresVerification) {
|
||||
return redirect("/auth/waiting-on-verification");
|
||||
}
|
||||
|
||||
let connectors: Connector<any>[] = [];
|
||||
if (connectorsResponse?.ok) {
|
||||
connectors = await connectorsResponse.json();
|
||||
|
@@ -1,6 +1,10 @@
|
||||
import { SearchSection } from "@/components/search/SearchSection";
|
||||
import { Header } from "@/components/Header";
|
||||
import { getAuthDisabledSS, getCurrentUserSS } from "@/lib/userSS";
|
||||
import {
|
||||
AuthTypeMetadata,
|
||||
getAuthTypeMetadataSS,
|
||||
getCurrentUserSS,
|
||||
} from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
import { ApiKeyModal } from "@/components/openai/ApiKeyModal";
|
||||
@@ -21,7 +25,7 @@ export default async function Home() {
|
||||
noStore();
|
||||
|
||||
const tasks = [
|
||||
getAuthDisabledSS(),
|
||||
getAuthTypeMetadataSS(),
|
||||
getCurrentUserSS(),
|
||||
fetchSS("/manage/connector"),
|
||||
fetchSS("/manage/document-set"),
|
||||
@@ -31,22 +35,32 @@ export default async function Home() {
|
||||
// catch cases where the backend is completely unreachable here
|
||||
// without try / catch, will just raise an exception and the page
|
||||
// will not render
|
||||
let results: (User | Response | boolean | null)[] = [null, null, null, null];
|
||||
let results: (User | Response | AuthTypeMetadata | null)[] = [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
];
|
||||
try {
|
||||
results = await Promise.all(tasks);
|
||||
} catch (e) {
|
||||
console.log(`Some fetch failed for the main search page - ${e}`);
|
||||
}
|
||||
const authDisabled = results[0] as boolean;
|
||||
const authTypeMetadata = results[0] as AuthTypeMetadata;
|
||||
const user = results[1] as User | null;
|
||||
const connectorsResponse = results[2] as Response | null;
|
||||
const documentSetsResponse = results[3] as Response | null;
|
||||
const personaResponse = results[4] as Response | null;
|
||||
|
||||
const authDisabled = authTypeMetadata.authType === "disabled";
|
||||
if (!authDisabled && !user) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
if (!user?.is_verified && authTypeMetadata.requiresVerification) {
|
||||
return redirect("/auth/waiting-on-verification");
|
||||
}
|
||||
|
||||
let connectors: Connector<any>[] = [];
|
||||
if (connectorsResponse?.ok) {
|
||||
connectors = await connectorsResponse.json();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export type AuthType = "disabled" | "google_oauth" | "oidc" | "saml";
|
||||
export type AuthType = "disabled" | "basic" | "google_oauth" | "oidc" | "saml";
|
||||
|
||||
export const INTERNAL_URL = process.env.INTERNAL_URL || "http://127.0.0.1:8080";
|
||||
export const NEXT_PUBLIC_DISABLE_STREAMING =
|
||||
|
@@ -19,3 +19,38 @@ export const logout = async (): Promise<Response> => {
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
export const basicLogin = async (
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<Response> => {
|
||||
const params = new URLSearchParams([
|
||||
["username", email],
|
||||
["password", password],
|
||||
]);
|
||||
const response = await fetch("/api/auth/login", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: params,
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
export const basicSignup = async (email: string, password: string) => {
|
||||
const response = await fetch("/api/auth/register", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
username: email,
|
||||
password,
|
||||
}),
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
@@ -7,6 +7,7 @@ import { AuthType } from "./constants";
|
||||
export interface AuthTypeMetadata {
|
||||
authType: AuthType;
|
||||
autoRedirect: boolean;
|
||||
requiresVerification: boolean;
|
||||
}
|
||||
|
||||
export const getAuthTypeMetadataSS = async (): Promise<AuthTypeMetadata> => {
|
||||
@@ -15,15 +16,24 @@ export const getAuthTypeMetadataSS = async (): Promise<AuthTypeMetadata> => {
|
||||
throw new Error("Failed to fetch data");
|
||||
}
|
||||
|
||||
const data: { auth_type: string } = await res.json();
|
||||
const data: { auth_type: string; requires_verification: boolean } =
|
||||
await res.json();
|
||||
const authType = data.auth_type as AuthType;
|
||||
|
||||
// for SAML / OIDC, we auto-redirect the user to the IdP when the user visits
|
||||
// Danswer in an un-authenticated state
|
||||
if (authType === "oidc" || authType === "saml") {
|
||||
return { authType, autoRedirect: true };
|
||||
return {
|
||||
authType,
|
||||
autoRedirect: true,
|
||||
requiresVerification: data.requires_verification,
|
||||
};
|
||||
}
|
||||
return { authType, autoRedirect: false };
|
||||
return {
|
||||
authType,
|
||||
autoRedirect: false,
|
||||
requiresVerification: data.requires_verification,
|
||||
};
|
||||
};
|
||||
|
||||
export const getAuthDisabledSS = async (): Promise<boolean> => {
|
||||
@@ -65,6 +75,8 @@ export const getAuthUrlSS = async (authType: AuthType): Promise<string> => {
|
||||
switch (authType) {
|
||||
case "disabled":
|
||||
return "";
|
||||
case "basic":
|
||||
return "";
|
||||
case "google_oauth": {
|
||||
return await getGoogleOAuthUrlSS();
|
||||
}
|
||||
|
Reference in New Issue
Block a user