From 5ed38a7f751b103add5f2405c7387713e7d3f87f Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 12 Dec 2023 22:19:37 +0200 Subject: [PATCH 1/3] feat(nextjs): Accept `redirectUrl` as option for `auth().protect()` --- .changeset/thirty-chicken-divide.md | 5 +++++ packages/nextjs/src/app-router/server/auth.ts | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 .changeset/thirty-chicken-divide.md diff --git a/.changeset/thirty-chicken-divide.md b/.changeset/thirty-chicken-divide.md new file mode 100644 index 00000000000..fe3c7b55125 --- /dev/null +++ b/.changeset/thirty-chicken-divide.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Accept `redirectUrl` as option for `auth().protect()` diff --git a/packages/nextjs/src/app-router/server/auth.ts b/packages/nextjs/src/app-router/server/auth.ts index 350d93c9b0d..a22e42c72ee 100644 --- a/packages/nextjs/src/app-router/server/auth.ts +++ b/packages/nextjs/src/app-router/server/auth.ts @@ -3,7 +3,7 @@ import type { CheckAuthorizationParamsWithCustomPermissions, CheckAuthorizationWithCustomPermissions, } from '@clerk/types'; -import { notFound } from 'next/navigation'; +import { notFound, redirect } from 'next/navigation'; import { authAuthHeaderMissing } from '../../server/errors'; import { buildClerkProps, createGetAuth } from '../../server/getAuth'; @@ -21,6 +21,7 @@ type AuthSignedIn = AuthObjectWithDeprecatedResources< params?: | CheckAuthorizationParamsWithCustomPermissions | ((has: CheckAuthorizationWithCustomPermissions) => boolean), + options?: { redirectUrl: string }, ) => AuthObjectWithDeprecatedResources; } >; @@ -42,12 +43,18 @@ export const auth = () => { noAuthStatusMessage: authAuthHeaderMissing(), })(buildRequestLike()); - (authObject as AuthSignedIn).protect = params => { + (authObject as AuthSignedIn).protect = (params, options) => { + const handleUnauthorized = (): never => { + if (options?.redirectUrl) { + redirect(options.redirectUrl); + } + notFound(); + }; /** * User is not authenticated */ if (!authObject.userId) { - notFound(); + return handleUnauthorized(); } /** @@ -64,7 +71,7 @@ export const auth = () => { if (params(authObject.has)) { return { ...authObject }; } - notFound(); + return handleUnauthorized(); } /** @@ -74,7 +81,7 @@ export const auth = () => { return { ...authObject }; } - notFound(); + return handleUnauthorized(); }; return authObject as AuthSignedIn | AuthSignedOut; From e7ed29581cb63ca4ede789c98658693abc418f53 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 13 Dec 2023 11:18:57 +0200 Subject: [PATCH 2/3] feat(nextjs): Allow for redirectUrl to be the first parameter --- packages/nextjs/src/app-router/server/auth.ts | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/nextjs/src/app-router/server/auth.ts b/packages/nextjs/src/app-router/server/auth.ts index a22e42c72ee..43a4e87144a 100644 --- a/packages/nextjs/src/app-router/server/auth.ts +++ b/packages/nextjs/src/app-router/server/auth.ts @@ -17,15 +17,23 @@ type AuthSignedIn = AuthObjectWithDeprecatedResources< * This function is experimental as it throws a Nextjs notFound error if user is not authenticated or authorized. * In the future we would investigate a way to throw a more appropriate error that clearly describes the not authorized of authenticated status. */ - protect: ( - params?: - | CheckAuthorizationParamsWithCustomPermissions - | ((has: CheckAuthorizationWithCustomPermissions) => boolean), - options?: { redirectUrl: string }, - ) => AuthObjectWithDeprecatedResources; + protect: { + ( + params?: + | CheckAuthorizationParamsWithCustomPermissions + | ((has: CheckAuthorizationWithCustomPermissions) => boolean), + options?: { redirectUrl: string }, + ): AuthObjectWithDeprecatedResources; + + (params?: { redirectUrl: string }): AuthObjectWithDeprecatedResources; + }; } >; +type ProtectGeneric = { + protect: (params?: unknown, options?: unknown) => AuthObjectWithDeprecatedResources; +}; + type AuthSignedOut = AuthObjectWithDeprecatedResources< SignedOutAuthObject & { /** @@ -43,13 +51,21 @@ export const auth = () => { noAuthStatusMessage: authAuthHeaderMissing(), })(buildRequestLike()); - (authObject as AuthSignedIn).protect = (params, options) => { + (authObject as unknown as ProtectGeneric).protect = (params: any, options: any) => { + const paramsOrFunction = params?.redirectUrl + ? undefined + : (params as + | CheckAuthorizationParamsWithCustomPermissions + | ((has: CheckAuthorizationWithCustomPermissions) => boolean)); + const redirectUrl = (params?.redirectUrl || options?.redirectUrl) as string | undefined; + const handleUnauthorized = (): never => { - if (options?.redirectUrl) { - redirect(options.redirectUrl); + if (redirectUrl) { + redirect(redirectUrl); } notFound(); }; + /** * User is not authenticated */ @@ -60,15 +76,15 @@ export const auth = () => { /** * User is authenticated */ - if (!params) { + if (!paramsOrFunction) { return { ...authObject }; } /** * if a function is passed and returns false then throw not found */ - if (typeof params === 'function') { - if (params(authObject.has)) { + if (typeof paramsOrFunction === 'function') { + if (paramsOrFunction(authObject.has)) { return { ...authObject }; } return handleUnauthorized(); @@ -77,7 +93,7 @@ export const auth = () => { /** * Checking if user is authorized when permission or role is passed */ - if (authObject.has(params)) { + if (authObject.has(paramsOrFunction)) { return { ...authObject }; } From 3d9144dc7be93d0033144b97e5e92f78e42fd133 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 13 Dec 2023 11:59:01 +0200 Subject: [PATCH 3/3] chore(nextjs): Update changeset --- .changeset/thirty-chicken-divide.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.changeset/thirty-chicken-divide.md b/.changeset/thirty-chicken-divide.md index fe3c7b55125..8e8b8f22891 100644 --- a/.changeset/thirty-chicken-divide.md +++ b/.changeset/thirty-chicken-divide.md @@ -2,4 +2,15 @@ '@clerk/nextjs': patch --- -Accept `redirectUrl` as option for `auth().protect()` +Accept `redirectUrl` as an option for `auth().protect()`. + +For example: + +```ts +// Authorization +auth().protect({ role:'org:admin' }, { redirectUrl: "/any-page" }) +auth().protect({ permission:'org:settings:manage' }, { redirectUrl: "/any-page" }) + +// Authentication +auth().protect({ redirectUrl: "/any-page" }) +```