@clerk/nextjs
Version:
Clerk SDK for NextJS
181 lines • 6.82 kB
JavaScript
import "../chunk-BUSYA2B4.js";
import { constants } from "@clerk/backend/internal";
import { isDevelopmentFromSecretKey } from "@clerk/shared/keys";
import { logger } from "@clerk/shared/logger";
import { isHttpOrHttps } from "@clerk/shared/proxy";
import { handleValueOrFn, isProductionEnvironment } from "@clerk/shared/utils";
import { NextResponse } from "next/server";
import { constants as nextConstants } from "../constants";
import { canUseKeyless } from "../utils/feature-flags";
import { AES, HmacSHA1, Utf8 } from "../vendor/crypto-es";
import { DOMAIN, ENCRYPTION_KEY, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from "./constants";
import {
authSignatureInvalid,
encryptionKeyInvalid,
encryptionKeyInvalidDev,
missingDomainAndProxy,
missingSignInUrlInDev
} from "./errors";
import { errorThrower } from "./errorThrower";
import { detectClerkMiddleware } from "./headers-utils";
const OVERRIDE_HEADERS = "x-middleware-override-headers";
const MIDDLEWARE_HEADER_PREFIX = "x-middleware-request";
const setRequestHeadersOnNextResponse = (res, req, newHeaders) => {
if (!res.headers.get(OVERRIDE_HEADERS)) {
res.headers.set(OVERRIDE_HEADERS, [...req.headers.keys()]);
req.headers.forEach((val, key) => {
res.headers.set(`${MIDDLEWARE_HEADER_PREFIX}-${key}`, val);
});
}
Object.entries(newHeaders).forEach(([key, val]) => {
res.headers.set(OVERRIDE_HEADERS, `${res.headers.get(OVERRIDE_HEADERS)},${key}`);
res.headers.set(`${MIDDLEWARE_HEADER_PREFIX}-${key}`, val);
});
};
function decorateRequest(req, res, requestState, requestData, keylessMode) {
const { reason, message, status, token } = requestState;
if (!res) {
res = NextResponse.next();
}
if (res.headers.get(nextConstants.Headers.NextRedirect)) {
return res;
}
let rewriteURL;
if (res.headers.get(nextConstants.Headers.NextResume) === "1") {
res.headers.delete(nextConstants.Headers.NextResume);
rewriteURL = new URL(req.url);
}
const rewriteURLHeader = res.headers.get(nextConstants.Headers.NextRewrite);
if (rewriteURLHeader) {
const reqURL = new URL(req.url);
rewriteURL = new URL(rewriteURLHeader);
if (rewriteURL.origin !== reqURL.origin) {
return res;
}
}
if (rewriteURL) {
const clerkRequestData = encryptClerkRequestData(requestData, keylessMode);
setRequestHeadersOnNextResponse(res, req, {
[constants.Headers.AuthStatus]: status,
[constants.Headers.AuthToken]: token || "",
[constants.Headers.AuthSignature]: token ? createTokenSignature(token, (requestData == null ? void 0 : requestData.secretKey) || SECRET_KEY || keylessMode.secretKey || "") : "",
[constants.Headers.AuthMessage]: message || "",
[constants.Headers.AuthReason]: reason || "",
[constants.Headers.ClerkUrl]: req.clerkUrl.toString(),
...clerkRequestData ? { [constants.Headers.ClerkRequestData]: clerkRequestData } : {}
});
res.headers.set(nextConstants.Headers.NextRewrite, rewriteURL.href);
}
return res;
}
const handleMultiDomainAndProxy = (clerkRequest, opts) => {
const relativeOrAbsoluteProxyUrl = handleValueOrFn(opts == null ? void 0 : opts.proxyUrl, clerkRequest.clerkUrl, PROXY_URL);
let proxyUrl;
if (!!relativeOrAbsoluteProxyUrl && !isHttpOrHttps(relativeOrAbsoluteProxyUrl)) {
proxyUrl = new URL(relativeOrAbsoluteProxyUrl, clerkRequest.clerkUrl).toString();
} else {
proxyUrl = relativeOrAbsoluteProxyUrl;
}
const isSatellite = handleValueOrFn(opts.isSatellite, new URL(clerkRequest.url), IS_SATELLITE);
const domain = handleValueOrFn(opts.domain, new URL(clerkRequest.url), DOMAIN);
const signInUrl = (opts == null ? void 0 : opts.signInUrl) || SIGN_IN_URL;
if (isSatellite && !proxyUrl && !domain) {
throw new Error(missingDomainAndProxy);
}
if (isSatellite && !isHttpOrHttps(signInUrl) && isDevelopmentFromSecretKey(opts.secretKey || SECRET_KEY)) {
throw new Error(missingSignInUrlInDev);
}
return {
proxyUrl,
isSatellite,
domain,
signInUrl
};
};
const redirectAdapter = (url) => {
return NextResponse.redirect(url, { headers: { [constants.Headers.ClerkRedirectTo]: "true" } });
};
function assertAuthStatus(req, error) {
if (!detectClerkMiddleware(req)) {
throw new Error(error);
}
}
function assertKey(key, onError) {
if (!key) {
onError();
}
return key;
}
function createTokenSignature(token, key) {
return HmacSHA1(token, key).toString();
}
function assertTokenSignature(token, key, signature) {
if (!signature) {
throw new Error(authSignatureInvalid);
}
const expectedSignature = createTokenSignature(token, key);
if (expectedSignature !== signature) {
throw new Error(authSignatureInvalid);
}
}
const KEYLESS_ENCRYPTION_KEY = "clerk_keyless_dummy_key";
function encryptClerkRequestData(requestData, keylessMode) {
const isEmpty = (obj) => {
if (!obj) {
return true;
}
return !Object.values(obj).some((v) => v !== void 0);
};
if (isEmpty(requestData) && isEmpty(keylessMode)) {
return;
}
if (requestData.secretKey && !ENCRYPTION_KEY) {
logger.warnOnce(
"Clerk: Missing `CLERK_ENCRYPTION_KEY`. Required for propagating `secretKey` middleware option. See docs: https://clerk.com/docs/references/nextjs/clerk-middleware#dynamic-keys"
);
return;
}
const maybeKeylessEncryptionKey = isProductionEnvironment() ? ENCRYPTION_KEY || assertKey(SECRET_KEY, () => errorThrower.throwMissingSecretKeyError()) : ENCRYPTION_KEY || SECRET_KEY || KEYLESS_ENCRYPTION_KEY;
return AES.encrypt(JSON.stringify({ ...keylessMode, ...requestData }), maybeKeylessEncryptionKey).toString();
}
function decryptClerkRequestData(encryptedRequestData) {
if (!encryptedRequestData) {
return {};
}
const maybeKeylessEncryptionKey = isProductionEnvironment() ? ENCRYPTION_KEY || SECRET_KEY : ENCRYPTION_KEY || SECRET_KEY || KEYLESS_ENCRYPTION_KEY;
try {
return decryptData(encryptedRequestData, maybeKeylessEncryptionKey);
} catch {
if (canUseKeyless) {
try {
return decryptData(encryptedRequestData, KEYLESS_ENCRYPTION_KEY);
} catch {
throwInvalidEncryptionKey();
}
}
throwInvalidEncryptionKey();
}
}
function throwInvalidEncryptionKey() {
if (isProductionEnvironment()) {
throw new Error(encryptionKeyInvalid);
}
throw new Error(encryptionKeyInvalidDev);
}
function decryptData(data, key) {
const decryptedBytes = AES.decrypt(data, key);
const encoded = decryptedBytes.toString(Utf8);
return JSON.parse(encoded);
}
export {
assertAuthStatus,
assertKey,
assertTokenSignature,
decorateRequest,
decryptClerkRequestData,
encryptClerkRequestData,
handleMultiDomainAndProxy,
redirectAdapter,
setRequestHeadersOnNextResponse
};
//# sourceMappingURL=utils.js.map