@clerk/nextjs
Version:
Clerk SDK for NextJS
1 lines • 13.3 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/server/utils.ts"],"sourcesContent":["import type { AuthenticateRequestOptions, ClerkRequest, RequestState } from '@clerk/backend/internal';\nimport { constants } from '@clerk/backend/internal';\nimport { isDevelopmentFromSecretKey } from '@clerk/shared/keys';\nimport { logger } from '@clerk/shared/logger';\nimport { isHttpOrHttps } from '@clerk/shared/proxy';\nimport { handleValueOrFn, isProductionEnvironment } from '@clerk/shared/utils';\nimport { NextResponse } from 'next/server';\n\nimport { constants as nextConstants } from '../constants';\nimport { canUseKeyless } from '../utils/feature-flags';\nimport { AES, HmacSHA1, Utf8 } from '../vendor/crypto-es';\nimport { DOMAIN, ENCRYPTION_KEY, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from './constants';\nimport {\n authSignatureInvalid,\n encryptionKeyInvalid,\n encryptionKeyInvalidDev,\n missingDomainAndProxy,\n missingSignInUrlInDev,\n} from './errors';\nimport { errorThrower } from './errorThrower';\nimport { detectClerkMiddleware } from './headers-utils';\nimport type { RequestLike } from './types';\n\nconst OVERRIDE_HEADERS = 'x-middleware-override-headers';\nconst MIDDLEWARE_HEADER_PREFIX = 'x-middleware-request' as string;\n\nexport const setRequestHeadersOnNextResponse = (\n res: NextResponse | Response,\n req: Request,\n newHeaders: Record<string, string>,\n) => {\n if (!res.headers.get(OVERRIDE_HEADERS)) {\n // Emulate a user setting overrides by explicitly adding the required nextjs headers\n // https://github.com/vercel/next.js/pull/41380\n // @ts-expect-error -- property keys does not exist on type Headers\n res.headers.set(OVERRIDE_HEADERS, [...req.headers.keys()]);\n req.headers.forEach((val, key) => {\n res.headers.set(`${MIDDLEWARE_HEADER_PREFIX}-${key}`, val);\n });\n }\n\n // Now that we have normalised res to include overrides, just append the new header\n Object.entries(newHeaders).forEach(([key, val]) => {\n res.headers.set(OVERRIDE_HEADERS, `${res.headers.get(OVERRIDE_HEADERS)},${key}`);\n res.headers.set(`${MIDDLEWARE_HEADER_PREFIX}-${key}`, val);\n });\n};\n\n// Auth result will be set as both a query param & header when applicable\nexport function decorateRequest(\n req: ClerkRequest,\n res: Response,\n requestState: RequestState,\n requestData: AuthenticateRequestOptions,\n keylessMode: Pick<AuthenticateRequestOptions, 'publishableKey' | 'secretKey'>,\n): Response {\n const { reason, message, status, token } = requestState;\n // pass-through case, convert to next()\n if (!res) {\n res = NextResponse.next();\n }\n\n // redirect() case, return early\n if (res.headers.get(nextConstants.Headers.NextRedirect)) {\n return res;\n }\n\n let rewriteURL;\n\n // next() case, convert to a rewrite\n if (res.headers.get(nextConstants.Headers.NextResume) === '1') {\n res.headers.delete(nextConstants.Headers.NextResume);\n rewriteURL = new URL(req.url);\n }\n\n // rewrite() case, set auth result only if origin remains the same\n const rewriteURLHeader = res.headers.get(nextConstants.Headers.NextRewrite);\n\n if (rewriteURLHeader) {\n const reqURL = new URL(req.url);\n rewriteURL = new URL(rewriteURLHeader);\n\n // if the origin has changed, return early\n if (rewriteURL.origin !== reqURL.origin) {\n return res;\n }\n }\n\n if (rewriteURL) {\n const clerkRequestData = encryptClerkRequestData(requestData, keylessMode);\n\n setRequestHeadersOnNextResponse(res, req, {\n [constants.Headers.AuthStatus]: status,\n [constants.Headers.AuthToken]: token || '',\n [constants.Headers.AuthSignature]: token\n ? createTokenSignature(token, requestData?.secretKey || SECRET_KEY || keylessMode.secretKey || '')\n : '',\n [constants.Headers.AuthMessage]: message || '',\n [constants.Headers.AuthReason]: reason || '',\n [constants.Headers.ClerkUrl]: req.clerkUrl.toString(),\n ...(clerkRequestData ? { [constants.Headers.ClerkRequestData]: clerkRequestData } : {}),\n });\n res.headers.set(nextConstants.Headers.NextRewrite, rewriteURL.href);\n }\n\n return res;\n}\n\nexport const handleMultiDomainAndProxy = (clerkRequest: ClerkRequest, opts: AuthenticateRequestOptions) => {\n const relativeOrAbsoluteProxyUrl = handleValueOrFn(opts?.proxyUrl, clerkRequest.clerkUrl, PROXY_URL);\n\n let proxyUrl;\n if (!!relativeOrAbsoluteProxyUrl && !isHttpOrHttps(relativeOrAbsoluteProxyUrl)) {\n proxyUrl = new URL(relativeOrAbsoluteProxyUrl, clerkRequest.clerkUrl).toString();\n } else {\n proxyUrl = relativeOrAbsoluteProxyUrl;\n }\n\n const isSatellite = handleValueOrFn(opts.isSatellite, new URL(clerkRequest.url), IS_SATELLITE);\n const domain = handleValueOrFn(opts.domain, new URL(clerkRequest.url), DOMAIN);\n const signInUrl = opts?.signInUrl || SIGN_IN_URL;\n\n if (isSatellite && !proxyUrl && !domain) {\n throw new Error(missingDomainAndProxy);\n }\n\n if (isSatellite && !isHttpOrHttps(signInUrl) && isDevelopmentFromSecretKey(opts.secretKey || SECRET_KEY)) {\n throw new Error(missingSignInUrlInDev);\n }\n\n return {\n proxyUrl,\n isSatellite,\n domain,\n signInUrl,\n };\n};\n\nexport const redirectAdapter = (url: string | URL) => {\n return NextResponse.redirect(url, { headers: { [constants.Headers.ClerkRedirectTo]: 'true' } });\n};\n\nexport function assertAuthStatus(req: RequestLike, error: string) {\n if (!detectClerkMiddleware(req)) {\n throw new Error(error);\n }\n}\n\nexport function assertKey(key: string | undefined, onError: () => never): string {\n if (!key) {\n onError();\n }\n\n return key;\n}\n\n/**\n * Compute a cryptographic signature from a session token and provided secret key. Used to validate that the token has not been modified when transferring between middleware and the Next.js origin.\n */\nfunction createTokenSignature(token: string, key: string): string {\n return HmacSHA1(token, key).toString();\n}\n\n/**\n * Assert that the provided token generates a matching signature.\n */\nexport function assertTokenSignature(token: string, key: string, signature?: string | null) {\n if (!signature) {\n throw new Error(authSignatureInvalid);\n }\n\n const expectedSignature = createTokenSignature(token, key);\n if (expectedSignature !== signature) {\n throw new Error(authSignatureInvalid);\n }\n}\n\nconst KEYLESS_ENCRYPTION_KEY = 'clerk_keyless_dummy_key';\n\n/**\n * Encrypt request data propagated between server requests.\n * @internal\n **/\nexport function encryptClerkRequestData(\n requestData: Partial<AuthenticateRequestOptions>,\n keylessMode: Pick<AuthenticateRequestOptions, 'publishableKey' | 'secretKey'>,\n) {\n const isEmpty = (obj: Record<string, any> | undefined) => {\n if (!obj) {\n return true;\n }\n return !Object.values(obj).some(v => v !== undefined);\n };\n\n if (isEmpty(requestData) && isEmpty(keylessMode)) {\n return;\n }\n\n if (requestData.secretKey && !ENCRYPTION_KEY) {\n // TODO SDK-1833: change this to an error in the next major version of `@clerk/nextjs`\n logger.warnOnce(\n 'Clerk: Missing `CLERK_ENCRYPTION_KEY`. Required for propagating `secretKey` middleware option. See docs: https://clerk.com/docs/references/nextjs/clerk-middleware#dynamic-keys',\n );\n\n return;\n }\n\n const maybeKeylessEncryptionKey = isProductionEnvironment()\n ? ENCRYPTION_KEY || assertKey(SECRET_KEY, () => errorThrower.throwMissingSecretKeyError())\n : ENCRYPTION_KEY || SECRET_KEY || KEYLESS_ENCRYPTION_KEY;\n\n return AES.encrypt(JSON.stringify({ ...keylessMode, ...requestData }), maybeKeylessEncryptionKey).toString();\n}\n\n/**\n * Decrypt request data propagated between server requests.\n * @internal\n */\nexport function decryptClerkRequestData(\n encryptedRequestData?: string | undefined | null,\n): Partial<AuthenticateRequestOptions> {\n if (!encryptedRequestData) {\n return {};\n }\n\n const maybeKeylessEncryptionKey = isProductionEnvironment()\n ? ENCRYPTION_KEY || SECRET_KEY\n : ENCRYPTION_KEY || SECRET_KEY || KEYLESS_ENCRYPTION_KEY;\n\n try {\n return decryptData(encryptedRequestData, maybeKeylessEncryptionKey);\n } catch {\n /**\n * There is a great chance when running in Keyless mode that the above fails,\n * because the keys hot-swapped and the Next.js dev server has not yet fully rebuilt middleware and routes.\n *\n * Attempt one more time with the default dummy value.\n */\n if (canUseKeyless) {\n try {\n return decryptData(encryptedRequestData, KEYLESS_ENCRYPTION_KEY);\n } catch {\n throwInvalidEncryptionKey();\n }\n }\n throwInvalidEncryptionKey();\n }\n}\n\nfunction throwInvalidEncryptionKey(): never {\n if (isProductionEnvironment()) {\n throw new Error(encryptionKeyInvalid);\n }\n throw new Error(encryptionKeyInvalidDev);\n}\n\nfunction decryptData(data: string, key: string) {\n const decryptedBytes = AES.decrypt(data, key);\n const encoded = decryptedBytes.toString(Utf8);\n return JSON.parse(encoded);\n}\n"],"mappings":"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","names":[]}