Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/sour-pears-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/backend': patch
'@clerk/nextjs': patch
---

Improve JSDoc comments to provide you with better IntelliSense information in your IDE
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"turbo": "^2.0.14",
"turbo-ignore": "^2.0.6",
"typedoc": "0.27.6",
"typedoc-plugin-missing-exports": "3.1.0",
"typescript": "catalog:repo",
"verdaccio": "^5.26.3",
"vitest": "3.0.2",
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/jwt/verifyJwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,25 @@ export function decodeJwt(token: string): JwtReturnType<Jwt, TokenVerificationEr
}

export type VerifyJwtOptions = {
/**
* A string or list of [audiences](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3). If passed, it is checked against the `aud` claim in the token.
*/
audience?: string | string[];
/**
* An allowlist of origins to verify against, to protect your application from the subdomain cookie leaking attack.
* @example
* ```ts
* authorizedParties: ['http://localhost:3000', 'https://example.com']
* ```
*/
authorizedParties?: string[];
/**
* Specifies the allowed time difference (in milliseconds) between the Clerk server (which generates the token) and the clock of the user's application server when validating a token. Defaults to 5000 ms (5 seconds).
*/
clockSkewInMs?: number;
/**
* @internal
*/
key: JsonWebKey | string;
};

Expand Down
15 changes: 15 additions & 0 deletions packages/backend/src/tokens/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,29 @@ export function loadClerkJWKFromLocal(localKey?: string): JsonWebKey {
}

export type LoadClerkJWKFromRemoteOptions = {
/**
* @internal
*/
kid: string;
/**
* @deprecated This cache TTL is deprecated and will be removed in the next major version. Specifying a cache TTL is now a no-op.
*/
jwksCacheTtlInMs?: number;
/**
* A flag to skip ignore cache and always fetch JWKS before each jwt verification.
*/
skipJwksCache?: boolean;
/**
* The Clerk Secret Key from the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard.
*/
secretKey?: string;
/**
* The [Clerk Backend API](https://clerk.com/docs/reference/backend-api) endpoint. Defaults to `'https://api.clerk.com'`.
*/
apiUrl?: string;
/**
* The version passed to the Clerk API. Defaults to `'v1'`.
*/
apiVersion?: string;
};

Expand Down
112 changes: 79 additions & 33 deletions packages/backend/src/tokens/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,114 @@ import type { ApiClient } from '../api';
import type { VerifyTokenOptions } from './verify';

export type AuthenticateRequestOptions = {
/**
* The Clerk Publishable Key from the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard.
*/
publishableKey?: string;
/**
* The domain of a [satellite application](https://clerk.com/docs/advanced-usage/satellite-domains) in a multi-domain setup.
*/
domain?: string;
/**
* Whether the instance is a satellite domain in a multi-domain setup. Defaults to `false`.
*/
isSatellite?: boolean;
/**
* The proxy URL from a multi-domain setup.
*/
proxyUrl?: string;
/**
* The sign-in URL from a multi-domain setup.
*/
signInUrl?: string;
/**
* The sign-up URL from a multi-domain setup.
*/
signUpUrl?: string;
/**
* Full URL or path to navigate to after successful sign in. Defaults to `/`.
*/
afterSignInUrl?: string;
/**
* Full URL or path to navigate to after successful sign up. Defaults to `/`.
*/
afterSignUpUrl?: string;
/**
* Used to activate a specific [organization](https://clerk.com/docs/organizations/overview) or [personal account](https://clerk.com/docs/organizations/organization-workspaces#organization-workspaces-in-the-clerk-dashboard:~:text=Personal%20account) based on URL path parameters. If there's a mismatch between the active organization in the session (e.g., as reported by `auth()`) and the organization indicated by the URL, an attempt to activate the organization specified in the URL will be made.
*
* If the activation can't be performed, either because an organization doesn't exist or the user lacks access, the active organization in the session won't be changed. Ultimately, it's the responsibility of the page to verify that the resources are appropriate to render given the URL and handle mismatches appropriately (e.g., by returning a 404).
*/
organizationSyncOptions?: OrganizationSyncOptions;
/**
* @internal
*/
apiClient?: ApiClient;
} & VerifyTokenOptions;

/**
* Defines the options for syncing an organization or personal account state from the URL to the clerk session.
* Useful if the application requires the inclusion of a URL that indicates that a given clerk organization
* (or personal account) must be active on the clerk session.
*
* If a mismatch between the active organization on the session and the organization as indicated by the URL is
* detected, an attempt to activate the given organization will be made.
*
* WARNING: If the activation cannot be performed, either because an organization does not exist or the user lacks
* access, then the active organization on the session will not be changed (and a warning will be logged). It is
* ultimately the responsibility of the page to verify that the resources are appropriate to render given the URL,
* and handle mismatches appropriately (e.g. by returning a 404).
* @expand
*/
export type OrganizationSyncOptions = {
/**
* URL patterns that are organization-specific and contain an organization ID or slug as a path token.
* If a request matches this path, the organization identifier will be extracted and activated before rendering.
* Specifies URL patterns that are organization-specific, containing an organization ID or slug as a path parameter. If a request matches this path, the organization identifier will be used to set that org as active.
*
* WARNING: If the organization cannot be activated either because it does not exist or the user lacks access,
* organization-related fields will be set to null. The server component must detect this and respond
* with an appropriate error (e.g., notFound()).
* If the route also matches the `personalAccountPatterns` prop, this prop takes precedence.
*
* If the route also matches the personalAccountPatterns, this takes precedence.
* Patterns must have a path parameter named either `:id` (to match a Clerk organization ID) or `:slug` (to match a Clerk organization slug).
*
* Must have a path token named either ":id" (matches a clerk organization ID) or ":slug" (matches a clerk
* organization slug).
* @warning
* If the organization can't be activated—either because it doesn't exist or the user lacks access—the previously active organization will remain unchanged. Components must detect this case and provide an appropriate error and/or resolution pathway, such as calling `notFound()` or displaying an [`<OrganizationSwitcher />`](https://clerk.com/docs/components/organization/organization-switcher).
*
* Common examples:
* - ["/orgs/:slug", "/orgs/:slug/(.*)"]
* - ["/orgs/:id", "/orgs/:id/(.*)"]
* - ["/app/:any/orgs/:slug", "/app/:any/orgs/:slug/(.*)"]
* @example
* ["/orgs/:slug", "/orgs/:slug/(.*)"]
* @example
* ["/orgs/:id", "/orgs/:id/(.*)"]
* @example
* ["/app/:any/orgs/:slug", "/app/:any/orgs/:slug/(.*)"]
*/
organizationPatterns?: Pattern[];

/**
* URL patterns for resources in the context of a clerk personal account (user-specific, outside any organization).
* If the route also matches the organizationPattern, the organizationPatterns takes precedence.
* URL patterns for resources that exist within the context of a [Clerk Personal Account](https://clerk.com/docs/organizations/organization-workspaces#organization-workspaces-in-the-clerk-dashboard:~:text=Personal%20account) (user-specific, outside any organization).
*
* Common examples:
* - ["/user", "/user/(.*)"]
* - ["/user/:any", "/user/:any/(.*)"]
* If the route also matches the `organizationPattern` prop, the `organizationPattern` prop takes precedence.
*
* @example
* ["/user", "/user/(.*)"]
* @example
* ["/user/:any", "/user/:any/(.*)"]
*/
personalAccountPatterns?: Pattern[];
};

/**
* A pattern representing the structure of a URL path.
* In addition to a valid URL, may include:
* - Named path tokens prefixed with a colon (e.g., ":id", ":slug", ":any")
* - Wildcard token (e.g., ".(*)"), which will match the remainder of the path
* Examples: "/orgs/:slug", "/app/:any/orgs/:id", "/personal-account/(.*)"
* A `Pattern` is a `string` that represents the structure of a URL path. In addition to any valid URL, it may include:
* - Named path parameters prefixed with a colon (e.g., `:id`, `:slug`, `:any`).
* - Wildcard token, `(.*)`, which matches the remainder of the path.
*
* @example
* /orgs/:slug
*
* ```ts
* '/orgs/acmecorp' // matches (`:slug` value: acmecorp)
* '/orgs' // does not match
* '/orgs/acmecorp/settings' // does not match
* ```
*
* @example
* /app/:any/orgs/:id
*
* ```ts
* '/app/petstore/orgs/org_123' // matches (`:id` value: org_123)
* '/app/dogstore/v2/orgs/org_123' // does not match
* ```
*
* @example
* /personal-account/(.*)
*
* ```ts
* '/personal-account/settings' // matches
* '/personal-account' // does not match
* ```
*/
type Pattern = string;
7 changes: 6 additions & 1 deletion packages/backend/src/tokens/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import type { LoadClerkJWKFromRemoteOptions } from './keys';
import { loadClerkJWKFromLocal, loadClerkJWKFromRemote } from './keys';

export type VerifyTokenOptions = Omit<VerifyJwtOptions, 'key'> &
Omit<LoadClerkJWKFromRemoteOptions, 'kid'> & { jwtKey?: string };
Omit<LoadClerkJWKFromRemoteOptions, 'kid'> & {
/**
* Used to verify the session token in a networkless manner. Supply the PEM public key from the **[**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page -> Show JWT public key -> PEM Public Key** section in the Clerk Dashboard. **It's recommended to use [the environment variable](https://clerk.com/docs/deployments/clerk-environment-variables) instead.** For more information, refer to [Manual JWT verification](https://clerk.com/docs/backend-requests/handling/manual-jwt).
*/
jwtKey?: string;
};

export async function verifyToken(
token: string,
Expand Down
41 changes: 39 additions & 2 deletions packages/nextjs/src/app-router/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,50 @@ import { createProtect } from '../../server/protect';
import { decryptClerkRequestData } from '../../server/utils';
import { buildRequestLike } from './utils';

type Auth = AuthObject & { redirectToSignIn: RedirectFun<ReturnType<typeof redirect>> };

/**
* `Auth` object of the currently active user and the `redirectToSignIn()` method.
*/
type Auth = AuthObject & {
/**
* The `auth()` helper returns the `redirectToSignIn()` method, which you can use to redirect the user to the sign-in page.
*
* @param [returnBackUrl] {string | URL} - The URL to redirect the user back to after they sign in.
*
* @note
* `auth()` on the server-side can only access redirect URLs defined via [environment variables](https://clerk.com/docs/deployments/clerk-environment-variables#sign-in-and-sign-up-redirects) or [`clerkMiddleware` dynamic keys](https://clerk.com/docs/references/nextjs/clerk-middleware#dynamic-keys).
*/
redirectToSignIn: RedirectFun<ReturnType<typeof redirect>>;
};
export interface AuthFn {
(): Promise<Auth>;
/**
* `auth` includes a single property, the `protect()` method, which you can use in two ways:
* - to check if a user is authenticated (signed in)
* - to check if a user is authorized (has the correct roles or permissions) to access something, such as a component or a route handler
*
* The following table describes how auth.protect() behaves based on user authentication or authorization status:
*
* | Authenticated | Authorized | `auth.protect()` will |
* | - | - | - |
* | Yes | Yes | Return the [`Auth`](https://clerk.com/docs/references/backend/types/auth-object) object. |
* | Yes | No | Return a `404` error. |
* | No | No | Redirect the user to the sign-in page\*. |
*
* @important
* \*For non-document requests, such as API requests, `auth.protect()` returns a `404` error to users who aren't authenticated.
*
* `auth.protect()` can be used to check if a user is authenticated or authorized to access certain parts of your application or even entire routes. See detailed examples in the [dedicated guide](https://clerk.com/docs/organizations/verify-user-permissions).
*/
protect: AuthProtect;
}

/**
* The `auth()` helper returns the [`Auth`](https://clerk.com/docs/references/backend/types/auth-object) object of the currently active user, as well as the [`redirectToSignIn()`](https://clerk.com/docs/references/nextjs/auth#redirect-to-sign-in) method.
*
* - Only available for App Router.
* - Only works on the server-side, such as in Server Components, Route Handlers, and Server Actions.
* - Requires [`clerkMiddleware()`](https://clerk.com/docs/references/nextjs/clerk-middleware) to be configured.
*/
export const auth: AuthFn = async () => {
require('server-only');

Expand Down
23 changes: 23 additions & 0 deletions packages/nextjs/src/app-router/server/currentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@ import type { User } from '@clerk/backend';
import { clerkClient } from '../../server/clerkClient';
import { auth } from './auth';

/**
* The `currentUser` helper returns the [Backend User](https://clerk.com/docs/references/backend/types/backend-user) object of the currently active user. It can be used in Server Components, Route Handlers, and Server Actions.
*
* Under the hood, this helper:
* - calls `fetch()`, so it is automatically deduped per request.
* - uses the [`GET /v1/users/{user_id}`](https://clerk.com/docs/reference/backend-api/tag/Users#operation/GetUser) endpoint.
* - counts towards the [Backend API request rate limit](https://clerk.com/docs/backend-requests/resources/rate-limits#rate-limits).
*
* @example
* ```tsx
* // app/page.tsx
*
* import { currentUser } from '@clerk/nextjs/server'
*
* export default async function Page() {
* const user = await currentUser()
*
* if (!user) return <div>Not signed in</div>
*
* return <div>Hello {user?.firstName}</div>
* }
* ```
*/
export async function currentUser(): Promise<User | null> {
require('server-only');

Expand Down
54 changes: 41 additions & 13 deletions packages/nextjs/src/server/buildClerkProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,52 @@ import type { RequestLike } from './types';

type BuildClerkPropsInitState = { user?: User | null; session?: Session | null; organization?: Organization | null };

type BuildClerkProps = (req: RequestLike, authState?: BuildClerkPropsInitState) => Record<string, unknown>;

/**
* To enable Clerk SSR support, include this object to the `props`
* returned from `getServerSideProps`. This will automatically make the auth state available to
* the Clerk components and hooks during SSR, the hydration phase and CSR.
* Clerk uses `buildClerkProps` to inform the client-side helpers of the authentication state of the user. This function is used SSR in the `getServerSideProps` function of your Next.js application.
*
* @example
* **Basic usage**
*
* ```tsx
* // pages/myServerSidePage.tsx
*
* import { getAuth, buildClerkProps } from '@clerk/nextjs/server'
* import { GetServerSideProps } from 'next'
*
* export const getServerSideProps: GetServerSideProps = async (ctx) => {
* const { userId } = getAuth(ctx.req)
*
* if (!userId) {
* // handle user is not signed in.
* }
*
* // Load any data your application needs for the page using the userId
* return { props: { ...buildClerkProps(ctx.req) } }
* }
* ```
*
* @example
* import { getAuth } from '@clerk/nextjs/server';
* **Usage with `clerkClient`**
*
* The `clerkClient` allows you to access the Clerk API. You can use this to retrieve or update data.
*
* ```tsx
* // pages/api/example.ts
*
* export const getServerSideProps = ({ req }) => {
* const { authServerSideProps } = getAuth(req);
* const myData = getMyData();
* import { getAuth, buildClerkProps, clerkClient } from '@clerk/nextjs/server'
* import { GetServerSideProps } from 'next'
*
* return {
* props: { myData, authServerSideProps },
* };
* };
* export const getServerSideProps: GetServerSideProps = async (ctx) => {
* const { userId } = getAuth(ctx.req)
*
* const user = userId ? await clerkClient().users.getUser(userId) : undefined
*
* return { props: { ...buildClerkProps(ctx.req, { user }) } }
* }
* ```
*/
type BuildClerkProps = (req: RequestLike, authState?: BuildClerkPropsInitState) => Record<string, unknown>;

export const buildClerkProps: BuildClerkProps = (req, initialState = {}) => {
const sanitizedAuthObject = getDynamicAuthData(req, initialState);

Expand Down
Loading
Loading