Skip to content

Commit

Permalink
[API] Better CORS handling (#525)
Browse files Browse the repository at this point in the history
* try dynamically updating middleware to set cors

* more explicit headers for options preflight request

* refactor

* comments
  • Loading branch information
stepandel authored Sep 28, 2024
1 parent 35286d0 commit 1e01b72
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 30 deletions.
25 changes: 0 additions & 25 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,6 @@ const nextConfig = {
async redirects() {
return [];
},
async headers() {
return [
{
source: "/api/(.*)",
headers: [
{
key: "Access-Control-Allow-Origin",
value: "*",
},
{
key: "Access-Control-Allow-Methods",
value: "GET, POST, PUT, DELETE, OPTIONS",
},
{
key: "Access-Control-Allow-Headers",
value: "Content-Type, Authorization",
},
{
key: "Access-Control-Allow-Credentials",
value: "true",
},
],
},
];
},
images: {
remotePatterns: [
{
Expand Down
72 changes: 67 additions & 5 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,60 @@ const API_PREFIX = "/api/v1";
const EXCLUDED_ROUTES_FROM_AUTH = ["/spec", "/auth/nonce", "/auth/verify"];
const ROOT_PATH = process.env.NEXT_PUBLIC_AGORA_ROOT || "/";

/*
CORS headers for authenticated API routes are handled poorly by Next
Since our API is open to the public, we need to set CORS headers to
allow all origins. This is done by setting preflight headers for OPTIONS
requests and standard headers for all other requests.
*/

function setOptionsCorsHeaders(request: Request) {
if (
request.headers.get("Origin") !== null &&
request.headers.get("Access-Control-Request-Method") !== null &&
request.headers.get("Access-Control-Request-Headers") !== null
) {
// Handle CORS preflight requests.
return new Response(null, {
headers: {
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS,UPDATE,DELETE",
"Access-Control-Max-Age": "86400",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Origin": request.headers.get("Origin")!,
"Access-Control-Allow-Headers": request.headers.get(
"Access-Control-Request-Headers"
)!,
},
});
} else {
// Handle standard OPTIONS request.
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, OPTIONS, UPDATE, DELETE",
},
});
}
}

function setCorsHeaders(request: NextRequest, response: Response) {
const requestHeaders = request.headers.get("Access-Control-Request-Headers");
if (requestHeaders) {
response.headers.set("Access-Control-Allow-Headers", requestHeaders);
}
// Set CORS headers
response.headers.set("Access-Control-Allow-Origin", "*");
response.headers.set(
"Access-Control-Allow-Methods",
"GET,HEAD,POST,OPTIONS,UPDATE,DELETE"
);
response.headers.set("Access-Control-Allow-Credentials", "true");
response.headers.set("Access-Control-Max-Age", "86400");
response.headers.set("content-type", "application/json");

return response;
}

/*
Middleware function to run on matching routes for config.matcher.
Expand All @@ -21,7 +75,11 @@ export async function middleware(request: NextRequest) {
return NextResponse.redirect(new URL(ROOT_PATH, request.url));
}

// TODO redundant check for API_PREFIX, consider removing, move to a sustainable pattern
// Handle preflight OPTIONS requests
if (request.method === "OPTIONS") {
return setOptionsCorsHeaders(request);
}

if (path.startsWith(API_PREFIX)) {
// validate bearer token for all api routes except excluded routes
if (
Expand All @@ -30,14 +88,18 @@ export async function middleware(request: NextRequest) {
)
) {
const authResponse = await validateBearerToken(request);
// TODO prisma client -> postgres db is currently not supported on edge
// runtime for vercel specifically; migrate API key check when it is
// TODO consider session/cookie
if (!authResponse.authenticated) {
return new Response(authResponse.failReason, { status: 401 });
return setCorsHeaders(
request,
new Response(authResponse.failReason, {
status: 401,
})
);
}
}
}

return setCorsHeaders(request, NextResponse.next());
}

export const config = {
Expand Down

0 comments on commit 1e01b72

Please sign in to comment.