@clerk/express
Version:
Clerk server SDK for usage with Express
1 lines • 15.3 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/clerkClient.ts","../src/authenticateRequest.ts","../src/errors.ts","../src/clerkMiddleware.ts","../src/getAuth.ts","../src/requireAuth.ts"],"sourcesContent":["export * from '@clerk/backend';\n\nexport { clerkClient } from './clerkClient';\n\nexport type { ExpressRequestWithAuth } from './types';\nexport { clerkMiddleware } from './clerkMiddleware';\nexport { getAuth } from './getAuth';\nexport { requireAuth } from './requireAuth';\nexport { authenticateRequest } from './authenticateRequest';\n","import type { ClerkClient } from '@clerk/backend';\nimport { createClerkClient } from '@clerk/backend';\n\nimport { loadApiEnv, loadClientEnv } from './utils';\n\nlet clerkClientSingleton = {} as unknown as ClerkClient;\n\nexport const clerkClient = new Proxy(clerkClientSingleton, {\n get(_target, property: keyof ClerkClient) {\n if (property in clerkClientSingleton) {\n return clerkClientSingleton[property];\n }\n\n const env = { ...loadApiEnv(), ...loadClientEnv() };\n const client = createClerkClient({ ...env, userAgent: `${PACKAGE_NAME}@${PACKAGE_VERSION}` });\n\n // if the client is initialized properly, cache it to a singleton instance variable\n // in the next invocation the guard at the top will be triggered instead of creating another instance\n if (env.secretKey) {\n clerkClientSingleton = client;\n }\n\n return client[property];\n },\n set() {\n return false;\n },\n});\n","import type { RequestState } from '@clerk/backend/internal';\nimport { AuthStatus, createClerkRequest } from '@clerk/backend/internal';\nimport { deprecated } from '@clerk/shared/deprecated';\nimport { isDevelopmentFromSecretKey } from '@clerk/shared/keys';\nimport { isHttpOrHttps, isProxyUrlRelative, isValidProxyUrl } from '@clerk/shared/proxy';\nimport { handleValueOrFn } from '@clerk/shared/utils';\nimport type { RequestHandler, Response } from 'express';\n\nimport { clerkClient as defaultClerkClient } from './clerkClient';\nimport { satelliteAndMissingProxyUrlAndDomain, satelliteAndMissingSignInUrl } from './errors';\nimport type { AuthenticateRequestParams, ClerkMiddlewareOptions, ExpressRequestWithAuth } from './types';\nimport { incomingMessageToRequest, loadApiEnv, loadClientEnv } from './utils';\n\nexport const authenticateRequest = (opts: AuthenticateRequestParams) => {\n const { clerkClient, request, options } = opts;\n const { jwtKey, authorizedParties, audience, acceptsToken } = options || {};\n\n const clerkRequest = createClerkRequest(incomingMessageToRequest(request));\n const env = { ...loadApiEnv(), ...loadClientEnv() };\n\n const secretKey = options?.secretKey || env.secretKey;\n const publishableKey = options?.publishableKey || env.publishableKey;\n\n const isSatellite = handleValueOrFn(options?.isSatellite, clerkRequest.clerkUrl, env.isSatellite);\n const domain = handleValueOrFn(options?.domain, clerkRequest.clerkUrl) || env.domain;\n const signInUrl = options?.signInUrl || env.signInUrl;\n const proxyUrl = absoluteProxyUrl(\n handleValueOrFn(options?.proxyUrl, clerkRequest.clerkUrl, env.proxyUrl),\n clerkRequest.clerkUrl.toString(),\n );\n\n if (isSatellite && !proxyUrl && !domain) {\n throw new Error(satelliteAndMissingProxyUrlAndDomain);\n }\n\n if (isSatellite && !isHttpOrHttps(signInUrl) && isDevelopmentFromSecretKey(secretKey || '')) {\n throw new Error(satelliteAndMissingSignInUrl);\n }\n\n return clerkClient.authenticateRequest(clerkRequest, {\n audience,\n secretKey,\n publishableKey,\n jwtKey,\n authorizedParties,\n proxyUrl,\n isSatellite,\n domain,\n signInUrl,\n acceptsToken,\n });\n};\n\nconst setResponseHeaders = (requestState: RequestState, res: Response): Error | undefined => {\n if (requestState.headers) {\n requestState.headers.forEach((value, key) => res.appendHeader(key, value));\n }\n return setResponseForHandshake(requestState, res);\n};\n\n/**\n * Depending on the auth state of the request, handles applying redirects and validating that a handshake state was properly handled.\n *\n * Returns an error if state is handshake without a redirect, otherwise returns undefined. res.writableEnded should be checked after this method is called.\n */\nconst setResponseForHandshake = (requestState: RequestState, res: Response): Error | undefined => {\n const hasLocationHeader = requestState.headers.get('location');\n if (hasLocationHeader) {\n // triggering a handshake redirect\n res.status(307).end();\n return;\n }\n\n if (requestState.status === AuthStatus.Handshake) {\n return new Error('Clerk: unexpected handshake without redirect');\n }\n\n return;\n};\n\nconst absoluteProxyUrl = (relativeOrAbsoluteUrl: string, baseUrl: string): string => {\n if (!relativeOrAbsoluteUrl || !isValidProxyUrl(relativeOrAbsoluteUrl) || !isProxyUrlRelative(relativeOrAbsoluteUrl)) {\n return relativeOrAbsoluteUrl;\n }\n return new URL(relativeOrAbsoluteUrl, baseUrl).toString();\n};\n\nexport const authenticateAndDecorateRequest = (options: ClerkMiddlewareOptions = {}): RequestHandler => {\n const clerkClient = options.clerkClient || defaultClerkClient;\n const enableHandshake = options.enableHandshake ?? true;\n\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n const middleware: RequestHandler = async (request, response, next) => {\n if ((request as ExpressRequestWithAuth).auth) {\n return next();\n }\n\n try {\n const requestState = await authenticateRequest({\n clerkClient,\n request,\n options,\n });\n\n if (enableHandshake) {\n const err = setResponseHeaders(requestState, response);\n if (err) {\n return next(err);\n }\n if (response.writableEnded) {\n return;\n }\n }\n\n // TODO: For developers coming from the clerk-sdk-node package, we gave them examples\n // to use `req.auth` without calling it as a function. We need to keep this for backwards compatibility.\n const authHandler = (opts: Parameters<typeof requestState.toAuth>[0]) => requestState.toAuth(opts);\n const authObject = requestState.toAuth();\n\n const auth = new Proxy(authHandler, {\n get(target, prop, receiver) {\n deprecated('req.auth', 'Use `req.auth()` as a function instead.');\n // If the property exists on the function, return it\n if (prop in target) return Reflect.get(target, prop, receiver);\n // Otherwise, get it from the authObject\n return authObject?.[prop as keyof typeof authObject];\n },\n });\n\n Object.assign(request, { auth });\n\n next();\n } catch (err) {\n next(err);\n }\n };\n\n return middleware;\n};\n","const createErrorMessage = (msg: string) => {\n return `🔒 Clerk: ${msg.trim()}\n\n For more info, check out the docs: https://clerk.com/docs,\n or come say hi in our discord server: https://clerk.com/discord\n `;\n};\n\nexport const middlewareRequired = (fnName: string) =>\n createErrorMessage(`The \"clerkMiddleware\" should be registered before using \"${fnName}\".\nExample:\n\nimport express from 'express';\nimport { clerkMiddleware } from '@clerk/express';\n\nconst app = express();\napp.use(clerkMiddleware());\n`);\n\nexport const satelliteAndMissingProxyUrlAndDomain =\n 'Missing domain and proxyUrl. A satellite application needs to specify a domain or a proxyUrl';\nexport const satelliteAndMissingSignInUrl = `\nInvalid signInUrl. A satellite application requires a signInUrl for development instances.\nCheck if signInUrl is missing from your configuration or if it is not an absolute URL.`;\n","import type { RequestHandler } from 'express';\n\nimport { authenticateAndDecorateRequest } from './authenticateRequest';\nimport type { ClerkMiddlewareOptions } from './types';\n\n/**\n * Middleware that integrates Clerk authentication into your Express application.\n * It checks the request's cookies and headers for a session JWT and, if found,\n * attaches the Auth object to the request object under the `auth` key.\n *\n * @example\n * app.use(clerkMiddleware(options));\n *\n * @example\n * const clerkClient = createClerkClient({ ... });\n * app.use(clerkMiddleware({ clerkClient }));\n *\n * @example\n * app.use(clerkMiddleware());\n */\nexport const clerkMiddleware = (options: ClerkMiddlewareOptions = {}): RequestHandler => {\n const authMiddleware = authenticateAndDecorateRequest({\n ...options,\n acceptsToken: 'any',\n });\n\n return (request, response, next) => {\n authMiddleware(request, response, next);\n };\n};\n","import type { AuthenticateRequestOptions, GetAuthFn } from '@clerk/backend/internal';\nimport { getAuthObjectForAcceptedToken } from '@clerk/backend/internal';\nimport type { PendingSessionOptions } from '@clerk/types';\nimport type { Request as ExpressRequest } from 'express';\n\nimport { middlewareRequired } from './errors';\nimport { requestHasAuthObject } from './utils';\n\ntype GetAuthOptions = PendingSessionOptions & { acceptsToken?: AuthenticateRequestOptions['acceptsToken'] };\n\n/**\n * Retrieves the Clerk AuthObject using the current request object.\n *\n * @param {GetAuthOptions} options - Optional configuration for retriving auth object.\n * @returns {AuthObject} Object with information about the request state and claims.\n * @throws {Error} `clerkMiddleware` or `requireAuth` is required to be set in the middleware chain before this util is used.\n */\nexport const getAuth: GetAuthFn<ExpressRequest> = ((req: ExpressRequest, options?: GetAuthOptions) => {\n if (!requestHasAuthObject(req)) {\n throw new Error(middlewareRequired('getAuth'));\n }\n\n const authObject = req.auth(options);\n\n return getAuthObjectForAcceptedToken({ authObject, acceptsToken: options?.acceptsToken });\n}) as GetAuthFn<ExpressRequest>;\n","import type { RequestHandler } from 'express';\n\nimport { authenticateAndDecorateRequest } from './authenticateRequest';\nimport type { ClerkMiddlewareOptions, ExpressRequestWithAuth } from './types';\n\n/**\n * Middleware to require authentication for user requests.\n * Redirects unauthenticated requests to the sign-in url.\n *\n * @example\n * // Basic usage\n * import { requireAuth } from '@clerk/express'\n *\n * router.use(requireAuth())\n * //or\n * router.get('/path', requireAuth(), getHandler)\n *\n * @example\n * // Customizing the sign-in path\n * router.use(requireAuth({ signInUrl: '/sign-in' }))\n *\n * @example\n * // Combining with permission check\n * import { getAuth, requireAuth } from '@clerk/express'\n *\n * const hasPermission = (req, res, next) => {\n * const auth = getAuth(req)\n * if (!auth.has({ permission: 'permission' })) {\n * return res.status(403).send('Forbidden')\n * }\n * return next()\n * }\n * router.get('/path', requireAuth(), hasPermission, getHandler)\n */\nexport const requireAuth = (options: ClerkMiddlewareOptions = {}): RequestHandler => {\n const authMiddleware = authenticateAndDecorateRequest({\n ...options,\n acceptsToken: 'any',\n });\n\n return (request, response, next) => {\n authMiddleware(request, response, err => {\n if (err) {\n return next(err);\n }\n\n const signInUrl = options.signInUrl || process.env.CLERK_SIGN_IN_URL || '/';\n\n if (!(request as ExpressRequestWithAuth).auth()?.userId) {\n return response.redirect(signInUrl);\n }\n\n next();\n });\n };\n};\n"],"mappings":"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","names":["clerkClient"]}