mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-22 22:11:03 +02:00
Add support for basic auth on FE
This commit is contained in:
parent
1e84b0daa4
commit
dab3ba8a41
@ -68,6 +68,12 @@ def verify_auth_setting() -> None:
|
|||||||
logger.info(f"Using Auth Type: {AUTH_TYPE.value}")
|
logger.info(f"Using Auth Type: {AUTH_TYPE.value}")
|
||||||
|
|
||||||
|
|
||||||
|
def user_needs_to_be_verified() -> bool:
|
||||||
|
# all other auth types besides basic should require users to be
|
||||||
|
# verified
|
||||||
|
return AUTH_TYPE != AuthType.BASIC or REQUIRE_EMAIL_VERIFICATION
|
||||||
|
|
||||||
|
|
||||||
def get_user_whitelist() -> list[str]:
|
def get_user_whitelist() -> list[str]:
|
||||||
global _user_whitelist
|
global _user_whitelist
|
||||||
if _user_whitelist is None:
|
if _user_whitelist is None:
|
||||||
@ -104,10 +110,9 @@ def verify_email_domain(email: str) -> None:
|
|||||||
def send_user_verification_email(user_email: str, token: str) -> None:
|
def send_user_verification_email(user_email: str, token: str) -> None:
|
||||||
msg = MIMEMultipart()
|
msg = MIMEMultipart()
|
||||||
msg["Subject"] = "Danswer Email Verification"
|
msg["Subject"] = "Danswer Email Verification"
|
||||||
msg["From"] = "no-reply@danswer.dev"
|
|
||||||
msg["To"] = user_email
|
msg["To"] = user_email
|
||||||
|
|
||||||
link = f"{WEB_DOMAIN}/verify-email?token={token}"
|
link = f"{WEB_DOMAIN}/auth/verify-email?token={token}"
|
||||||
|
|
||||||
body = MIMEText(f"Click the following link to verify your email address: {link}")
|
body = MIMEText(f"Click the following link to verify your email address: {link}")
|
||||||
msg.attach(body)
|
msg.attach(body)
|
||||||
@ -256,9 +261,11 @@ fastapi_users = FastAPIUserWithLogoutRouter[User, uuid.UUID](
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
optional_valid_user = fastapi_users.current_user(
|
# NOTE: verified=REQUIRE_EMAIL_VERIFICATION is not used here since we
|
||||||
active=True, verified=REQUIRE_EMAIL_VERIFICATION, optional=True
|
# take care of that in `double_check_user` ourself. This is needed, since
|
||||||
)
|
# we want the /me endpoint to still return a user even if they are not
|
||||||
|
# yet verified, so that the frontend knows they exist
|
||||||
|
optional_valid_user = fastapi_users.current_user(active=True, optional=True)
|
||||||
|
|
||||||
|
|
||||||
async def double_check_user(
|
async def double_check_user(
|
||||||
@ -276,6 +283,12 @@ async def double_check_user(
|
|||||||
detail="Access denied. User is not authenticated.",
|
detail="Access denied. User is not authenticated.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if user_needs_to_be_verified() and not user.is_verified:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Access denied. User is not verified.",
|
||||||
|
)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,8 +75,8 @@ OAUTH_CLIENT_SECRET = (
|
|||||||
REQUIRE_EMAIL_VERIFICATION = (
|
REQUIRE_EMAIL_VERIFICATION = (
|
||||||
os.environ.get("REQUIRE_EMAIL_VERIFICATION", "").lower() == "true"
|
os.environ.get("REQUIRE_EMAIL_VERIFICATION", "").lower() == "true"
|
||||||
)
|
)
|
||||||
SMTP_SERVER = os.environ.get("SMTP_SERVER", "smtp.gmail.com")
|
SMTP_SERVER = os.environ.get("SMTP_SERVER") or "smtp.gmail.com"
|
||||||
SMTP_PORT = int(os.environ.get("SMTP_PORT", "587"))
|
SMTP_PORT = int(os.environ.get("SMTP_PORT") or "587")
|
||||||
SMTP_USER = os.environ.get("SMTP_USER", "your-email@gmail.com")
|
SMTP_USER = os.environ.get("SMTP_USER", "your-email@gmail.com")
|
||||||
SMTP_PASS = os.environ.get("SMTP_PASS", "your-gmail-password")
|
SMTP_PASS = os.environ.get("SMTP_PASS", "your-gmail-password")
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from danswer import __version__
|
from danswer import __version__
|
||||||
|
from danswer.auth.users import user_needs_to_be_verified
|
||||||
from danswer.configs.app_configs import AUTH_TYPE
|
from danswer.configs.app_configs import AUTH_TYPE
|
||||||
from danswer.server.manage.models import AuthTypeResponse
|
from danswer.server.manage.models import AuthTypeResponse
|
||||||
from danswer.server.manage.models import VersionResponse
|
from danswer.server.manage.models import VersionResponse
|
||||||
@ -16,7 +17,9 @@ def healthcheck() -> StatusResponse:
|
|||||||
|
|
||||||
@router.get("/auth/type")
|
@router.get("/auth/type")
|
||||||
def get_auth_type() -> AuthTypeResponse:
|
def get_auth_type() -> AuthTypeResponse:
|
||||||
return AuthTypeResponse(auth_type=AUTH_TYPE)
|
return AuthTypeResponse(
|
||||||
|
auth_type=AUTH_TYPE, requires_verification=user_needs_to_be_verified()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/version")
|
@router.get("/version")
|
||||||
|
@ -18,6 +18,9 @@ class VersionResponse(BaseModel):
|
|||||||
|
|
||||||
class AuthTypeResponse(BaseModel):
|
class AuthTypeResponse(BaseModel):
|
||||||
auth_type: AuthType
|
auth_type: AuthType
|
||||||
|
# specifies whether the current auth setup requires
|
||||||
|
# users to have verified emails
|
||||||
|
requires_verification: bool
|
||||||
|
|
||||||
|
|
||||||
class UserInfo(BaseModel):
|
class UserInfo(BaseModel):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
from fastapi import status
|
||||||
from fastapi_users.db import SQLAlchemyUserDatabase
|
from fastapi_users.db import SQLAlchemyUserDatabase
|
||||||
from fastapi_users_db_sqlalchemy import UUID_ID
|
from fastapi_users_db_sqlalchemy import UUID_ID
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
@ -10,6 +11,7 @@ from danswer.auth.schemas import UserRead
|
|||||||
from danswer.auth.schemas import UserRole
|
from danswer.auth.schemas import UserRole
|
||||||
from danswer.auth.users import current_admin_user
|
from danswer.auth.users import current_admin_user
|
||||||
from danswer.auth.users import current_user
|
from danswer.auth.users import current_user
|
||||||
|
from danswer.auth.users import optional_valid_user
|
||||||
from danswer.db.engine import get_session
|
from danswer.db.engine import get_session
|
||||||
from danswer.db.engine import get_sqlalchemy_async_engine
|
from danswer.db.engine import get_sqlalchemy_async_engine
|
||||||
from danswer.db.models import User
|
from danswer.db.models import User
|
||||||
@ -55,9 +57,14 @@ async def get_user_role(user: User = Depends(current_user)) -> UserRoleResponse:
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/me")
|
@router.get("/me")
|
||||||
def verify_user_logged_in(user: User | None = Depends(current_user)) -> UserInfo:
|
def verify_user_logged_in(user: User | None = Depends(optional_valid_user)) -> UserInfo:
|
||||||
|
# NOTE: this does not use `current_user` / `current_admin_user` because we don't want
|
||||||
|
# to enforce user verification here - the frontend always wants to get the info about
|
||||||
|
# the current user regardless of if they are currently verified
|
||||||
if user is None:
|
if user is None:
|
||||||
raise HTTPException(status_code=401, detail="User Not Authenticated")
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN, detail="User Not Authenticated"
|
||||||
|
)
|
||||||
|
|
||||||
return UserInfo(
|
return UserInfo(
|
||||||
id=str(user.id),
|
id=str(user.id),
|
||||||
|
@ -22,6 +22,11 @@ services:
|
|||||||
- VALID_EMAIL_DOMAINS=${VALID_EMAIL_DOMAINS:-}
|
- VALID_EMAIL_DOMAINS=${VALID_EMAIL_DOMAINS:-}
|
||||||
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-}
|
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-}
|
||||||
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-}
|
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-}
|
||||||
|
- REQUIRE_EMAIL_VERIFICATION=${REQUIRE_EMAIL_VERIFICATION:-}
|
||||||
|
- SMTP_SERVER=${SMTP_SERVER:-} # For sending verification emails, if unspecified then defaults to 'smtp.gmail.com'
|
||||||
|
- SMTP_PORT=${SMTP_PORT:-} # For sending verification emails, if unspecified then defaults to '587'
|
||||||
|
- SMTP_USER=${SMTP_USER:-}
|
||||||
|
- SMTP_PASS=${SMTP_PASS:-}
|
||||||
# Gen AI Settings
|
# Gen AI Settings
|
||||||
- GEN_AI_MODEL_PROVIDER=${GEN_AI_MODEL_PROVIDER:-openai}
|
- GEN_AI_MODEL_PROVIDER=${GEN_AI_MODEL_PROVIDER:-openai}
|
||||||
- GEN_AI_MODEL_VERSION=${GEN_AI_MODEL_VERSION:-gpt-3.5-turbo}
|
- GEN_AI_MODEL_VERSION=${GEN_AI_MODEL_VERSION:-gpt-3.5-turbo}
|
||||||
|
@ -27,6 +27,7 @@ GEN_AI_MODEL_VERSION=gpt-4
|
|||||||
|
|
||||||
# The following are for configuring User Authentication, supported flows are:
|
# The following are for configuring User Authentication, supported flows are:
|
||||||
# disabled
|
# disabled
|
||||||
|
# basic (standard username / password)
|
||||||
# google_oauth (login with google/gmail account)
|
# google_oauth (login with google/gmail account)
|
||||||
# oidc (only in Danswer enterprise edition)
|
# oidc (only in Danswer enterprise edition)
|
||||||
# saml (only in Danswer enterprise edition)
|
# saml (only in Danswer enterprise edition)
|
||||||
@ -37,6 +38,16 @@ GOOGLE_OAUTH_CLIENT_ID=
|
|||||||
GOOGLE_OAUTH_CLIENT_SECRET=
|
GOOGLE_OAUTH_CLIENT_SECRET=
|
||||||
SECRET=
|
SECRET=
|
||||||
|
|
||||||
|
# if using basic auth and you want to require email verification,
|
||||||
|
# then uncomment / set the following
|
||||||
|
#REQUIRE_EMAIL_VERIFICATION=true
|
||||||
|
#SMTP_USER=your-email@company.com
|
||||||
|
#SMTP_PASS=your-gmail-password
|
||||||
|
|
||||||
|
# The below are only needed if you aren't using gmail as your SMTP
|
||||||
|
#SMTP_SERVER=
|
||||||
|
#SMTP_PORT=
|
||||||
|
|
||||||
# OpenID Connect (OIDC)
|
# OpenID Connect (OIDC)
|
||||||
#OPENID_CONFIG_URL=
|
#OPENID_CONFIG_URL=
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ data:
|
|||||||
AUTH_TYPE: "disabled" # Change this for production uses unless Danswer is only accessible behind VPN
|
AUTH_TYPE: "disabled" # Change this for production uses unless Danswer is only accessible behind VPN
|
||||||
SESSION_EXPIRE_TIME_SECONDS: "86400" # 1 Day Default
|
SESSION_EXPIRE_TIME_SECONDS: "86400" # 1 Day Default
|
||||||
VALID_EMAIL_DOMAINS: "" # Can be something like danswer.ai, as an extra double-check
|
VALID_EMAIL_DOMAINS: "" # Can be something like danswer.ai, as an extra double-check
|
||||||
|
SMTP_SERVER: "" # For sending verification emails, if unspecified then defaults to 'smtp.gmail.com'
|
||||||
|
SMTP_PORT: "" # For sending verification emails, if unspecified then defaults to '587'
|
||||||
|
SMTP_USER: "" # 'your-email@company.com'
|
||||||
|
SMTP_PASS: "" # 'your-gmail-password'
|
||||||
# Gen AI Settings
|
# Gen AI Settings
|
||||||
GEN_AI_MODEL_PROVIDER: "openai"
|
GEN_AI_MODEL_PROVIDER: "openai"
|
||||||
GEN_AI_MODEL_VERSION: "gpt-3.5-turbo" # Use GPT-4 if you have it
|
GEN_AI_MODEL_VERSION: "gpt-3.5-turbo" # Use GPT-4 if you have it
|
||||||
|
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,
|
AuthTypeMetadata,
|
||||||
} from "@/lib/userSS";
|
} from "@/lib/userSS";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { getWebVersion, getBackendVersion } from "@/lib/version";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { SignInButton } from "./SignInButton";
|
import { SignInButton } from "./SignInButton";
|
||||||
|
import { EmailPasswordForm } from "./EmailPasswordForm";
|
||||||
|
import { Card, Title, Text } from "@tremor/react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
const Page = async ({
|
const Page = async ({
|
||||||
searchParams,
|
searchParams,
|
||||||
@ -32,24 +34,17 @@ const Page = async ({
|
|||||||
console.log(`Some fetch failed for the login page - ${e}`);
|
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
|
// simply take the user to the home page if Auth is disabled
|
||||||
if (authTypeMetadata?.authType === "disabled") {
|
if (authTypeMetadata?.authType === "disabled") {
|
||||||
return redirect("/");
|
return redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if user is already logged in, take them to the main app page
|
// 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("/");
|
return redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,20 +72,38 @@ const Page = async ({
|
|||||||
<div className="h-16 w-16 mx-auto">
|
<div className="h-16 w-16 mx-auto">
|
||||||
<Image src="/logo.png" alt="Logo" width="1419" height="1520" />
|
<Image src="/logo.png" alt="Logo" width="1419" height="1520" />
|
||||||
</div>
|
</div>
|
||||||
|
{authUrl && authTypeMetadata && (
|
||||||
|
<>
|
||||||
<h2 className="text-center text-xl text-strong font-bold mt-6">
|
<h2 className="text-center text-xl text-strong font-bold mt-6">
|
||||||
Log In to Danswer
|
Log In to Danswer
|
||||||
</h2>
|
</h2>
|
||||||
{authUrl && authTypeMetadata && (
|
|
||||||
<SignInButton
|
<SignInButton
|
||||||
authorizeUrl={authUrl}
|
authorizeUrl={authUrl}
|
||||||
authType={authTypeMetadata?.authType}
|
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>
|
</div>
|
||||||
<div className="fixed bottom-4 right-4 z-50 text-slate-400 p-2">
|
|
||||||
VERSION w{web_version} b{backend_version}
|
|
||||||
</div>
|
|
||||||
</main>
|
</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 { redirect } from "next/navigation";
|
||||||
import { fetchSS } from "@/lib/utilsSS";
|
import { fetchSS } from "@/lib/utilsSS";
|
||||||
import { Connector, DocumentSet, User, ValidSources } from "@/lib/types";
|
import { Connector, DocumentSet, User, ValidSources } from "@/lib/types";
|
||||||
@ -31,7 +35,7 @@ export default async function ChatPage({
|
|||||||
const currentChatId = chatId ? parseInt(chatId) : null;
|
const currentChatId = chatId ? parseInt(chatId) : null;
|
||||||
|
|
||||||
const tasks = [
|
const tasks = [
|
||||||
getAuthDisabledSS(),
|
getAuthTypeMetadataSS(),
|
||||||
getCurrentUserSS(),
|
getCurrentUserSS(),
|
||||||
fetchSS("/manage/connector"),
|
fetchSS("/manage/connector"),
|
||||||
fetchSS("/manage/document-set"),
|
fetchSS("/manage/document-set"),
|
||||||
@ -45,7 +49,7 @@ export default async function ChatPage({
|
|||||||
// catch cases where the backend is completely unreachable here
|
// catch cases where the backend is completely unreachable here
|
||||||
// without try / catch, will just raise an exception and the page
|
// without try / catch, will just raise an exception and the page
|
||||||
// will not render
|
// will not render
|
||||||
let results: (User | Response | boolean | null)[] = [
|
let results: (User | Response | AuthTypeMetadata | null)[] = [
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -59,7 +63,7 @@ export default async function ChatPage({
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Some fetch failed for the main search page - ${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 user = results[1] as User | null;
|
||||||
const connectorsResponse = results[2] as Response | null;
|
const connectorsResponse = results[2] as Response | null;
|
||||||
const documentSetsResponse = results[3] 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 chatSessionsResponse = results[5] as Response | null;
|
||||||
const chatSessionMessagesResponse = results[6] as Response | null;
|
const chatSessionMessagesResponse = results[6] as Response | null;
|
||||||
|
|
||||||
|
const authDisabled = authTypeMetadata.authType === "disabled";
|
||||||
if (!authDisabled && !user) {
|
if (!authDisabled && !user) {
|
||||||
return redirect("/auth/login");
|
return redirect("/auth/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!user?.is_verified && authTypeMetadata.requiresVerification) {
|
||||||
|
return redirect("/auth/waiting-on-verification");
|
||||||
|
}
|
||||||
|
|
||||||
let connectors: Connector<any>[] = [];
|
let connectors: Connector<any>[] = [];
|
||||||
if (connectorsResponse?.ok) {
|
if (connectorsResponse?.ok) {
|
||||||
connectors = await connectorsResponse.json();
|
connectors = await connectorsResponse.json();
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { SearchSection } from "@/components/search/SearchSection";
|
import { SearchSection } from "@/components/search/SearchSection";
|
||||||
import { Header } from "@/components/Header";
|
import { Header } from "@/components/Header";
|
||||||
import { getAuthDisabledSS, getCurrentUserSS } from "@/lib/userSS";
|
import {
|
||||||
|
AuthTypeMetadata,
|
||||||
|
getAuthTypeMetadataSS,
|
||||||
|
getCurrentUserSS,
|
||||||
|
} from "@/lib/userSS";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||||
import { ApiKeyModal } from "@/components/openai/ApiKeyModal";
|
import { ApiKeyModal } from "@/components/openai/ApiKeyModal";
|
||||||
@ -21,7 +25,7 @@ export default async function Home() {
|
|||||||
noStore();
|
noStore();
|
||||||
|
|
||||||
const tasks = [
|
const tasks = [
|
||||||
getAuthDisabledSS(),
|
getAuthTypeMetadataSS(),
|
||||||
getCurrentUserSS(),
|
getCurrentUserSS(),
|
||||||
fetchSS("/manage/connector"),
|
fetchSS("/manage/connector"),
|
||||||
fetchSS("/manage/document-set"),
|
fetchSS("/manage/document-set"),
|
||||||
@ -31,22 +35,32 @@ export default async function Home() {
|
|||||||
// catch cases where the backend is completely unreachable here
|
// catch cases where the backend is completely unreachable here
|
||||||
// without try / catch, will just raise an exception and the page
|
// without try / catch, will just raise an exception and the page
|
||||||
// will not render
|
// will not render
|
||||||
let results: (User | Response | boolean | null)[] = [null, null, null, null];
|
let results: (User | Response | AuthTypeMetadata | null)[] = [
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
];
|
||||||
try {
|
try {
|
||||||
results = await Promise.all(tasks);
|
results = await Promise.all(tasks);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Some fetch failed for the main search page - ${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 user = results[1] as User | null;
|
||||||
const connectorsResponse = results[2] as Response | null;
|
const connectorsResponse = results[2] as Response | null;
|
||||||
const documentSetsResponse = results[3] as Response | null;
|
const documentSetsResponse = results[3] as Response | null;
|
||||||
const personaResponse = results[4] as Response | null;
|
const personaResponse = results[4] as Response | null;
|
||||||
|
|
||||||
|
const authDisabled = authTypeMetadata.authType === "disabled";
|
||||||
if (!authDisabled && !user) {
|
if (!authDisabled && !user) {
|
||||||
return redirect("/auth/login");
|
return redirect("/auth/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!user?.is_verified && authTypeMetadata.requiresVerification) {
|
||||||
|
return redirect("/auth/waiting-on-verification");
|
||||||
|
}
|
||||||
|
|
||||||
let connectors: Connector<any>[] = [];
|
let connectors: Connector<any>[] = [];
|
||||||
if (connectorsResponse?.ok) {
|
if (connectorsResponse?.ok) {
|
||||||
connectors = await connectorsResponse.json();
|
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 INTERNAL_URL = process.env.INTERNAL_URL || "http://127.0.0.1:8080";
|
||||||
export const NEXT_PUBLIC_DISABLE_STREAMING =
|
export const NEXT_PUBLIC_DISABLE_STREAMING =
|
||||||
|
@ -19,3 +19,38 @@ export const logout = async (): Promise<Response> => {
|
|||||||
});
|
});
|
||||||
return 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 {
|
export interface AuthTypeMetadata {
|
||||||
authType: AuthType;
|
authType: AuthType;
|
||||||
autoRedirect: boolean;
|
autoRedirect: boolean;
|
||||||
|
requiresVerification: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAuthTypeMetadataSS = async (): Promise<AuthTypeMetadata> => {
|
export const getAuthTypeMetadataSS = async (): Promise<AuthTypeMetadata> => {
|
||||||
@ -15,15 +16,24 @@ export const getAuthTypeMetadataSS = async (): Promise<AuthTypeMetadata> => {
|
|||||||
throw new Error("Failed to fetch data");
|
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;
|
const authType = data.auth_type as AuthType;
|
||||||
|
|
||||||
// for SAML / OIDC, we auto-redirect the user to the IdP when the user visits
|
// for SAML / OIDC, we auto-redirect the user to the IdP when the user visits
|
||||||
// Danswer in an un-authenticated state
|
// Danswer in an un-authenticated state
|
||||||
if (authType === "oidc" || authType === "saml") {
|
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> => {
|
export const getAuthDisabledSS = async (): Promise<boolean> => {
|
||||||
@ -65,6 +75,8 @@ export const getAuthUrlSS = async (authType: AuthType): Promise<string> => {
|
|||||||
switch (authType) {
|
switch (authType) {
|
||||||
case "disabled":
|
case "disabled":
|
||||||
return "";
|
return "";
|
||||||
|
case "basic":
|
||||||
|
return "";
|
||||||
case "google_oauth": {
|
case "google_oauth": {
|
||||||
return await getGoogleOAuthUrlSS();
|
return await getGoogleOAuthUrlSS();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user