UNPKG

@clerk/nextjs

Version:

Clerk SDK for NextJS

314 lines • 15.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var clerkMiddleware_exports = {}; __export(clerkMiddleware_exports, { clerkMiddleware: () => clerkMiddleware, createAuthenticateRequestOptions: () => createAuthenticateRequestOptions }); module.exports = __toCommonJS(clerkMiddleware_exports); var import_internal = require("@clerk/backend/internal"); var import_keys = require("@clerk/shared/keys"); var import_navigation = require("next/navigation"); var import_server = require("next/server"); var import_utils = require("../utils"); var import_debugLogger = require("../utils/debugLogger"); var import_feature_flags = require("../utils/feature-flags"); var import_clerkClient = require("./clerkClient"); var import_constants = require("./constants"); var import_content_security_policy = require("./content-security-policy"); var import_errorThrower = require("./errorThrower"); var import_headers_utils = require("./headers-utils"); var import_keyless = require("./keyless"); var import_middleware_storage = require("./middleware-storage"); var import_nextErrors = require("./nextErrors"); var import_protect = require("./protect"); var import_utils2 = require("./utils"); const clerkMiddleware = (...args) => { const [request, event] = parseRequestAndEvent(args); const [handler, params] = parseHandlerAndOptions(args); const middleware = import_middleware_storage.clerkMiddlewareRequestDataStorage.run(import_middleware_storage.clerkMiddlewareRequestDataStore, () => { const baseNextMiddleware = (0, import_debugLogger.withLogger)("clerkMiddleware", (logger) => async (request2, event2) => { var _a, _b; const resolvedParams = typeof params === "function" ? await params(request2) : params; const keyless = await (0, import_keyless.getKeylessCookieValue)((name) => { var _a2; return (_a2 = request2.cookies.get(name)) == null ? void 0 : _a2.value; }); const publishableKey = (0, import_utils2.assertKey)( resolvedParams.publishableKey || import_constants.PUBLISHABLE_KEY || (keyless == null ? void 0 : keyless.publishableKey), () => import_errorThrower.errorThrower.throwMissingPublishableKeyError() ); const secretKey = (0, import_utils2.assertKey)( resolvedParams.secretKey || import_constants.SECRET_KEY || (keyless == null ? void 0 : keyless.secretKey), () => import_errorThrower.errorThrower.throwMissingSecretKeyError() ); const signInUrl = resolvedParams.signInUrl || import_constants.SIGN_IN_URL; const signUpUrl = resolvedParams.signUpUrl || import_constants.SIGN_UP_URL; const options = { publishableKey, secretKey, signInUrl, signUpUrl, ...resolvedParams }; import_middleware_storage.clerkMiddlewareRequestDataStore.set("requestData", options); const resolvedClerkClient = await (0, import_clerkClient.clerkClient)(); if (options.debug) { logger.enable(); } const clerkRequest = (0, import_internal.createClerkRequest)(request2); logger.debug("options", options); logger.debug("url", () => clerkRequest.toJSON()); const authHeader = request2.headers.get(import_internal.constants.Headers.Authorization); if (authHeader && authHeader.startsWith("Basic ")) { logger.debug("Basic Auth detected"); } const cspHeader = request2.headers.get(import_internal.constants.Headers.ContentSecurityPolicy); if (cspHeader) { logger.debug("Content-Security-Policy detected", () => ({ value: cspHeader })); } 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(import_internal.constants.Headers.Location); if (locationHeader) { return new Response(null, { status: 307, headers: requestState.headers }); } else if (requestState.status === import_internal.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 redirectToSignUp = createMiddlewareRedirectToSignUp(clerkRequest); const protect = await createMiddlewareProtect(clerkRequest, authObject, redirectToSignIn); const authHandler = createMiddlewareAuthHandler(authObject, redirectToSignIn, redirectToSignUp); authHandler.protect = protect; let handlerResult = import_server.NextResponse.next(); try { const userHandlerResult = await import_middleware_storage.clerkMiddlewareRequestDataStorage.run( import_middleware_storage.clerkMiddlewareRequestDataStore, async () => handler == null ? void 0 : handler(authHandler, request2, event2) ); handlerResult = userHandlerResult || handlerResult; } catch (e) { handlerResult = handleControlFlowErrors(e, clerkRequest, request2, requestState); } if (options.contentSecurityPolicy) { const { headers } = (0, import_content_security_policy.createContentSecurityPolicyHeaders)( ((_b = (_a = (0, import_keys.parsePublishableKey)(publishableKey)) == null ? void 0 : _a.frontendApi) != null ? _b : "").replace("$", ""), options.contentSecurityPolicy ); headers.forEach(([key, value]) => { (0, import_utils.setHeader)(handlerResult, key, value); }); logger.debug("Clerk generated CSP", () => ({ headers })); } if (requestState.headers) { requestState.headers.forEach((value, key) => { if (key === import_internal.constants.Headers.ContentSecurityPolicy) { logger.debug("Content-Security-Policy detected", () => ({ value })); } handlerResult.headers.append(key, value); }); } if ((0, import_utils.isRedirect)(handlerResult)) { logger.debug("handlerResult is redirect"); return (0, import_utils.serverRedirectWithAuth)(clerkRequest, handlerResult, options); } if (options.debug) { (0, import_utils2.setRequestHeadersOnNextResponse)(handlerResult, clerkRequest, { [import_internal.constants.Headers.EnableDebug]: "true" }); } const keylessKeysForRequestData = ( // Only pass keyless credentials when there are no explicit keys secretKey === (keyless == null ? void 0 : keyless.secretKey) ? { publishableKey: keyless == null ? void 0 : keyless.publishableKey, secretKey: keyless == null ? void 0 : keyless.secretKey } : {} ); (0, import_utils2.decorateRequest)(clerkRequest, handlerResult, requestState, resolvedParams, keylessKeysForRequestData); return handlerResult; }); const keylessMiddleware = async (request2, event2) => { var _a, _b; if (isKeylessSyncRequest(request2)) { return returnBackFromKeylessSync(request2); } const resolvedParams = typeof params === "function" ? await params(request2) : params; const keyless = await (0, import_keyless.getKeylessCookieValue)((name) => { var _a2; return (_a2 = request2.cookies.get(name)) == null ? void 0 : _a2.value; }); const isMissingPublishableKey = !(resolvedParams.publishableKey || import_constants.PUBLISHABLE_KEY || (keyless == null ? void 0 : keyless.publishableKey)); const authHeader = (_b = (_a = (0, import_headers_utils.getHeader)(request2, import_internal.constants.Headers.Authorization)) == null ? void 0 : _a.replace("Bearer ", "")) != null ? _b : ""; if (isMissingPublishableKey && !(0, import_internal.isMachineTokenByPrefix)(authHeader)) { const res = import_server.NextResponse.next(); (0, import_utils2.setRequestHeadersOnNextResponse)(res, request2, { [import_internal.constants.Headers.AuthStatus]: "signed-out" }); return res; } return baseNextMiddleware(request2, event2); }; const nextMiddleware = async (request2, event2) => { if (import_feature_flags.canUseKeyless) { return keylessMiddleware(request2, event2); } return baseNextMiddleware(request2, event2); }; if (request && event) { return nextMiddleware(request, event); } return nextMiddleware; }); return middleware; }; 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 import_server.NextResponse.redirect(returnUrl || url.toString()); }; const createAuthenticateRequestOptions = (clerkRequest, options) => { return { ...options, ...(0, import_utils2.handleMultiDomainAndProxy)(clerkRequest, options), // TODO: Leaving the acceptsToken as 'any' opens up the possibility of // an economic attack. We should revisit this and only verify a token // when auth() or auth.protect() is invoked. acceptsToken: "any" }; }; const createMiddlewareRedirectToSignIn = (clerkRequest) => { return (opts = {}) => { const url = clerkRequest.clerkUrl.toString(); (0, import_nextErrors.redirectToSignInError)(url, opts.returnBackUrl); }; }; const createMiddlewareRedirectToSignUp = (clerkRequest) => { return (opts = {}) => { const url = clerkRequest.clerkUrl.toString(); (0, import_nextErrors.redirectToSignUpError)(url, opts.returnBackUrl); }; }; const createMiddlewareProtect = (clerkRequest, rawAuthObject, redirectToSignIn) => { return async (params, options) => { const notFound = () => (0, import_navigation.notFound)(); const redirect = (url) => (0, import_nextErrors.nextjsRedirectError)(url, { redirectUrl: url }); const requestedToken = (params == null ? void 0 : params.token) || (options == null ? void 0 : options.token) || import_internal.TokenType.SessionToken; const authObject = (0, import_internal.getAuthObjectForAcceptedToken)({ authObject: rawAuthObject, acceptsToken: requestedToken }); return (0, import_protect.createProtect)({ request: clerkRequest, redirect, notFound, unauthorized: import_nextErrors.unauthorized, authObject, redirectToSignIn })(params, options); }; }; const createMiddlewareAuthHandler = (rawAuthObject, redirectToSignIn, redirectToSignUp) => { const authHandler = async (options) => { var _a; const acceptsToken = (_a = options == null ? void 0 : options.acceptsToken) != null ? _a : import_internal.TokenType.SessionToken; const authObject = (0, import_internal.getAuthObjectForAcceptedToken)({ authObject: rawAuthObject, acceptsToken }); if (authObject.tokenType === import_internal.TokenType.SessionToken && (0, import_internal.isTokenTypeAccepted)(import_internal.TokenType.SessionToken, acceptsToken)) { return Object.assign(authObject, { redirectToSignIn, redirectToSignUp }); } return authObject; }; return authHandler; }; const handleControlFlowErrors = (e, clerkRequest, nextRequest, requestState) => { var _a; if ((0, import_nextErrors.isNextjsUnauthorizedError)(e)) { const response = new import_server.NextResponse(null, { status: 401 }); const authObject = requestState.toAuth(); if (authObject && authObject.tokenType === import_internal.TokenType.OAuthToken) { const publishableKey = (0, import_keys.parsePublishableKey)(requestState.publishableKey); return (0, import_utils.setHeader)( response, "WWW-Authenticate", `Bearer resource_metadata="https://${publishableKey == null ? void 0 : publishableKey.frontendApi}/.well-known/oauth-protected-resource"` ); } return response; } if ((0, import_nextErrors.isNextjsNotFoundError)(e)) { return (0, import_utils.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. import_server.NextResponse.rewrite(new URL(`/clerk_${Date.now()}`, nextRequest.url)), import_internal.constants.Headers.AuthReason, "protect-rewrite" ); } const isRedirectToSignIn = (0, import_nextErrors.isRedirectToSignInError)(e); const isRedirectToSignUp = (0, import_nextErrors.isRedirectToSignUpError)(e); if (isRedirectToSignIn || isRedirectToSignUp) { const redirect = (0, import_internal.createRedirect)({ redirectAdapter: import_utils2.redirectAdapter, baseUrl: clerkRequest.clerkUrl, signInUrl: requestState.signInUrl, signUpUrl: requestState.signUpUrl, publishableKey: requestState.publishableKey, sessionStatus: (_a = requestState.toAuth()) == null ? void 0 : _a.sessionStatus }); const { returnBackUrl } = e; return redirect[isRedirectToSignIn ? "redirectToSignIn" : "redirectToSignUp"]({ returnBackUrl }); } if ((0, import_nextErrors.isNextjsRedirectError)(e)) { return (0, import_utils2.redirectAdapter)(e.redirectUrl); } throw e; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { clerkMiddleware, createAuthenticateRequestOptions }); //# sourceMappingURL=clerkMiddleware.js.map