Skip to content

Commit 96b9bb5

Browse files
authored
feat(nextjs): Accept redirectUrl as option for auth().protect() #2329 (#2333)
* feat(nextjs): Accept `redirectUrl` as option for `auth().protect()` * feat(nextjs): Allow for redirectUrl to be the first parameter * chore(nextjs): Update changeset
1 parent b4868ab commit 96b9bb5

File tree

2 files changed

+53
-14
lines changed

2 files changed

+53
-14
lines changed

.changeset/thirty-chicken-divide.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@clerk/nextjs': patch
3+
---
4+
5+
Accept `redirectUrl` as an option for `auth().protect()`.
6+
7+
For example:
8+
9+
```ts
10+
// Authorization
11+
auth().protect({ role:'org:admin' }, { redirectUrl: "/any-page" })
12+
auth().protect({ permission:'org:settings:manage' }, { redirectUrl: "/any-page" })
13+
14+
// Authentication
15+
auth().protect({ redirectUrl: "/any-page" })
16+
```

packages/nextjs/src/app-router/server/auth.ts

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {
33
CheckAuthorizationParamsWithCustomPermissions,
44
CheckAuthorizationWithCustomPermissions,
55
} from '@clerk/types';
6-
import { notFound } from 'next/navigation';
6+
import { notFound, redirect } from 'next/navigation';
77

88
import { authAuthHeaderMissing } from '../../server/errors';
99
import { buildClerkProps, createGetAuth } from '../../server/getAuth';
@@ -17,14 +17,23 @@ type AuthSignedIn = AuthObjectWithDeprecatedResources<
1717
* This function is experimental as it throws a Nextjs notFound error if user is not authenticated or authorized.
1818
* In the future we would investigate a way to throw a more appropriate error that clearly describes the not authorized of authenticated status.
1919
*/
20-
protect: (
21-
params?:
22-
| CheckAuthorizationParamsWithCustomPermissions
23-
| ((has: CheckAuthorizationWithCustomPermissions) => boolean),
24-
) => AuthObjectWithDeprecatedResources<SignedInAuthObject>;
20+
protect: {
21+
(
22+
params?:
23+
| CheckAuthorizationParamsWithCustomPermissions
24+
| ((has: CheckAuthorizationWithCustomPermissions) => boolean),
25+
options?: { redirectUrl: string },
26+
): AuthObjectWithDeprecatedResources<SignedInAuthObject>;
27+
28+
(params?: { redirectUrl: string }): AuthObjectWithDeprecatedResources<SignedInAuthObject>;
29+
};
2530
}
2631
>;
2732

33+
type ProtectGeneric = {
34+
protect: (params?: unknown, options?: unknown) => AuthObjectWithDeprecatedResources<SignedInAuthObject>;
35+
};
36+
2837
type AuthSignedOut = AuthObjectWithDeprecatedResources<
2938
SignedOutAuthObject & {
3039
/**
@@ -42,39 +51,53 @@ export const auth = () => {
4251
noAuthStatusMessage: authAuthHeaderMissing(),
4352
})(buildRequestLike());
4453

45-
(authObject as AuthSignedIn).protect = params => {
54+
(authObject as unknown as ProtectGeneric).protect = (params: any, options: any) => {
55+
const paramsOrFunction = params?.redirectUrl
56+
? undefined
57+
: (params as
58+
| CheckAuthorizationParamsWithCustomPermissions
59+
| ((has: CheckAuthorizationWithCustomPermissions) => boolean));
60+
const redirectUrl = (params?.redirectUrl || options?.redirectUrl) as string | undefined;
61+
62+
const handleUnauthorized = (): never => {
63+
if (redirectUrl) {
64+
redirect(redirectUrl);
65+
}
66+
notFound();
67+
};
68+
4669
/**
4770
* User is not authenticated
4871
*/
4972
if (!authObject.userId) {
50-
notFound();
73+
return handleUnauthorized();
5174
}
5275

5376
/**
5477
* User is authenticated
5578
*/
56-
if (!params) {
79+
if (!paramsOrFunction) {
5780
return { ...authObject };
5881
}
5982

6083
/**
6184
* if a function is passed and returns false then throw not found
6285
*/
63-
if (typeof params === 'function') {
64-
if (params(authObject.has)) {
86+
if (typeof paramsOrFunction === 'function') {
87+
if (paramsOrFunction(authObject.has)) {
6588
return { ...authObject };
6689
}
67-
notFound();
90+
return handleUnauthorized();
6891
}
6992

7093
/**
7194
* Checking if user is authorized when permission or role is passed
7295
*/
73-
if (authObject.has(params)) {
96+
if (authObject.has(paramsOrFunction)) {
7497
return { ...authObject };
7598
}
7699

77-
notFound();
100+
return handleUnauthorized();
78101
};
79102

80103
return authObject as AuthSignedIn | AuthSignedOut;

0 commit comments

Comments
 (0)