Consolidate versions for easier extension (#495)

This commit is contained in:
Yuhong Sun
2023-10-01 23:49:38 -07:00
committed by GitHub
parent a808c733b8
commit 351475de28
29 changed files with 530 additions and 278 deletions

View File

@@ -30,6 +30,9 @@ ENV NEXT_TELEMETRY_DISABLED 1
ARG NEXT_PUBLIC_DISABLE_STREAMING
ENV NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING}
ARG NEXT_PUBLIC_AUTH_TYPE
ENV NEXT_PUBLIC_AUTH_TYPE=${NEXT_PUBLIC_AUTH_TYPE}
RUN npm run build
# Step 3. Production image, copy all the files and run next
@@ -65,6 +68,9 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
ARG NEXT_PUBLIC_DISABLE_STREAMING
ENV NEXT_PUBLIC_DISABLE_STREAMING=${NEXT_PUBLIC_DISABLE_STREAMING}
ARG NEXT_PUBLIC_AUTH_TYPE
ENV NEXT_PUBLIC_AUTH_TYPE=${NEXT_PUBLIC_AUTH_TYPE}
# Note: Don't expose ports here, Compose will handle that for us if necessary.
# If you want to run this without compose, specify the ports to
# expose via cli

View File

@@ -1,23 +0,0 @@
import { getDomain } from "@/lib/redirectSS";
import { buildUrl } from "@/lib/utilsSS";
import { NextRequest, NextResponse } from "next/server";
export const GET = async (request: NextRequest) => {
// Wrapper around the FastAPI endpoint /auth/google/callback,
// which adds back a redirect to the main app.
const url = new URL(buildUrl("/auth/google/callback"));
url.search = request.nextUrl.search;
const response = await fetch(url.toString());
const setCookieHeader = response.headers.get("set-cookie");
if (!setCookieHeader) {
return NextResponse.redirect(new URL("/auth/error", getDomain(request)));
}
const redirectResponse = NextResponse.redirect(
new URL("/", getDomain(request))
);
redirectResponse.headers.set("set-cookie", setCookieHeader);
return redirectResponse;
};

View File

@@ -1,7 +1,7 @@
import { HealthCheckBanner } from "@/components/health/healthcheck";
import { DISABLE_AUTH, OAUTH_NAME } from "@/lib/constants";
import { User } from "@/lib/types";
import { getGoogleOAuthUrlSS, getCurrentUserSS } from "@/lib/userSS";
import { getCurrentUserSS, getAuthUrlSS } from "@/lib/userSS";
import { redirect } from "next/navigation";
const BUTTON_STYLE =
@@ -21,10 +21,11 @@ const Page = async () => {
// will not render
let currentUser: User | null = null;
let authorizationUrl: string | null = null;
let autoRedirect: boolean = false;
try {
[currentUser, authorizationUrl] = await Promise.all([
[currentUser, [authorizationUrl, autoRedirect]] = await Promise.all([
getCurrentUserSS(),
getGoogleOAuthUrlSS(),
getAuthUrlSS(),
]);
} catch (e) {
console.log(`Some fetch failed for the login page - ${e}`);
@@ -35,6 +36,10 @@ const Page = async () => {
return redirect("/");
}
if (autoRedirect && authorizationUrl) {
return redirect(authorizationUrl);
}
return (
<main>
<div className="absolute top-10x w-full">

View File

@@ -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);
};

View File

@@ -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;
};

View File

@@ -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";

View File

@@ -2,7 +2,7 @@ import { User } from "./types";
// should be used client-side only
export const getCurrentUser = async (): Promise<User | null> => {
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<User | null> => {
};
export const logout = async (): Promise<boolean> => {
const response = await fetch("/api/auth/database/logout", {
const response = await fetch("/auth/logout", {
method: "POST",
credentials: "include",
});

View File

@@ -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<string> => {
const geOIDCAuthUrlSS = async (): Promise<string> => {
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<string> => {
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<string> => {
return data.authorization_url;
};
// should be used server-side only
const getSAMLAuthUrlSS = async (): Promise<string> => {
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<Response> => {
return await fetch(buildUrl("/auth/logout"), {
method: "POST",
headers: headers,
});
};
const logoutSAMLSS = async (headers: Headers): Promise<Response> => {
return await fetch(buildUrl("/auth/saml/logout"), {
method: "POST",
headers: headers,
});
};
export const logoutSS = async (headers: Headers): Promise<Response | null> => {
switch (AUTH_TYPE) {
case "disabled":
return null;
case "saml": {
return await logoutSAMLSS(headers);
}
default: {
return await logoutStandardSS(headers);
}
}
};
export const getCurrentUserSS = async (): Promise<User | null> => {
try {
const response = await fetch(buildUrl("/users/me"), {
const response = await fetch(buildUrl("/manage/me"), {
credentials: "include",
next: { revalidate: 0 },
headers: {