-
Notifications
You must be signed in to change notification settings - Fork 267
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
base: main
Are you sure you want to change the base?
feat(nextjs): Support Accountless mode #4602
Conversation
🦋 Changeset detectedLatest commit: b27445d The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
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 |
…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
key
The hard-coded value "clerk_accountless_dummy_key" is used as
key
!snapshot |
Hey @nikosdouvlis - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/astro@1.4.14-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/backend@1.18.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/chrome-extension@1.3.38-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/clerk-js@5.35.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/elements@0.19.5-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/clerk-expo@2.3.7-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/expo-passkeys@0.0.7-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/express@1.3.16-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/fastify@2.0.18-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/localizations@3.6.5-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/nextjs@6.5.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/nuxt@0.0.2-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/clerk-react@5.16.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/remix@4.2.54-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/clerk-sdk-node@5.0.67-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/shared@2.15.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/tanstack-start@0.5.1-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/testing@1.3.28-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/themes@2.1.46-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/types@4.35.0-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/ui@0.1.22-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact
npm i @clerk/vue@0.0.6-snapshot.v86758dae8d163423fa7a7e671b8ed2acfe654bd1 --save-exact |
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 1 Skipped Deployment
|
8f908b0
to
18e8fe4
Compare
18e8fe4
to
5e0181d
Compare
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(); | ||
|
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
}, | ||
"#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" |
There was a problem hiding this comment.
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.
"#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" |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this 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, |
There was a problem hiding this comment.
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.
skipSecretKey = false, | |
requireSecretKey = false, |
// 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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a better name:
export const ALLOW_ACCOUNTLESS = isTruthy(process.env.NEXT_PUBLIC_CLERK_ACCOUNTLESS); | |
export const ENABLE_KEYLESS = isTruthy(process.env.NEXT_PUBLIC_CLERK_ENABLE_KEYLESS); |
/** | ||
* If publishable key avoid appending an invalid script in the dom. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** | |
* 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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the extension necessary?
import('./lazy-accountless-creator.js').then(m => m.AccountlessCreateKeys), | |
import('./lazy-accountless-creator').then(m => m.AccountlessCreateKeys), |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const safePk = mergeNextClerkPropsWithEnv(rest).publishableKey; | |
const safePublishableKey = mergeNextClerkPropsWithEnv(rest).publishableKey; |
const { children, ...rest } = props; | ||
const safePk = mergeNextClerkPropsWithEnv(rest).publishableKey; | ||
|
||
if (safePk || isNextWithUnstableServerActions || !ALLOW_ACCOUNTLESS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
publicRequestDate: Pick< | |
publicRequestData: Pick< |
requestData: AuthenticateRequestOptions, | ||
publicRequestDate: Pick< |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo
# | ||
# # #### #### #### # # # # ##### # ###### #### #### | ||
# # # # # # # # # # ## # # # # # # | ||
# # # # # # # # # # # # # ##### #### #### | ||
####### # # # # # # # # # # # # # # | ||
# # # # # # # # # # # ## # # # # # # # | ||
# # #### #### #### #### # # # ###### ###### #### #### |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keyless?
!snapshot |
Hey @issuedat - the snapshot version command generated the following package versions:
Tip: Use the snippet copy button below to quickly install the required packages. npm i @clerk/astro@1.5.0-snapshot.v20241129092324 --save-exact
npm i @clerk/backend@1.19.0-snapshot.v20241129092324 --save-exact
npm i @clerk/chrome-extension@2.0.1-snapshot.v20241129092324 --save-exact
npm i @clerk/clerk-js@5.36.0-snapshot.v20241129092324 --save-exact
npm i @clerk/elements@0.19.8-snapshot.v20241129092324 --save-exact
npm i @clerk/clerk-expo@2.3.11-snapshot.v20241129092324 --save-exact
npm i @clerk/expo-passkeys@0.0.10-snapshot.v20241129092324 --save-exact
npm i @clerk/express@1.3.19-snapshot.v20241129092324 --save-exact
npm i @clerk/fastify@2.0.21-snapshot.v20241129092324 --save-exact
npm i @clerk/localizations@3.7.1-snapshot.v20241129092324 --save-exact
npm i @clerk/nextjs@6.6.0-snapshot.v20241129092324 --save-exact
npm i @clerk/nuxt@0.0.5-snapshot.v20241129092324 --save-exact
npm i @clerk/clerk-react@5.17.1-snapshot.v20241129092324 --save-exact
npm i @clerk/remix@4.2.57-snapshot.v20241129092324 --save-exact
npm i @clerk/clerk-sdk-node@5.0.70-snapshot.v20241129092324 --save-exact
npm i @clerk/shared@2.17.1-snapshot.v20241129092324 --save-exact
npm i @clerk/tanstack-start@0.5.4-snapshot.v20241129092324 --save-exact
npm i @clerk/testing@1.3.31-snapshot.v20241129092324 --save-exact
npm i @clerk/themes@2.1.49-snapshot.v20241129092324 --save-exact
npm i @clerk/types@4.36.0-snapshot.v20241129092324 --save-exact
npm i @clerk/ui@0.1.25-snapshot.v20241129092324 --save-exact
npm i @clerk/vue@0.0.9-snapshot.v20241129092324 --save-exact |
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).
.clerk/
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 thatclerkMiddelware
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
Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change