diff --git a/web/src/app/auth/logout/route.ts b/web/src/app/auth/logout/route.ts
new file mode 100644
index 00000000000..297094a9ec6
--- /dev/null
+++ b/web/src/app/auth/logout/route.ts
@@ -0,0 +1,8 @@
+import { logoutSS } from "@/lib/userSS";
+import { NextRequest } from "next/server";
+
+export const POST = async (request: NextRequest) => {
+ // Directs the logout request to the appropriate FastAPI endpoint.
+ // Needed since env variables don't work well on the client-side
+ return await logoutSS(request.headers);
+};
diff --git a/web/src/app/auth/saml/callback/route.ts b/web/src/app/auth/saml/callback/route.ts
new file mode 100644
index 00000000000..fe9db7be126
--- /dev/null
+++ b/web/src/app/auth/saml/callback/route.ts
@@ -0,0 +1,33 @@
+import { getDomain } from "@/lib/redirectSS";
+import { buildUrl } from "@/lib/utilsSS";
+import { NextRequest, NextResponse } from "next/server";
+
+// have to use this so we don't hit the redirect URL with a `POST` request
+const SEE_OTHER_REDIRECT_STATUS = 303;
+
+export const POST = async (request: NextRequest) => {
+ // Wrapper around the FastAPI endpoint /auth/saml/callback,
+ // which adds back a redirect to the main app.
+ const url = new URL(buildUrl("/auth/saml/callback"));
+ url.search = request.nextUrl.search;
+
+ const response = await fetch(url.toString(), {
+ method: "POST",
+ body: await request.formData(),
+ });
+ const setCookieHeader = response.headers.get("set-cookie");
+
+ if (!setCookieHeader) {
+ return NextResponse.redirect(
+ new URL("/auth/error", getDomain(request)),
+ SEE_OTHER_REDIRECT_STATUS
+ );
+ }
+
+ const redirectResponse = NextResponse.redirect(
+ new URL("/", getDomain(request)),
+ SEE_OTHER_REDIRECT_STATUS
+ );
+ redirectResponse.headers.set("set-cookie", setCookieHeader);
+ return redirectResponse;
+};
diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts
index 1343f07ad21..ef487a8010f 100644
--- a/web/src/lib/constants.ts
+++ b/web/src/lib/constants.ts
@@ -1,5 +1,9 @@
-export const DISABLE_AUTH = process.env.DISABLE_AUTH?.toLowerCase() === "true";
+export type AuthType = "disabled" | "google_oauth" | "oidc" | "saml";
+export const AUTH_TYPE = (process.env.AUTH_TYPE ||
+ process.env.NEXT_PUBLIC_AUTH_TYPE ||
+ "disabled") as AuthType;
+export const DISABLE_AUTH = AUTH_TYPE === "disabled";
export const OAUTH_NAME = process.env.OAUTH_NAME || "Google";
export const INTERNAL_URL = process.env.INTERNAL_URL || "http://127.0.0.1:8080";
diff --git a/web/src/lib/user.ts b/web/src/lib/user.ts
index cccd7c72ec4..f4d67acff11 100644
--- a/web/src/lib/user.ts
+++ b/web/src/lib/user.ts
@@ -2,7 +2,7 @@ import { User } from "./types";
// should be used client-side only
export const getCurrentUser = async (): Promise => {
- const response = await fetch("/api/users/me", {
+ const response = await fetch("/api/manage/me", {
credentials: "include",
});
if (!response.ok) {
@@ -13,7 +13,7 @@ export const getCurrentUser = async (): Promise => {
};
export const logout = async (): Promise => {
- const response = await fetch("/api/auth/database/logout", {
+ const response = await fetch("/auth/logout", {
method: "POST",
credentials: "include",
});
diff --git a/web/src/lib/userSS.ts b/web/src/lib/userSS.ts
index bedaee71ece..306177effc7 100644
--- a/web/src/lib/userSS.ts
+++ b/web/src/lib/userSS.ts
@@ -2,8 +2,19 @@ import { cookies } from "next/headers";
import { User } from "./types";
import { buildUrl } from "./utilsSS";
import { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies";
+import { AUTH_TYPE } from "./constants";
-export const getGoogleOAuthUrlSS = async (): Promise => {
+const geOIDCAuthUrlSS = async (): Promise => {
+ const res = await fetch(buildUrl("/auth/oidc/authorize"));
+ if (!res.ok) {
+ throw new Error("Failed to fetch data");
+ }
+
+ const data: { authorization_url: string } = await res.json();
+ return data.authorization_url;
+};
+
+const getGoogleOAuthUrlSS = async (): Promise => {
const res = await fetch(buildUrl("/auth/oauth/authorize"));
if (!res.ok) {
throw new Error("Failed to fetch data");
@@ -13,10 +24,63 @@ export const getGoogleOAuthUrlSS = async (): Promise => {
return data.authorization_url;
};
-// should be used server-side only
+const getSAMLAuthUrlSS = async (): Promise => {
+ const res = await fetch(buildUrl("/auth/saml/authorize"));
+ if (!res.ok) {
+ throw new Error("Failed to fetch data");
+ }
+
+ const data: { authorization_url: string } = await res.json();
+ return data.authorization_url;
+};
+
+export const getAuthUrlSS = async (): Promise<[string, boolean]> => {
+ // Returns the auth url and whether or not we should auto-redirect
+ switch (AUTH_TYPE) {
+ case "disabled":
+ return ["", true];
+ case "google_oauth": {
+ return [await getGoogleOAuthUrlSS(), false];
+ }
+ case "saml": {
+ return [await getSAMLAuthUrlSS(), true];
+ }
+ case "oidc": {
+ return [await geOIDCAuthUrlSS(), true];
+ }
+ }
+};
+
+const logoutStandardSS = async (headers: Headers): Promise => {
+ return await fetch(buildUrl("/auth/logout"), {
+ method: "POST",
+ headers: headers,
+ });
+};
+
+const logoutSAMLSS = async (headers: Headers): Promise => {
+ return await fetch(buildUrl("/auth/saml/logout"), {
+ method: "POST",
+ headers: headers,
+ });
+};
+
+export const logoutSS = async (headers: Headers): Promise => {
+ switch (AUTH_TYPE) {
+ case "disabled":
+ return null;
+ case "saml": {
+ return await logoutSAMLSS(headers);
+ }
+ default: {
+ return await logoutStandardSS(headers);
+ }
+ }
+};
+
export const getCurrentUserSS = async (): Promise => {
try {
- const response = await fetch(buildUrl("/users/me"), {
+ const response = await fetch(buildUrl("/manage/me"), {
credentials: "include",
next: { revalidate: 0 },
headers: {