Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nextjs,clerk-react): Experimental protectComponent, protectAction, protectRoute #4267

Closed
wants to merge 83 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
cc45755
WIP
panteliselef Sep 8, 2024
243e846
useAssurance internally
panteliselef Sep 9, 2024
2b186f6
minor cleanup
panteliselef Sep 9, 2024
0bef2f6
fix tests
panteliselef Sep 9, 2024
ec63553
remove search params
panteliselef Sep 10, 2024
4d4eeca
Merge branch 'refs/heads/main' into elef/user-665-display-userverific…
panteliselef Sep 10, 2024
1a6446a
Merge branch 'refs/heads/main' into elef/user-665-display-userverific…
panteliselef Sep 16, 2024
374146a
refactor
panteliselef Sep 16, 2024
678a2c6
clean up assurance
panteliselef Sep 16, 2024
85c5cd4
Merge branch 'refs/heads/main' into elef/user-665-display-userverific…
panteliselef Sep 16, 2024
9ae4a62
fix localizations
panteliselef Sep 16, 2024
f025a26
add changeset
panteliselef Sep 16, 2024
274ef1d
update bundlewatch.config.json
panteliselef Sep 16, 2024
0b02ad4
Merge branch 'refs/heads/main' into elef/user-665-display-userverific…
panteliselef Sep 16, 2024
68d078a
update localization
panteliselef Sep 16, 2024
319fab0
update changelog
panteliselef Sep 16, 2024
f5099c8
Merge branch 'main' into elef/user-665-display-userverification-for-s…
panteliselef Sep 16, 2024
fec40a1
remove portals from all menus/select components
panteliselef Sep 18, 2024
3d7a9c5
Merge branch 'main' into elef/user-665-display-userverification-for-s…
panteliselef Sep 18, 2024
4d5a871
restore portals and drop zIndex
panteliselef Sep 18, 2024
063509a
avoid using arbitrary zIndexes
panteliselef Sep 18, 2024
8280af9
Revert "avoid using arbitrary zIndexes"
panteliselef Sep 18, 2024
df6f3a1
Revert "restore portals and drop zIndex"
panteliselef Sep 18, 2024
8f6a7d2
Revert "remove portals from all menus/select components"
panteliselef Sep 18, 2024
8ea72f8
revert changes to indexes and use isolate instead
panteliselef Sep 18, 2024
3b427c1
Server side `protect()`
panteliselef Sep 19, 2024
71f4dfe
Client side `protect()`
panteliselef Sep 19, 2024
7fe540b
allow for easier debugging
panteliselef Sep 19, 2024
7410f07
clerk-js: fix weird bug where afterVerification would not be called b…
panteliselef Sep 19, 2024
e3a7bad
Add useAssurance
panteliselef Sep 19, 2024
5ad3efe
Update protect() to work with types from useAssurance
panteliselef Sep 19, 2024
771e92b
wip
panteliselef Sep 19, 2024
d463d10
Inject auth to .component w/ correct types
panteliselef Sep 20, 2024
2a265b3
Inject auth to .action w/ correct types
panteliselef Sep 20, 2024
db0af27
support json responses
panteliselef Sep 21, 2024
3d39db7
improve types
panteliselef Sep 23, 2024
4ac57d6
add `unstable_notify` to `__experimental_closeUserVerification`
panteliselef Sep 26, 2024
4554d8b
fallback modal
panteliselef Sep 26, 2024
bc85b66
create protectComponent
panteliselef Sep 27, 2024
12df638
Rebrand assurance to reverification
panteliselef Sep 30, 2024
56daee7
protectAction and protectComponent for client and protectRoute
panteliselef Sep 30, 2024
21b224d
make sure the new protectComponent works as the old utils
panteliselef Oct 1, 2024
33f11f7
Merge branch 'refs/heads/main' into elef/user-722-create-the-new-protect
panteliselef Oct 1, 2024
6e762a7
add e2e tests for protectAction
panteliselef Oct 2, 2024
3023028
add e2e tests for protectRoute
panteliselef Oct 2, 2024
271ddc5
Merge branch 'refs/heads/main' into elef/user-722-create-the-new-protect
panteliselef Oct 2, 2024
1a5a8ee
use __experimental_SessionVerificationMaxAgeMinutes instead of __expe…
panteliselef Oct 2, 2024
baedbdb
add changeset
panteliselef Oct 2, 2024
26ac152
Merge branch 'refs/heads/main' into elef/user-722-create-the-new-protect
panteliselef Oct 2, 2024
3780eb4
cleanup e2e tests
panteliselef Oct 2, 2024
0047cbb
remove unused code
panteliselef Oct 2, 2024
f3d182b
update snapshot
panteliselef Oct 2, 2024
2244583
bring back delete pages
panteliselef Oct 2, 2024
c12895c
bring back delete pages
panteliselef Oct 3, 2024
54f1f91
move complementary-components to shared
panteliselef Oct 3, 2024
f8f839e
drop useAssurance
panteliselef Oct 3, 2024
be216de
remove unused code
panteliselef Oct 3, 2024
ba02f9a
clean up shared logic
panteliselef Oct 3, 2024
77c58d3
update snapshot for chrome-ext
panteliselef Oct 3, 2024
8c336ab
cleanup unused code
panteliselef Oct 3, 2024
9792e24
rollback change on useAfterVerification
panteliselef Oct 3, 2024
ca24369
export all as experimental
panteliselef Oct 3, 2024
4a49bf3
enable serverActions
panteliselef Oct 3, 2024
cf460ff
remove console.log
panteliselef Oct 3, 2024
c16bcd8
add changesets
panteliselef Oct 3, 2024
8a7c218
increase timeout
panteliselef Oct 3, 2024
4b920b8
increase timeout
panteliselef Oct 3, 2024
cd663f8
utilities for generating the authorization error objects
panteliselef Oct 3, 2024
5dd706f
wip
panteliselef Oct 4, 2024
f41aae5
wip 2
panteliselef Oct 4, 2024
3849f69
wip 3
panteliselef Oct 4, 2024
f08e4e1
wip 4
panteliselef Oct 4, 2024
e805507
final
panteliselef Oct 4, 2024
538ce80
change tests waittime
panteliselef Oct 7, 2024
143c009
fix redirect back on modal
panteliselef Oct 7, 2024
a02b70b
add type tests for protectAction
panteliselef Oct 8, 2024
721c3bb
clean up protectAction.ts
panteliselef Oct 8, 2024
0a416a8
builder + correct types for protectComponent + update e2e tests
panteliselef Oct 8, 2024
698eaa9
cleanup server protectComponent
panteliselef Oct 8, 2024
e9b6227
add type tests for protectRoute
panteliselef Oct 8, 2024
0e45319
protectAction type tests cleanup
panteliselef Oct 8, 2024
ca869e2
protectAction returns an async function
panteliselef Oct 8, 2024
cdf1f7d
bump duration
panteliselef Oct 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/cold-mails-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@clerk/types": patch
"@clerk/clerk-js": patch
---

Updated `__experimental_closeUserVerification` to accept an "unstable_notify" property
8 changes: 8 additions & 0 deletions .changeset/dirty-moles-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@clerk/nextjs": minor
---

Exports a new set of experimental authorization helpers:
- `__experimental_protectComponent` (HoC)
- `__experimental_protectAction` (HoF)
- `__experimental_protectRoute` (HoF)
9 changes: 9 additions & 0 deletions .changeset/green-buckets-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@clerk/shared": minor
---

Export two React component, which are used internally from `__experimental_protectComponent`:
- UserVerificationModal
- UserVerificationTrigger

Export a new utility `__internal_findFailedProtectConfiguration` for shared usage across @clerk/clerk-react and @clerk/nextjs
5 changes: 5 additions & 0 deletions .changeset/ninety-islands-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/clerk-react": minor
---

Exports a new `__experimental_protectComponent` HoC.
2 changes: 1 addition & 1 deletion integration/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const common: PlaywrightTestConfig = {
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
timeout: 90000,
timeout: 9000000,
maxFailures: process.env.CI ? 1 : undefined,
workers: process.env.CI ? '50%' : '70%',
reporter: process.env.CI ? 'line' : 'list',
Expand Down
8 changes: 8 additions & 0 deletions integration/presets/envs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ const withCustomRoles = base
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-custom-roles').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-custom-roles').pk);

const withReverification = base
.clone()
.setId('withReverification')
.setEnvVariable('private', 'CLERK_SECRET_KEY', instanceKeys.get('with-reverification').sk)
.setEnvVariable('public', 'CLERK_PUBLISHABLE_KEY', instanceKeys.get('with-reverification').pk)
.setEnvVariable('private', 'CLERK_ENCRYPTION_KEY', constants.E2E_CLERK_ENCRYPTION_KEY || 'a-key');

const withEmailCodesQuickstart = withEmailCodes
.clone()
.setEnvVariable('public', 'CLERK_SIGN_IN_URL', '')
Expand Down Expand Up @@ -100,6 +107,7 @@ export const envs = {
withEmailCodes_destroy_client,
withEmailLinks,
withCustomRoles,
withReverification,
withEmailCodesQuickstart,
withAPCore1ClerkLatest,
withAPCore1ClerkV4,
Expand Down
1 change: 1 addition & 0 deletions integration/presets/longRunningApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const createLongRunningApps = () => {
env: envs.withEmailCodes_destroy_client,
},
{ id: 'next.appRouter.withCustomRoles', config: next.appRouter, env: envs.withCustomRoles },
{ id: 'next.appRouter.withReverification', config: next.appRouter, env: envs.withReverification },
{ id: 'quickstart.next.appRouter', config: next.appRouterQuickstart, env: envs.withEmailCodesQuickstart },
{ id: 'elements.next.appRouter', config: elements.nextAppRouter, env: envs.withEmailCodes },
{ id: 'astro.node.withCustomRoles', config: astro.node, env: envs.withCustomRoles },
Expand Down
3 changes: 3 additions & 0 deletions integration/templates/next-app-router/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
serverActions: true,
},
};

module.exports = nextConfig;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { __experimental_protectRoute } from '@clerk/nextjs/server';

export const POST = __experimental_protectRoute()
.with({
role: 'org:admin',
})
.with({
reverification: {
level: 'secondFactor',
afterMinutes: 1,
},
})
.route(auth => {
return new Response(
JSON.stringify({
userId: auth.userId,
}),
{ status: 200 },
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { __experimental_protectRoute } from '@clerk/nextjs/server';

export const POST = __experimental_protectRoute()
.with({
reverification: {
level: 'secondFactor',
afterMinutes: 1,
},
})
.route(auth => {
return new Response(
JSON.stringify({
userId: auth.userId,
}),
{ status: 200 },
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { __experimental_protectRoute } from '@clerk/nextjs/server';

export const POST = __experimental_protectRoute()
.with({
role: 'org:admin',
})
.route(auth => {
return new Response(
JSON.stringify({
userId: auth.userId,
}),
{ status: 200 },
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { __experimental_protectRoute } from '@clerk/nextjs/server';

export const POST = __experimental_protectRoute().route(auth => {
return new Response(
JSON.stringify({
userId: auth.userId,
}),
{ status: 200 },
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { __experimental_protectRoute } from '@clerk/nextjs/server';

// export function GET() {
// const { userId } = auth().protect(has => has({ role: 'admin' }) || has({ role: 'org:editor' }));
// return new Response(JSON.stringify({ userId }));
// }

export const GET = __experimental_protectRoute()
.with({
role: 'org:admin',
})
.route(_auth => {
const { userId } = _auth;
return new Response(JSON.stringify({ userId }));
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use server';
import { __experimental_protectAction } from '@clerk/nextjs/server';

const logUserIdAction = __experimental_protectAction().action(auth => {
return {
userId: auth.userId,
};
});

const logUserIdActionRole = __experimental_protectAction()
.with({
role: 'org:admin',
})
.action(auth => {
return {
userId: auth.userId,
};
});

const logUserIdActionReverification = __experimental_protectAction()
.with({
reverification: {
level: 'secondFactor',
afterMinutes: 1,
},
})
.action(auth => {
return {
userId: auth.userId,
};
});

const logUserIdActionStack = __experimental_protectAction()
.with({
role: 'org:admin',
})
.with({
reverification: {
level: 'secondFactor',
afterMinutes: 1,
},
})
.action(auth => {
return {
userId: auth.userId,
};
});

export { logUserIdAction, logUserIdActionRole, logUserIdActionReverification, logUserIdActionStack };
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';
import { useState, useTransition } from 'react';

export function PageComponent({ action }: { action: () => Promise<any> }) {
const [pending, startTransition] = useTransition();
const [res, setRes] = useState(null);

return (
<>
<button
disabled={pending}
onClick={() => {
startTransition(async () => {
await action().then(setRes);
});
}}
>
LogUserId
</button>
<pre>{JSON.stringify(res)}</pre>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';
import { logUserIdActionStack } from '@/app/protect-action/actions';
import { PageComponent } from '@/app/protect-action/page-component';

function Page() {
// @ts-ignore
return <PageComponent action={logUserIdActionStack} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';
import { logUserIdActionReverification } from '@/app/protect-action/actions';
import { PageComponent } from '@/app/protect-action/page-component';

function Page() {
// @ts-ignore
return <PageComponent action={logUserIdActionReverification} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';
import { logUserIdActionRole } from '@/app/protect-action/actions';
import { PageComponent } from '@/app/protect-action/page-component';

function Page() {
// @ts-ignore
return <PageComponent action={logUserIdActionRole} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';
import { logUserIdAction } from '@/app/protect-action/actions';
import { PageComponent } from '@/app/protect-action/page-component';

function Page() {
// @ts-ignore
return <PageComponent action={logUserIdAction} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useState } from 'react';

export function PageComponent({ url }: { url: string }) {
const [res, setRes] = useState(null);

return (
<>
<button
onClick={async () => {
await fetch(url, {
method: 'POST',
})
.then(res => res.json())
.then(setRes);
}}
>
LogUserId
</button>
<pre>{JSON.stringify(res)}</pre>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client';
import { PageComponent } from '@/app/protect-route/page-component';

function Page() {
return <PageComponent url={'/api/log-user-id-all'} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client';
import { PageComponent } from '@/app/protect-route/page-component';

function Page() {
return <PageComponent url={'/api/log-user-id-reverification'} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client';
import { PageComponent } from '@/app/protect-route/page-component';

function Page() {
return <PageComponent url={'/api/log-user-id-role'} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client';
import { PageComponent } from '@/app/protect-route/page-component';

function Page() {
return <PageComponent url={'/api/log-user-id'} />;
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { __experimental_protectComponent } from '@clerk/nextjs';

const Page = __experimental_protectComponent({
fallback: 'redirectToSignIn',
}).component(() => {
return <div>Protected Page</div>;
});
export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { __experimental_protectComponent } from '@clerk/nextjs';

const Page = __experimental_protectComponent({
fallback: () => <p>Signed out!</p>,
})
.with({
permission: 'org:posts:manage',
fallback: () => <p>User is missing permissions</p>,
})
.component(() => {
return <p>User has access</p>;
});

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { __experimental_protectComponent } from '@clerk/nextjs';

const Page = __experimental_protectComponent({
fallback: 'redirectToSignIn',
})
.with({
role: 'org:admin',
})
.component(() => {
return <p>User has access</p>;
});

export default Page;
Loading
Loading