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): Support Accountless mode #4602

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

panteliselef
Copy link
Member

@panteliselef panteliselef commented Nov 19, 2024

Description

Opt-in support for Accountless

This is introduced for @clerk/nextjs but more sdks may follow in the future.

What does the PR do (for development only).

  • Allows for a Clerk Next.js application to run without a PK and SK being explicitly set.
  • It will automatically creates new keys and stores them inside .clerk/
  • The application will use those keys, but prompts the user to claim them.

Technical

  • For ClerkProvider as RSC: clerkMIddleware detects missing sk -> basically calls next() as "signed-out" and let rendering to call Server ClerkProvider -> Server ClerkProvider fetches and creates the keys, stores them into a file -> It passes the keys into a client component that will fire a server action on mount that sets the keys into cookies and redirect to a made up url that clerkMiddelware expects -> clerkMiddleware runs again, this time detects the cookies that contain the accountless keys and properly propagates the pk and sk further down.

  • For ClerkProvider as Client component: The provider will render which fires a server action that creates the keys and set them into cookies. Once this is completed we return only the PK and the claimUrl and we force the ClerkProvider from clerk-react to remount.

Requirements

  • Store the keys in the file system, in order for the keys to "belong" to a certain project.
  • Use hashed cookies based on the working directory (PWD) of the project to allow multiple projects that run in accountless project to work.
  • We are using cookies as a way of syncing the rendering lifecycle with the middleware.

Checklist

  • pnpm test runs as expected.
  • pnpm 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:

Copy link

changeset-bot bot commented Nov 19, 2024

🦋 Changeset detected

Latest commit: b27445d

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

This PR includes changesets to release 22 packages
Name Type
@clerk/clerk-js Minor
@clerk/types Minor
@clerk/backend Minor
@clerk/nextjs Minor
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch
@clerk/astro Patch
@clerk/elements Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/localizations Patch
@clerk/nuxt Patch
@clerk/clerk-react Patch
@clerk/remix Patch
@clerk/clerk-sdk-node Patch
@clerk/shared Patch
@clerk/tanstack-start Patch
@clerk/testing Patch
@clerk/themes Patch
@clerk/ui Patch
@clerk/vue 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

@panteliselef panteliselef self-assigned this Nov 19, 2024
…der and update middleware to read sk and pk from cookies
@@ -224,6 +237,8 @@
}
}

const ACCOUNTLESS_ENCRYPTION_KEY = 'clerk_accountless_dummy_key';

Check failure

Code scanning / CodeQL

Hard-coded credentials Critical

The hard-coded value "clerk_accountless_dummy_key" is used as
key
.
The hard-coded value "clerk_accountless_dummy_key" is used as
key
.
@nikosdouvlis
Copy link
Member

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @nikosdouvlis - the snapshot version command generated the following package versions:

Package Version
@clerk/astro 1.4.14-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/backend 1.18.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/chrome-extension 1.3.38-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/clerk-js 5.35.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/elements 0.19.5-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/clerk-expo 2.3.7-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/expo-passkeys 0.0.7-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/express 1.3.16-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/fastify 2.0.18-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/localizations 3.6.5-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/nextjs 6.5.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/nuxt 0.0.2-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/clerk-react 5.16.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/remix 4.2.54-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/clerk-sdk-node 5.0.67-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/shared 2.15.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/tanstack-start 0.5.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/testing 1.3.28-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/themes 2.1.46-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/types 4.35.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/ui 0.1.22-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1
@clerk/vue 0.0.6-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@1.4.14-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/backend

npm i @clerk/backend@1.18.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@1.3.38-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@5.35.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/elements

npm i @clerk/elements@0.19.5-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/clerk-expo

npm i @clerk/clerk-expo@2.3.7-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@0.0.7-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/express

npm i @clerk/express@1.3.16-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.0.18-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/localizations

npm i @clerk/localizations@3.6.5-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@6.5.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@0.0.2-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/clerk-react

npm i @clerk/clerk-react@5.16.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/remix

npm i @clerk/remix@4.2.54-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/clerk-sdk-node

npm i @clerk/clerk-sdk-node@5.0.67-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/shared

npm i @clerk/shared@2.15.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/tanstack-start

npm i @clerk/tanstack-start@0.5.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/testing

npm i @clerk/testing@1.3.28-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/themes

npm i @clerk/themes@2.1.46-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/types

npm i @clerk/types@4.35.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/ui

npm i @clerk/ui@0.1.22-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

@clerk/vue

npm i @clerk/vue@0.0.6-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact

Copy link

vercel bot commented Nov 21, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
clerk-js-sandbox ⬜️ Skipped (Inspect) Nov 27, 2024 10:58am

React does not like the fact that <html><body> may be missing and that Suspense is not used inside <html>
…s-in-clerknextjs

# Conflicts:
#	packages/clerk-js/src/core/clerk.ts
#	packages/clerk-js/src/ui/Components.tsx
#	packages/clerk-js/src/ui/lazyModules/components.ts
export function ClientComponent() {
useAuth();

Copy link
Member Author

Choose a reason for hiding this comment

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

this is just auto formatting

@@ -23,6 +24,9 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
const request = buildRequest(options);

return {
__experimental_accountlessApplications: new AccountlessApplicationAPI(
Copy link
Member Author

Choose a reason for hiding this comment

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

i'd like to have this as experimental for a while

@@ -51,13 +51,23 @@ type BuildRequestOptions = {
apiVersion?: string;
/* Library/SDK name */
userAgent?: string;

skipSecretKey?: boolean;
Copy link
Member Author

Choose a reason for hiding this comment

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

the endpoint for creating the accountless keys doesn't require a secret key

Comment on lines +7 to +16
},
"#fs": {
"edge-light": "./runtime/browser/fs.js",
"worker": "./runtime/browser/fs.js",
"browser": "./runtime/browser/fs.js",
"node": {
"require": "./runtime/node/fs.js",
"import": "./runtime/node/fs.js"
},
"default": "./runtime/browser/fs.js"
Copy link
Member Author

Choose a reason for hiding this comment

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

fixes a bug in nextjs where importing from node:fs is prohibited and the dev server or build step fails even if the code is wrapped with try/catch.

Comment on lines +8 to +16
"#fs": {
"edge-light": "./runtime/browser/fs.js",
"worker": "./runtime/browser/fs.js",
"browser": "./runtime/browser/fs.js",
"node": {
"require": "./runtime/node/fs.js",
"import": "./runtime/node/fs.js"
},
"default": "./runtime/browser/fs.js"
Copy link
Member Author

Choose a reason for hiding this comment

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

fixes a bug in nextjs where importing from node:fs is prohibited and the dev server or build step fails even if the code is wrapped with try/catch.

@@ -0,0 +1,12 @@
const { existsSync, writeFileSync, readFileSync, appendFileSync, mkdirSync, rmSync } = require('node:fs');
const path = require('node:path');
Copy link
Member Author

Choose a reason for hiding this comment

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

i added path inside the #fs module because it was faster, let me know if you disagree

Copy link
Member

Choose a reason for hiding this comment

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

Make we can rename it something else if we want to export both.

@panteliselef panteliselef marked this pull request as ready for review November 27, 2024 14:30
@panteliselef panteliselef requested a review from a team November 27, 2024 14:37
Copy link
Member

@BRKalow BRKalow left a comment

Choose a reason for hiding this comment

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

Looking good so far.

const { secretKey, apiUrl = API_URL, apiVersion = API_VERSION, userAgent = USER_AGENT } = options;
const {
secretKey,
skipSecretKey = false,
Copy link
Member

Choose a reason for hiding this comment

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

I think requireSecretKey might be a better name.

Suggested change
skipSecretKey = false,
requireSecretKey = false,

Comment on lines +18 to +19
// TODO-ACCOUNTLESS: Do we even need this ? I think setting the cookie will reset the router cache.
redirect(`/clerk-sync-accountless?returnUrl=${returnUrl}`, RedirectType.replace);
Copy link
Member

@BRKalow BRKalow Nov 27, 2024

Choose a reason for hiding this comment

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

What is the route for specifically? Would be nice if we could remove! Would it be enough if we triggered a full page refresh?

@@ -21,3 +21,5 @@ export const SDK_METADATA = {

export const TELEMETRY_DISABLED = isTruthy(process.env.NEXT_PUBLIC_CLERK_TELEMETRY_DISABLED);
export const TELEMETRY_DEBUG = isTruthy(process.env.NEXT_PUBLIC_CLERK_TELEMETRY_DEBUG);

export const ALLOW_ACCOUNTLESS = isTruthy(process.env.NEXT_PUBLIC_CLERK_ACCOUNTLESS);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe a better name:

Suggested change
export const ALLOW_ACCOUNTLESS = isTruthy(process.env.NEXT_PUBLIC_CLERK_ACCOUNTLESS);
export const ENABLE_KEYLESS = isTruthy(process.env.NEXT_PUBLIC_CLERK_ENABLE_KEYLESS);

Comment on lines +16 to +17
/**
* If publishable key avoid appending an invalid script in the dom.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/**
* If publishable key avoid appending an invalid script in the dom.
/**
* If no publishable key, avoid appending an invalid script in the DOM.

* Note: Using lazy() with Suspense instead of dynamic is not possible as React will throw a hydration error when `ClerkProvider` wraps `<html><body>...`
*/
const LazyAccountlessCreator = dynamic(() =>
import('./lazy-accountless-creator.js').then(m => m.AccountlessCreateKeys),
Copy link
Member

Choose a reason for hiding this comment

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

Is the extension necessary?

Suggested change
import('./lazy-accountless-creator.js').then(m => m.AccountlessCreateKeys),
import('./lazy-accountless-creator').then(m => m.AccountlessCreateKeys),

Copy link
Member Author

Choose a reason for hiding this comment

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

i believe eslint was complaining


export const ClientClerkProvider = (props: NextClerkProviderProps) => {
const { children, ...rest } = props;
const safePk = mergeNextClerkPropsWithEnv(rest).publishableKey;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const safePk = mergeNextClerkPropsWithEnv(rest).publishableKey;
const safePublishableKey = mergeNextClerkPropsWithEnv(rest).publishableKey;

const { children, ...rest } = props;
const safePk = mergeNextClerkPropsWithEnv(rest).publishableKey;

if (safePk || isNextWithUnstableServerActions || !ALLOW_ACCOUNTLESS) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (safePk || isNextWithUnstableServerActions || !ALLOW_ACCOUNTLESS) {
if (safePublishableKey || isNextWithUnstableServerActions || !ALLOW_ACCOUNTLESS) {

@@ -97,7 +97,19 @@ export function decorateRequest(
req: ClerkRequest,
res: Response,
requestState: RequestState,
requestData?: AuthenticateRequestOptions,
requestData: AuthenticateRequestOptions,
publicRequestDate: Pick<
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
publicRequestDate: Pick<
publicRequestData: Pick<

Comment on lines +100 to +101
requestData: AuthenticateRequestOptions,
publicRequestDate: Pick<
Copy link
Member

Choose a reason for hiding this comment

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

Can you remind me why we need these two different buckets of request data?

'@clerk/types': minor
---

accountless
Copy link
Member

Choose a reason for hiding this comment

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

Once we're ready, let's expand the changeset description here. And consolidate the two changeset files.

*/
const dynamicConfig = await getDynamicConfig();

const newOrReadKeys = dynamicConfig.accountlessMode
Copy link
Member Author

Choose a reason for hiding this comment

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

About dynamicConfig.accountlessMode we need a way to let us know that the middleware is using the PK and SK from the accountless cookies

'@clerk/types': minor
---

accountless
Copy link
Member Author

Choose a reason for hiding this comment

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

todo

'@clerk/nextjs': minor
---

accountless
Copy link
Member Author

Choose a reason for hiding this comment

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

todo

@panteliselef panteliselef mentioned this pull request Nov 28, 2024
13 tasks
Comment on lines +77 to +83
#
# # #### #### #### # # # # ##### # ###### #### ####
# # # # # # # # # # ## # # # # # #
# # # # # # # # # # # # # ##### #### ####
####### # # # # # # # # # # # # # #
# # # # # # # # # # # ## # # # # # # #
# # #### #### #### #### # # # ###### ###### #### ####

Choose a reason for hiding this comment

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

Keyless?

@issuedat
Copy link
Member

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @issuedat - the snapshot version command generated the following package versions:

Package Version
@clerk/astro 1.5.0-snapshot.v20241129092324
@clerk/backend 1.19.0-snapshot.v20241129092324
@clerk/chrome-extension 2.0.1-snapshot.v20241129092324
@clerk/clerk-js 5.36.0-snapshot.v20241129092324
@clerk/elements 0.19.8-snapshot.v20241129092324
@clerk/clerk-expo 2.3.11-snapshot.v20241129092324
@clerk/expo-passkeys 0.0.10-snapshot.v20241129092324
@clerk/express 1.3.19-snapshot.v20241129092324
@clerk/fastify 2.0.21-snapshot.v20241129092324
@clerk/localizations 3.7.1-snapshot.v20241129092324
@clerk/nextjs 6.6.0-snapshot.v20241129092324
@clerk/nuxt 0.0.5-snapshot.v20241129092324
@clerk/clerk-react 5.17.1-snapshot.v20241129092324
@clerk/remix 4.2.57-snapshot.v20241129092324
@clerk/clerk-sdk-node 5.0.70-snapshot.v20241129092324
@clerk/shared 2.17.1-snapshot.v20241129092324
@clerk/tanstack-start 0.5.4-snapshot.v20241129092324
@clerk/testing 1.3.31-snapshot.v20241129092324
@clerk/themes 2.1.49-snapshot.v20241129092324
@clerk/types 4.36.0-snapshot.v20241129092324
@clerk/ui 0.1.25-snapshot.v20241129092324
@clerk/vue 0.0.9-snapshot.v20241129092324

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@1.5.0-snapshot.v20241129092324 --save-exact

@clerk/backend

npm i @clerk/backend@1.19.0-snapshot.v20241129092324 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@2.0.1-snapshot.v20241129092324 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@5.36.0-snapshot.v20241129092324 --save-exact

@clerk/elements

npm i @clerk/elements@0.19.8-snapshot.v20241129092324 --save-exact

@clerk/clerk-expo

npm i @clerk/clerk-expo@2.3.11-snapshot.v20241129092324 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@0.0.10-snapshot.v20241129092324 --save-exact

@clerk/express

npm i @clerk/express@1.3.19-snapshot.v20241129092324 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.0.21-snapshot.v20241129092324 --save-exact

@clerk/localizations

npm i @clerk/localizations@3.7.1-snapshot.v20241129092324 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@6.6.0-snapshot.v20241129092324 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@0.0.5-snapshot.v20241129092324 --save-exact

@clerk/clerk-react

npm i @clerk/clerk-react@5.17.1-snapshot.v20241129092324 --save-exact

@clerk/remix

npm i @clerk/remix@4.2.57-snapshot.v20241129092324 --save-exact

@clerk/clerk-sdk-node

npm i @clerk/clerk-sdk-node@5.0.70-snapshot.v20241129092324 --save-exact

@clerk/shared

npm i @clerk/shared@2.17.1-snapshot.v20241129092324 --save-exact

@clerk/tanstack-start

npm i @clerk/tanstack-start@0.5.4-snapshot.v20241129092324 --save-exact

@clerk/testing

npm i @clerk/testing@1.3.31-snapshot.v20241129092324 --save-exact

@clerk/themes

npm i @clerk/themes@2.1.49-snapshot.v20241129092324 --save-exact

@clerk/types

npm i @clerk/types@4.36.0-snapshot.v20241129092324 --save-exact

@clerk/ui

npm i @clerk/ui@0.1.25-snapshot.v20241129092324 --save-exact

@clerk/vue

npm i @clerk/vue@0.0.9-snapshot.v20241129092324 --save-exact

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