Skip to content

Commit 3f64080

Browse files
authored
feat(nextjs): Support Keyless mode (#4602)
1 parent e7abe51 commit 3f64080

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+673
-64
lines changed

.changeset/dry-cats-change.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/backend': minor
3+
---
4+
5+
New **experimental** API: `AccountlessApplicationAPI`
6+
7+
Inside `clerkClient` you can activate this new API through `__experimental_accountlessApplications`. It allows you to generate an "accountless" application and the API returns the publishable key, secret key, and an URL as a response. The URL allows a user to claim this application with their account. Hence the name "accountless" because in its initial state the application is not attached to any account yet.

.changeset/grumpy-camels-think.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@clerk/clerk-react': minor
3+
---
4+
5+
Various internal changes have been made to support a new feature called "Keyless mode". You'll be able to use this feature with Next.js and `@clerk/nextjs` initially. Read the `@clerk/nextjs` changelog to learn more.
6+
7+
List of changes:
8+
- A new internal prop called `__internal_bypassMissingPublishableKey` has been added. Normally an error is thrown when the publishable key is missing, this disables this behavior.
9+
- Loading of `clerk-js` won't be attempted when a missing key is present
10+
- A new instance of `IsomorphicClerk` (an internal Clerk class) is created for each new publishable key

.changeset/itchy-cats-drive.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@clerk/nextjs': minor
3+
---
4+
5+
A new **experimental** feature is available: "Keyless mode"
6+
7+
Normally, in order to start a Clerk + Next.js application you need to provide a publishable key and secret key. With "Keyless mode" activated you no longer need to provide these two keys to start your Clerk application. These keys will be automatically generated and the application can be claimed with your account either through a UI prompt or with a URL in your terminal.
8+
9+
**Requirements**:
10+
- You need to use Next.js `14.2.0` or later
11+
- You need to set the environment variable `NEXT_PUBLIC_CLERK_ENABLE_KEYLESS=true`

.changeset/warm-spiders-develop.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/types': minor
4+
---
5+
6+
Replace `__internal_claimAccountlessKeysUrl` with `__internal_claimKeylessApplicationUrl`.

integration/tests/next-build.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
132132
'src/app/nested-provider/page.tsx',
133133
() => `import { ClerkProvider } from '@clerk/nextjs';
134134
import { ClientComponent } from './client';
135-
135+
136136
export default function Page() {
137137
return (
138138
<ClerkProvider dynamic>
@@ -147,10 +147,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
147147
() => `'use client';
148148
149149
import { useAuth } from '@clerk/nextjs';
150-
150+
151151
export function ClientComponent() {
152152
useAuth();
153-
153+
154154
return <p>I am dynamically rendered</p>;
155155
}
156156
`,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { AccountlessApplication } from '../resources/AccountlessApplication';
2+
import { AbstractAPI } from './AbstractApi';
3+
4+
const basePath = '/accountless_applications';
5+
6+
export class AccountlessApplicationAPI extends AbstractAPI {
7+
public async createAccountlessApplication() {
8+
return this.request<AccountlessApplication>({
9+
method: 'POST',
10+
path: basePath,
11+
});
12+
}
13+
}

packages/backend/src/api/endpoints/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './AccountlessApplicationsAPI';
12
export * from './AbstractApi';
23
export * from './AllowlistIdentifierApi';
34
export * from './ClientApi';

packages/backend/src/api/factory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
AccountlessApplicationAPI,
23
AllowlistIdentifierAPI,
34
ClientAPI,
45
DomainAPI,
@@ -23,6 +24,9 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
2324
const request = buildRequest(options);
2425

2526
return {
27+
__experimental_accountlessApplications: new AccountlessApplicationAPI(
28+
buildRequest({ ...options, requireSecretKey: false }),
29+
),
2630
allowlistIdentifiers: new AllowlistIdentifierAPI(request),
2731
clients: new ClientAPI(request),
2832
emailAddresses: new EmailAddressAPI(request),

packages/backend/src/api/request.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,26 @@ type BuildRequestOptions = {
5151
apiVersion?: string;
5252
/* Library/SDK name */
5353
userAgent?: string;
54+
/**
55+
* Allow requests without specifying a secret key. In most cases this should be set to `false`.
56+
* Defaults to `true`.
57+
*/
58+
requireSecretKey?: boolean;
5459
};
5560
export function buildRequest(options: BuildRequestOptions) {
5661
const requestFn = async <T>(requestOptions: ClerkBackendApiRequestOptions): Promise<ClerkBackendApiResponse<T>> => {
57-
const { secretKey, apiUrl = API_URL, apiVersion = API_VERSION, userAgent = USER_AGENT } = options;
62+
const {
63+
secretKey,
64+
requireSecretKey = true,
65+
apiUrl = API_URL,
66+
apiVersion = API_VERSION,
67+
userAgent = USER_AGENT,
68+
} = options;
5869
const { path, method, queryParams, headerParams, bodyParams, formData } = requestOptions;
5970

60-
assertValidSecretKey(secretKey);
71+
if (requireSecretKey) {
72+
assertValidSecretKey(secretKey);
73+
}
6174

6275
const url = joinPaths(apiUrl, apiVersion, path);
6376

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { AccountlessApplicationJSON } from './JSON';
2+
3+
export class AccountlessApplication {
4+
constructor(
5+
readonly publishableKey: string,
6+
readonly secretKey: string,
7+
readonly claimUrl: string,
8+
) {}
9+
10+
static fromJSON(data: AccountlessApplicationJSON): AccountlessApplication {
11+
return new AccountlessApplication(data.publishable_key, data.secret_key, data.claim_url);
12+
}
13+
}

0 commit comments

Comments
 (0)