@clerk/nextjs
Version:
Clerk SDK for NextJS
219 lines • 9.19 kB
JavaScript
import "../chunk-BUSYA2B4.js";
import { AuthStatus, constants, createClerkRequest, createRedirect } from "@clerk/backend/internal";
import { eventMethodCalled } from "@clerk/shared/telemetry";
import { notFound as nextjsNotFound } from "next/navigation";
import { NextResponse } from "next/server";
import { isRedirect, serverRedirectWithAuth, setHeader } from "../utils";
import { withLogger } from "../utils/debugLogger";
import { canUseKeyless } from "../utils/feature-flags";
import { clerkClient } from "./clerkClient";
import { PUBLISHABLE_KEY, SECRET_KEY, SIGN_IN_URL, SIGN_UP_URL } from "./constants";
import { errorThrower } from "./errorThrower";
import { getKeylessCookieValue } from "./keyless";
import { clerkMiddlewareRequestDataStorage, clerkMiddlewareRequestDataStore } from "./middleware-storage";
import {
isNextjsNotFoundError,
isNextjsRedirectError,
isRedirectToSignInError,
nextjsRedirectError,
redirectToSignInError
} from "./nextErrors";
import { createProtect } from "./protect";
import {
assertKey,
decorateRequest,
handleMultiDomainAndProxy,
redirectAdapter,
setRequestHeadersOnNextResponse
} from "./utils";
const clerkMiddleware = (...args) => {
const [request, event] = parseRequestAndEvent(args);
const [handler, params] = parseHandlerAndOptions(args);
return clerkMiddlewareRequestDataStorage.run(clerkMiddlewareRequestDataStore, () => {
const baseNextMiddleware = withLogger("clerkMiddleware", (logger) => async (request2, event2) => {
const resolvedParams = typeof params === "function" ? params(request2) : params;
const keyless = await getKeylessCookieValue((name) => {
var _a;
return (_a = request2.cookies.get(name)) == null ? void 0 : _a.value;
});
const publishableKey = assertKey(
resolvedParams.publishableKey || PUBLISHABLE_KEY || (keyless == null ? void 0 : keyless.publishableKey),
() => errorThrower.throwMissingPublishableKeyError()
);
const secretKey = assertKey(
resolvedParams.secretKey || SECRET_KEY || (keyless == null ? void 0 : keyless.secretKey),
() => errorThrower.throwMissingSecretKeyError()
);
const signInUrl = resolvedParams.signInUrl || SIGN_IN_URL;
const signUpUrl = resolvedParams.signUpUrl || SIGN_UP_URL;
const options = {
publishableKey,
secretKey,
signInUrl,
signUpUrl,
...resolvedParams
};
clerkMiddlewareRequestDataStore.set("requestData", options);
const resolvedClerkClient = await clerkClient();
resolvedClerkClient.telemetry.record(
eventMethodCalled("clerkMiddleware", {
handler: Boolean(handler),
satellite: Boolean(options.isSatellite),
proxy: Boolean(options.proxyUrl)
})
);
if (options.debug) {
logger.enable();
}
const clerkRequest = createClerkRequest(request2);
logger.debug("options", options);
logger.debug("url", () => clerkRequest.toJSON());
const requestState = await resolvedClerkClient.authenticateRequest(
clerkRequest,
createAuthenticateRequestOptions(clerkRequest, options)
);
logger.debug("requestState", () => ({
status: requestState.status,
// @ts-expect-error : FIXME
headers: JSON.stringify(Object.fromEntries(requestState.headers)),
reason: requestState.reason
}));
const locationHeader = requestState.headers.get(constants.Headers.Location);
if (locationHeader) {
return new Response(null, { status: 307, headers: requestState.headers });
} else if (requestState.status === AuthStatus.Handshake) {
throw new Error("Clerk: handshake status without redirect");
}
const authObject = requestState.toAuth();
logger.debug("auth", () => ({ auth: authObject, debug: authObject.debug() }));
const redirectToSignIn = createMiddlewareRedirectToSignIn(clerkRequest);
const protect = await createMiddlewareProtect(clerkRequest, authObject, redirectToSignIn);
const authObjWithMethods = Object.assign(authObject, { redirectToSignIn });
const authHandler = () => Promise.resolve(authObjWithMethods);
authHandler.protect = protect;
let handlerResult = NextResponse.next();
try {
const userHandlerResult = await clerkMiddlewareRequestDataStorage.run(
clerkMiddlewareRequestDataStore,
async () => handler == null ? void 0 : handler(authHandler, request2, event2)
);
handlerResult = userHandlerResult || handlerResult;
} catch (e) {
handlerResult = handleControlFlowErrors(e, clerkRequest, request2, requestState);
}
if (requestState.headers) {
requestState.headers.forEach((value, key) => {
handlerResult.headers.append(key, value);
});
}
if (isRedirect(handlerResult)) {
logger.debug("handlerResult is redirect");
return serverRedirectWithAuth(clerkRequest, handlerResult, options);
}
if (options.debug) {
setRequestHeadersOnNextResponse(handlerResult, clerkRequest, { [constants.Headers.EnableDebug]: "true" });
}
decorateRequest(clerkRequest, handlerResult, requestState, resolvedParams, {
publishableKey: keyless == null ? void 0 : keyless.publishableKey,
secretKey: keyless == null ? void 0 : keyless.secretKey
});
return handlerResult;
});
const keylessMiddleware = async (request2, event2) => {
if (isKeylessSyncRequest(request2)) {
return returnBackFromKeylessSync(request2);
}
const resolvedParams = typeof params === "function" ? params(request2) : params;
const keyless = await getKeylessCookieValue((name) => {
var _a;
return (_a = request2.cookies.get(name)) == null ? void 0 : _a.value;
});
const isMissingPublishableKey = !(resolvedParams.publishableKey || PUBLISHABLE_KEY || (keyless == null ? void 0 : keyless.publishableKey));
if (isMissingPublishableKey) {
const res = NextResponse.next();
setRequestHeadersOnNextResponse(res, request2, {
[constants.Headers.AuthStatus]: "signed-out"
});
return res;
}
return baseNextMiddleware(request2, event2);
};
const nextMiddleware = async (request2, event2) => {
if (canUseKeyless) {
return keylessMiddleware(request2, event2);
}
return baseNextMiddleware(request2, event2);
};
if (request && event) {
return nextMiddleware(request, event);
}
return nextMiddleware;
});
};
const parseRequestAndEvent = (args) => {
return [args[0] instanceof Request ? args[0] : void 0, args[0] instanceof Request ? args[1] : void 0];
};
const parseHandlerAndOptions = (args) => {
return [
typeof args[0] === "function" ? args[0] : void 0,
(args.length === 2 ? args[1] : typeof args[0] === "function" ? {} : args[0]) || {}
];
};
const isKeylessSyncRequest = (request) => request.nextUrl.pathname === "/clerk-sync-keyless";
const returnBackFromKeylessSync = (request) => {
const returnUrl = request.nextUrl.searchParams.get("returnUrl");
const url = new URL(request.url);
url.pathname = "";
return NextResponse.redirect(returnUrl || url.toString());
};
const createAuthenticateRequestOptions = (clerkRequest, options) => {
return {
...options,
...handleMultiDomainAndProxy(clerkRequest, options)
};
};
const createMiddlewareRedirectToSignIn = (clerkRequest) => {
return (opts = {}) => {
const url = clerkRequest.clerkUrl.toString();
redirectToSignInError(url, opts.returnBackUrl);
};
};
const createMiddlewareProtect = (clerkRequest, authObject, redirectToSignIn) => {
return async (params, options) => {
const notFound = () => nextjsNotFound();
const redirect = (url) => nextjsRedirectError(url, {
redirectUrl: url
});
return createProtect({ request: clerkRequest, redirect, notFound, authObject, redirectToSignIn })(params, options);
};
};
const handleControlFlowErrors = (e, clerkRequest, nextRequest, requestState) => {
if (isNextjsNotFoundError(e)) {
return setHeader(
// This is an internal rewrite purely to trigger a not found error. We do not want Next.js to think that the
// destination URL is a valid page, so we use `nextRequest.url` as the base for the fake URL, which Next.js
// understands is an internal URL and won't run middleware against the request.
NextResponse.rewrite(new URL(`/clerk_${Date.now()}`, nextRequest.url)),
constants.Headers.AuthReason,
"protect-rewrite"
);
}
if (isRedirectToSignInError(e)) {
return createRedirect({
redirectAdapter,
baseUrl: clerkRequest.clerkUrl,
signInUrl: requestState.signInUrl,
signUpUrl: requestState.signUpUrl,
publishableKey: requestState.publishableKey
}).redirectToSignIn({ returnBackUrl: e.returnBackUrl });
}
if (isNextjsRedirectError(e)) {
return redirectAdapter(e.redirectUrl);
}
throw e;
};
export {
clerkMiddleware,
createAuthenticateRequestOptions
};
//# sourceMappingURL=clerkMiddleware.js.map