Skip to content

feat(nextjs): Introduce clerkMiddleware - #2404

Merged
nikosdouvlis merged 7 commits into
mainfrom
nikos/introduce-clerkmiddleware
Jan 3, 2024
Merged

feat(nextjs): Introduce clerkMiddleware#2404
nikosdouvlis merged 7 commits into
mainfrom
nikos/introduce-clerkmiddleware

Conversation

@nikosdouvlis

@nikosdouvlis nikosdouvlis commented Dec 19, 2023

Copy link
Copy Markdown
Member

Description

clerkMiddleware API:

export default clerkMiddleware(auth => {
  auth().protect();
  auth().protect({ permission: 'org:domains:delete' });
  auth().protect({ permission: 'org:domains:delete' }, { redirectUrl: 'https://clerk.com' });

  auth().redirectToSignIn();
  auth().redirectToSignIn({ returnBackUrl: 'https://clerk.com' });

  const { userId, redirectToSignIn } = auth();
  if (!userId) {
    return redirectToSignIn();
  }

  return NextResponse.next();
});

protect API:

The protect API can be used to protect pages, route handlers, server actions and any other request that can be intercepted using the middleware. It can be used to handle both unauthenticated and unauthorized requests.

Calling protect without arguments will catch all unauthenticated requests, and depending on the context it's used in, it will either redirect to the sign-in URL (for page requests) or throw a notFound error (for route handlers and server actions). If a redirectUrl is provided, protect will always respect it and redirect to it.

Calling protect with the same arguments the has API accepts, will catch all unauthorized requests, and throw a notFound error (for route handlers and server actions). If a redirectUrl is provided, protect will always respect it and redirect to it.

SDK-647

Checklist

  • npm test runs as expected.
  • npm run build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Packages affected

  • @clerk/backend
  • @clerk/chrome-extension
  • @clerk/clerk-js
  • @clerk/clerk-expo
  • @clerk/fastify
  • gatsby-plugin-clerk
  • @clerk/localizations
  • @clerk/nextjs
  • @clerk/clerk-react
  • @clerk/remix
  • @clerk/clerk-sdk-node
  • @clerk/shared
  • @clerk/themes
  • @clerk/types
  • build/tooling/chore

@changeset-bot

changeset-bot Bot commented Dec 19, 2023

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 832ca35

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@clerk/nextjs Patch
@clerk/elements Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/backend/src/tokens/clerkUrl.ts
Comment thread packages/nextjs/src/app-router/server/auth.ts
Comment thread packages/nextjs/src/pages/__tests__/index.test.tsx
Comment thread packages/nextjs/src/server/clerkMiddleware.ts
Comment thread packages/types/src/multiDomain.ts
Comment thread packages/nextjs/src/server/types.ts
const res = new Response(null, { status: 307, headers: requestState.headers });
return decorateResponseWithObservabilityHeaders(res, requestState);
} else if (requestState.status === AuthStatus.Handshake) {
throw new Error('Clerk: handshake status without redirect');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙃 let's use the errorThrower instead of Error.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nikosdouvlis Reminder ☝️

Comment thread packages/backend/src/tokens/clerkRequest.ts
Comment thread packages/nextjs/src/server/authMiddleware.ts
Comment thread packages/nextjs/src/server/clerkMiddleware.ts
switch (e.message) {
case PROTECT_REWRITE:
// Rewrite to bonus URL to force not found error
return NextResponse.rewrite(`${clerkRequest.clerkUrl.origin}/clerk_${Date.now()}`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 this might pollute a consumer's logs, it's probably fine but we should call this out in the docs.

Comment thread packages/nextjs/src/server/clerkMiddleware.ts
Comment thread packages/backend/src/tokens/clerkUrl.ts
Comment thread packages/types/src/multiDomain.ts Outdated
Comment thread packages/nextjs/src/server/protect.ts Outdated
@nikosdouvlis
nikosdouvlis force-pushed the nikos/introduce-clerkmiddleware branch from bb318b2 to 0939fda Compare December 22, 2023 16:44
Comment on lines +110 to +130
const isServerActionRequest = (req: Request) => {
return (
!!req.headers.get(nextConstants.Headers.NextUrl) &&
(req.headers.get(constants.Headers.Accept)?.includes('text/x-component') ||
req.headers.get(constants.Headers.ContentType)?.includes('multipart/form-data') ||
!!req.headers.get(nextConstants.Headers.NextAction))
);
};

const isPageRequest = (req: Request): boolean => {
return (
req.headers.get(constants.Headers.SecFetchDest) === 'document' ||
req.headers.get(constants.Headers.Accept)?.includes('text/html') ||
(!!req.headers.get(nextConstants.Headers.NextUrl) && !isServerActionRequest(req))
);
};

// In case we want to handle router handlers and server actions differently in the future
// const isRouteHandler = (req: Request) => {
// return !isPageRequest(req) && !isServerAction(req);
// };

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks to @panteliselef for taking the time to investigate, test, and help me simplify these checks 🥇

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove the commented out code?

@nikosdouvlis
nikosdouvlis force-pushed the nikos/introduce-clerkmiddleware branch from 0939fda to 2ba6cc6 Compare December 22, 2023 16:51
@nikosdouvlis
nikosdouvlis force-pushed the nikos/introduce-clerkmiddleware branch from 2ba6cc6 to 00c6a30 Compare January 3, 2024 01:12
@nikosdouvlis
nikosdouvlis force-pushed the nikos/introduce-clerkmiddleware branch from 00c6a30 to 8b7c324 Compare January 3, 2024 16:26
We're leveraging the isPageRequest and isServerActionRequest checks to detect whether the current request is a page request. For page requests, instead of throwing a 404 or 401, we're redirecting to SIGN_IN_URL automatically.

In order to achieve that, we're using native headers but also the `next-urk` and `next-action` headers of NextJS
@nikosdouvlis
nikosdouvlis force-pushed the nikos/introduce-clerkmiddleware branch from 8b7c324 to 832ca35 Compare January 3, 2024 17:12
@nikosdouvlis
nikosdouvlis merged commit 9a87ece into main Jan 3, 2024
@nikosdouvlis
nikosdouvlis deleted the nikos/introduce-clerkmiddleware branch January 3, 2024 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants