Skip to content

Commit

Permalink
add some todos
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity committed Oct 9, 2024
1 parent cb9c306 commit 9a8966e
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/ui/docs-bundle/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const middleware: NextMiddleware = async (request) => {
if (!withBasicTokenPublic(authConfig, pathname)) {
const destination = new URL(authConfig.redirect);
destination.searchParams.set("state", urlJoin(withDefaultProtocol(xFernHost), pathname));
// TODO: validate allowlist of domains to prevent open redirects
return NextResponse.redirect(destination);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { signFernJWT } from "@/server/auth/FernJWT";
import { withSecureCookie } from "@/server/auth/withSecure";
import { safeUrl } from "@/server/safeUrl";
import { getWorkOS, getWorkOSClientId } from "@/server/workos";
import { getXFernHostEdge, getXFernHostHeaderFallbackOrigin } from "@/server/xfernhost/edge";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
Expand All @@ -13,6 +14,7 @@ export const runtime = "edge";
function redirectWithLoginError(location: string, errorMessage: string): NextResponse {
const url = new URL(location);
url.searchParams.set("loginError", errorMessage);
// TODO: validate allowlist of domains to prevent open redirects
return NextResponse.redirect(url.toString());
}

Expand All @@ -27,7 +29,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
const state = req.nextUrl.searchParams.get("state");
const error = req.nextUrl.searchParams.get("error");
const error_description = req.nextUrl.searchParams.get("error_description");
const redirectLocation = state ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));
const redirectLocation = safeUrl(state) ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));

if (error != null) {
return redirectWithLoginError(redirectLocation, error_description ?? error);
Expand All @@ -53,6 +55,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
nextUrl.host = req.headers.get(HEADER_X_FERN_HOST)!;
}

// TODO: validate allowlist of domains to prevent open redirects
return NextResponse.redirect(nextUrl);
}

Expand All @@ -74,6 +77,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {

const token = await signFernJWT(fernUser, user);

// TODO: validate allowlist of domains to prevent open redirects
const res = NextResponse.redirect(redirectLocation);
res.cookies.set(COOKIE_FERN_TOKEN, token, withSecureCookie());
return res;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { verifyFernJWTConfig } from "@/server/auth/FernJWT";
import { withSecureCookie } from "@/server/auth/withSecure";
import { safeUrl } from "@/server/safeUrl";
import { getXFernHostEdge, getXFernHostHeaderFallbackOrigin } from "@/server/xfernhost/edge";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge-config";
Expand All @@ -11,6 +12,7 @@ export const runtime = "edge";
function redirectWithLoginError(location: string, errorMessage: string): NextResponse {
const url = new URL(location);
url.searchParams.set("loginError", errorMessage);
// TODO: validate allowlist of domains to prevent open redirects
return NextResponse.redirect(url.toString());
}

Expand All @@ -25,7 +27,7 @@ export default async function handler(req: NextRequest): Promise<NextResponse> {
// since we expect the callback to be redirected to, the token will be in the query params
const token = req.nextUrl.searchParams.get(COOKIE_FERN_TOKEN);
const state = req.nextUrl.searchParams.get("state");
const redirectLocation = state ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));
const redirectLocation = safeUrl(state) ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));

if (edgeConfig?.type !== "basic_token_verification" || token == null) {
// eslint-disable-next-line no-console
Expand All @@ -36,6 +38,7 @@ export default async function handler(req: NextRequest): Promise<NextResponse> {
try {
await verifyFernJWTConfig(token, edgeConfig);

// TODO: validate allowlist of domains to prevent open redirects
const res = NextResponse.redirect(redirectLocation);
res.cookies.set(COOKIE_FERN_TOKEN, token, withSecureCookie());
return res;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { safeUrl } from "@/server/safeUrl";
import { getXFernHostEdge, getXFernHostHeaderFallbackOrigin } from "@/server/xfernhost/edge";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge-config";
Expand All @@ -14,7 +15,8 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {

const state = req.nextUrl.searchParams.get("state");

const redirectLocation = logoutUrl ?? state ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));
const redirectLocation =
safeUrl(logoutUrl) ?? safeUrl(state) ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));

const res = NextResponse.redirect(redirectLocation);
res.cookies.delete(COOKIE_FERN_TOKEN);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const runtime = "edge";
export default async function handler(req: NextRequest): Promise<NextResponse> {
const email = req.nextUrl.searchParams.get(COOKIE_EMAIL);

// TODO: validate allowlist of domains to prevent open redirects
const res = NextResponse.redirect(withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req)));

if (email) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { signFernJWT } from "@/server/auth/FernJWT";
import { OAuth2Client } from "@/server/auth/OAuth2Client";
import { withSecureCookie } from "@/server/auth/withSecure";
import { safeUrl } from "@/server/safeUrl";
import { getXFernHostEdge, getXFernHostHeaderFallbackOrigin } from "@/server/xfernhost/edge";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { FernUser, OryAccessTokenSchema } from "@fern-ui/fern-docs-auth";
Expand All @@ -13,6 +14,7 @@ export const runtime = "edge";
function redirectWithLoginError(location: string, errorMessage: string): NextResponse {
const url = new URL(location);
url.searchParams.set("loginError", errorMessage);
// TODO: validate allowlist of domains to prevent open redirects
return NextResponse.redirect(url.toString());
}

Expand All @@ -27,7 +29,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
const state = req.nextUrl.searchParams.get("state");
const error = req.nextUrl.searchParams.get("error");
const error_description = req.nextUrl.searchParams.get("error_description");
const redirectLocation = state ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));
const redirectLocation = safeUrl(state) ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));

if (error != null) {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -60,6 +62,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
email: token.ext?.email,
};
const expires = token.exp == null ? undefined : new Date(token.exp * 1000);
// TODO: validate allowlist of domains to prevent open redirects
const res = NextResponse.redirect(redirectLocation);
res.cookies.set(COOKIE_FERN_TOKEN, await signFernJWT(fernUser), withSecureCookie({ expires }));
res.cookies.set(COOKIE_ACCESS_TOKEN, access_token, withSecureCookie({ expires }));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { withSecureCookie } from "@/server/auth/withSecure";
import { safeUrl } from "@/server/safeUrl";
import { getXFernHostEdge, getXFernHostHeaderFallbackOrigin } from "@/server/xfernhost/edge";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge-config";
Expand All @@ -10,6 +11,7 @@ export const runtime = "edge";
function redirectWithLoginError(location: string, errorMessage: string): NextResponse {
const url = new URL(location);
url.searchParams.set("loginError", errorMessage);
// TODO: validate allowlist of domains to prevent open redirects
return NextResponse.redirect(url.toString());
}

Expand All @@ -24,7 +26,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
const state = req.nextUrl.searchParams.get("state");
const error = req.nextUrl.searchParams.get("error");
const error_description = req.nextUrl.searchParams.get("error_description");
const redirectLocation = state ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));
const redirectLocation = safeUrl(state) ?? withDefaultProtocol(getXFernHostHeaderFallbackOrigin(req));

if (error != null) {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -54,6 +56,7 @@ export default async function GET(req: NextRequest): Promise<NextResponse> {
code,
});

// TODO: validate allowlist of domains to prevent open redirects
const res = NextResponse.redirect(redirectLocation);
res.cookies.set("access_token", accessToken, withSecureCookie());
return res;
Expand Down
18 changes: 18 additions & 0 deletions packages/ui/docs-bundle/src/server/safeUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { withDefaultProtocol } from "@fern-api/ui-core-utils";

export function safeUrl(url: string | null | undefined): string | undefined {
if (url == null) {
return undefined;
}

url = withDefaultProtocol(url);

try {
new URL(url);
return url;
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
return undefined;
}
}

0 comments on commit 9a8966e

Please sign in to comment.