Use a root-relative link (/contact/support) for the passwordHasher "contact support" reference so the generated API reference renders it as an internal same-tab link instead of an external one. (#9347) by @manovotny
Updated dependencies [34d278b]:
clerkClient.users.removePassword(userId, params?) to remove a user's password through the Backend API. Password removal is allowed even when the user has no alternate sign-in method configured. Existing sessions remain active by default; pass { signOutOfOtherSessions: true } to revoke them. (#9326) by @joshrowleyImprove generated API reference links, expose BillingSubscriptionItemStatus, and clarify the createUser() identification status documentation. (#9340) by @SarahSoutoul
Updated dependencies [1ef84c3, d639048, a66cbbf]:
Add the optional emailAddressIdentificationStatus and phoneNumberIdentificationStatus parameters to CreateUserParams. The Backend API has supported these arrays on POST /v1/users since they shipped, but createUser() had no way to pass them, so every email address and phone number was necessarily created verified. Each array runs parallel to emailAddress / phoneNumber — one item per identifier, applied by position — and an item set to 'reserved' creates that identifier unverified but still usable for sign-in and locked so no other user can claim it. (#9305) by @dmoerner
The createUser() documentation is corrected accordingly: it stated unconditionally that created email addresses and phone numbers are automatically verified, which is only the default.
Updated dependencies [5c81479]:
Return a TokenVerificationError from decodeJwt and verifyToken for tokens whose header, payload, or signature cannot be decoded. (#9268) by @wobsoriano
Updated dependencies [aaea141]:
EnterpriseConnection response resource with what the Backend API actually returns: (#9156) by @manovotny
EnterpriseConnection now exposes provider, logoPublicUrl, allowOrganizationAccountLinking, authenticatable, disableJitProvisioning, and customAttributes.EnterpriseConnectionSamlConnection now exposes active, forceAuthn, and loginHint.EnterpriseConnectionOauthConfig now exposes providerKey, authUrl, tokenUrl, userInfoUrl, and requiresPkce.undefined despite their declared types: allowSubdomains on EnterpriseConnection (use samlConnection.allowSubdomains), and idpMetadata and syncUserAttributes on EnterpriseConnectionSamlConnection (use the top-level syncUserAttributes).organizationId is now normalized to null when the Backend API omits it, matching its declared string | null type. Properties backed by optional API fields (for example oauthConfig.clientId and the SAML IdP fields) are now typed as possibly undefined to match runtime behavior.blockDisposableEmailDomains bolded "Block sign-ups that use disposable email domains", but the toggle in the Clerk Dashboard is "Block sign-ups that use disposable email addresses". ignoreDotsForGmailAddresses bolded "Ignore dots for Gmail addresses" as a Dashboard toggle, but no such control exists — the wording now matches the equivalent comment on UpdateRestrictionsParams. Property names are unchanged. (#9253) by @manovotny01f2c12]:
fapiUrl option to Frontend API proxy helpers so requests can target a custom Clerk Frontend API URL. (#9223) by @thiskevinwang097432d]:
Fix a cross-origin handshake bypass where isKnownClerkReferrer() trusted overly broad referrer hosts as Clerk-owned: any accounts.* host (e.g. accounts.attacker.com), plus dev account-portal domains (*.accounts.dev and legacy suffixes) on production instances. These let unrelated origins skip the handshake and its session-freshness check. The referrer is now trusted only for the accounts portal derived from the instance's frontend API, plus dev account-portal domains on non-production instances. (#9145) by @dominic-clerk
Add an optional orgId parameter to createSignInToken() for activating an Organization when the token is redeemed. (#9192) by @swolfand
Standardize JSDoc punctuation to always follow e.g. and i.e. with a comma (e.g., / i.e.,), matching the docs style guide. Comment-only change; no runtime behavior is affected. This keeps the generated Typedoc reference output consistent. (#9201) by @SarahSoutoul
Add the required provider field to CreateEnterpriseConnectionParams. The Backend API has always required provider when creating an enterprise connection, so calls to createEnterpriseConnection() without it type-checked but failed at runtime. The field is typed to the supported provider values ('saml_custom', 'saml_okta', 'saml_google', 'saml_microsoft', 'oidc_custom', 'oidc_github_enterprise', 'oidc_gitlab'), so unsupported values are also caught at compile time. (#9155) by @manovotny
Add the remaining optional enterprise connection parameters supported by the Backend API. CreateEnterpriseConnectionParams and UpdateEnterpriseConnectionParams now accept allowOrganizationAccountLinking, customAttributes, authenticatable, and disableJitProvisioning (update also accepts disableAdditionalIdentifications), and SAML params accept loginHint for configuring the login_hint sent to the IdP. (#9155) by @manovotny
Align CreateEnterpriseConnectionParams and UpdateEnterpriseConnectionParams with the Backend API contract: (#9155) by @manovotny
name and domains are now required on CreateEnterpriseConnectionParams. The Backend API already rejected requests missing either of them, so calls that omitted these fields failed at runtime; the types now surface this at compile time.syncUserAttributes on CreateEnterpriseConnectionParams. The Backend API ignores this parameter on create; use updateEnterpriseConnection() to set it.provider on UpdateEnterpriseConnectionParams. The Backend API ignores this parameter on update; the provider cannot be changed after creation.Updated dependencies [858a689, c904fb4]:
Reject machine tokens (M2M and OAuth JWTs) presented in the __session cookie. Previously such a token could pass session verification and produce a signed-in state with the machine identity as userId, defeating if (userId) authorization checks. The cookie path now mirrors the existing header-path guard and returns a signed-out state for these tokens. (#9168) by @dominic-clerk
Updated dependencies [bcbdda6]:
Add CLERK_DISABLE_AUTO_PROXY=true to opt out of automatic Frontend API proxying on Vercel production deployments. (#9159) by @brkalow
Updated dependencies [e162b71]:
M2MToken, APIKey, and IdPOAuthAccessToken JSDoc that the timestamp properties (expiration, lastUsedAt, createdAt, and updatedAt) are Unix timestamps in milliseconds (not seconds). (#9122) by @SarahSoutoul8dbf343]:
Improve satellite-domain redirect loop diagnostics. (#8636) by @jescalan
Updated dependencies [62f6702]:
Enforce the azp (authorized party) claim when authorizedParties is configured. Previously, a session token that was missing the azp claim was accepted even when authorizedParties was set, allowing the authorized-parties check to be bypassed by omitting the claim. Now, when authorizedParties is configured, a token with a missing or empty azp claim is rejected. Tokens without azp continue to be accepted when no authorizedParties are configured. (#8877) by @dominic-clerk
Updated dependencies [6f97ef5, bab1f29, f2d9e4b]:
idpCertificateIssuedAt and idpCertificateExpiresAt to SAML enterprise connections, exposing the IdP certificate validity window (#9077) by @LauraBeatrisAdd an experimental clerkClient.emails.create() method for sending transactional emails. It accepts address- or user-based recipients, supports optional replyTo, subject, and HTML and/or text content, and returns the created Email resource. (#9010) by @cbnsndwch
This method is marked @experimental and may change in a future release.
clerkClient.oauthApplications.revokeToken() for revoking opaque OAuth application access and refresh tokens. (#9040) by @jfosheeorganizations.deleteOrganization() now validates that an organization ID was provided. Calling it with an empty ID throws A valid resource ID is required. locally instead of issuing a DELETE request to the organizations collection endpoint, matching the other ID-based methods on the API. (#9036) by @jacekradko
M2M JWT verification now validates the token-category (cat) header and rejects M2M JWTs tagged as a different token class. M2M JWTs minted by Clerk carry the correct category and are unaffected; M2M JWTs without the header continue to verify. (#9038) by @wobsoriano
Updated dependencies [4306146, 533f0b1]:
Add an optional externalAccountId to the backend ExternalAccount resource. For Google and Facebook accounts the resource id is the idn_-prefixed identification id, which users.deleteUserExternalAccount() rejects; externalAccountId now exposes the eac_-prefixed id those calls expect. For all other providers id is already the eac_ id and externalAccountId is undefined, so use externalAccountId ?? id to get an id you can delete with. (#8995) by @jacekradko
Updated dependencies [cb76aa2]:
19ce04a]:
c38d853, 7e3174a, 97039bb, f43071d, 0e0ff11, 0039618, a536a0d]:
01789b4]:
preferredSignInStrategyWhenPasswordRequired parameter to clerkClient.instance.update(). Accepts 'password' or 'otp' to override the preferred sign-in strategy when a password is required, or an empty string to clear the override. (#8878) by @dmoernerf4167ec, 17e4164, ed2cf75, 67c04a4, 51c8fdc, c2ba971, 8744728, d9b5c7d]:
Add clerkClient.organizations.replaceOrganizationMetadata(organizationId, params) for replacing an organization's metadata fields in full. (#8787) by @brunol95
Use replaceOrganizationMetadata when the provided metadata should become the complete value for that metadata field:
await clerkClient.organizations.replaceOrganizationMetadata(organizationId, {
publicMetadata: { plan: 'pro' },
});
Use clerkClient.organizations.updateOrganizationMetadata(organizationId, params) when you want to partially update metadata with deep-merge semantics:
await clerkClient.organizations.updateOrganizationMetadata(organizationId, {
publicMetadata: { onboardingComplete: true },
});
The publicMetadata and privateMetadata parameters on clerkClient.organizations.updateOrganization() are now deprecated. They continue to work, but new code should use updateOrganizationMetadata() for partial updates or replaceOrganizationMetadata() for full replacement.
Add clerkClient.users.replaceUserMetadata(userId, params) for replacing a user's metadata fields in full. (#8587) by @brunol95
Use replaceUserMetadata when the provided metadata should become the complete value for that metadata field:
await clerkClient.users.replaceUserMetadata(userId, {
publicMetadata: { plan: 'pro' },
});
Use clerkClient.users.updateUserMetadata(userId, params) when you want to partially update metadata with deep-merge semantics:
await clerkClient.users.updateUserMetadata(userId, {
publicMetadata: { onboardingComplete: true },
});
The publicMetadata, privateMetadata, and unsafeMetadata parameters on clerkClient.users.updateUser() are now deprecated. They continue to work, but new code should use updateUserMetadata() for partial updates or replaceUserMetadata() for full replacement.
Migrate the build pipeline to tsdown and TypeScript 6.0. This is an internal tooling change with no intended changes to the public API or runtime behavior. (#8177) by @dstaley
Updated dependencies [f046c49, b5fa9f6, 3d5b2fe]:
a5c7bc7]:
createClerkClient() now exposes: (#8774) by @dmoerner
organizationPermissions — list, get, create, update, and delete organization permissions.organizationRoles — list, get, create, update, and delete organization roles, plus assign/remove a permission to/from a role.roleSets — list, get, create, update, add roles to, replace a role in, and replace a role set.Fix the return type of clerkClient.organizations.createOrganizationInvitationBulk() to PaginatedResourceResponse<OrganizationInvitation[]>. The Backend API returns the bulk-created invitations in a { data, totalCount } envelope (the same shape as getOrganizationInvitationList()), but the method was typed as OrganizationInvitation[], which did not match the value returned at runtime. (#8751) by @VihAMBR
Return IdPOAuthAccessToken timestamps in milliseconds when an OAuth access token is verified as a JWT. The expiration, createdAt, and updatedAt fields were previously populated with the JWT's raw second-based exp/iat values, making them inconsistent with the same fields on M2MToken and with the values returned when the token is fetched from the API. Comparing expiration against Date.now() now behaves as expected. The expired flag was already computed correctly and is unaffected. (#8771) by @jacekradko
Prevent an unhandled exception when verifying a machine token whose JWT payload has a missing or non-string sub. Such tokens are now classified and rejected with a typed verification error instead of throwing, so a crafted Authorization header can no longer surface as an unhandled error during request authentication. (#8744) by @jacekradko
Redact raw bearer credentials from the auth object's debug output. The debug payload (surfaced when an SDK enables middleware debug logging) previously included full session, machine, refresh, dev-browser and handshake tokens; each now exposes only a short, non-reconstructable prefix, matching how secretKey and jwtKey are already handled. (#8744) by @jacekradko
Add and improve JSDoc comments across public types and methods to support generated reference documentation for the /objects docs section. Exports a few previously-internal types (OnEventListener, OffEventListener, ClerkOptionsNavigation) so they can be referenced from the generated docs. (#8276) by @alexisintech
Updated dependencies [2d6670c, af706e3, 032632c, 0fece6f, b295af3, 8e1bd48]:
users.replaceUserEmailAddress(userId, { emailAddress }) replaces all of a user's email addresses with a single verified, primary email address (PUT /users/{user_id}/email_address).users.replaceUserPhoneNumber(userId, { phoneNumber }) replaces all of a user's phone numbers with a single verified, primary phone number (PUT /users/{user_id}/phone_number).users.createUser now accepts banned and locked parameters to create a user that is already banned or locked.Emit the "session token from cookie is missing the azp claim" warning once per process instead of on every authenticated request. An azp-less cookie token is reused across requests, so the previous unguarded console.warn could flood production logs. (#8698) by @jacekradko
Stop authenticateRequest from consuming the incoming request body, which previously left downstream handlers unable to read it (for example a Hono POST route calling c.req.json()). (#8708) by @jacekradko
Prevent keyless mode from activating in CI and other automated environments in framework SDKs. (#8676) by @mwickett
Preserve custom claims when verifying JWT-format M2M tokens. M2MToken.fromJwtPayload previously hardcoded claims to null, so client.m2m.verify() (and request-level auth()) dropped any custom claims embedded in the token. Custom claims are now reconstructed from the verified payload by stripping only the structural claims the backend adds when minting the token (iss, sub, exp, nbf, iat, jti). User-supplied claims such as aud are preserved. Tokens without custom claims still return claims: null, consistent with the opaque-token path. (#8697) by @jacekradko
Strip private_metadata from the backend resource _raw payload in stripPrivateDataFromObject, preventing it from leaking into __clerk_ssr_state when a User/Organization resource is passed to buildClerkProps. (#8702) by @dominic-clerk
Updated dependencies [afb75e6, c3df67a, 86fd38f, 8d6bb56, 43dfefa, 5fc7b21, c2ba134]:
a036ce8]:
Adds agentTaskId and deprecates taskId to Agent Tasks Create response. (#8013) by @tmilewski
Updated dependencies [95f6c2f]:
4fc38a0]:
Support min_remaining_ttl_seconds for M2M token creation. (#8513) by @wobsoriano
Usage:
clerkClient.m2m.createToken({
machineSecretKey: 'ak_xxxxx',
minRemainingTtlSeconds: 240,
});
Add RoleSetJSON, RoleSetItemJSON, and RoleSetMigrationJSON types matching the BAPI OpenAPI schema. Add role_set_key, last_active_at, and missing_member_with_elevated_permissions to OrganizationJSON. (#8502) by @jacekradko
Updated dependencies [5cda3ee]:
Fix OAuth consent component and hook related types. (#8483) by @SarahSoutoul
Updated dependencies [7a5892f]:
1bfd8ab]:
Auto-proxy FAPI requests for .vercel.app subdomains. When deployed to a .vercel.app domain without explicit proxy or domain configuration, the SDK automatically routes Frontend API requests through /__clerk on the app's own origin. This enables Clerk production mode on Vercel deployments without manual proxy setup. (#8035) by @brkalow
Fix Request cloning and outbound fetch to omit cross-realm AbortSignal. Node 24's bundled undici tightened the instanceof AbortSignal check on RequestInit.signal, which broke: (#8351) by @jacekradko
NextRequest in @clerk/backend's ClerkRequest.Requests passed through patchRequest in @clerk/react-router and @clerk/tanstack-react-start.@clerk/backend's clerkFrontendApiProxy, which forwarded the inbound request's signal to the upstream fetch. Abort propagation will be restored in a follow-up via an in-realm AbortController bridge.Updated dependencies [9b57986, a9f9b29]:
da76490]:
createBootstrapSignedOutState helper to @clerk/backend/internal. Returns a synthetic UnauthenticatedState<'session_token'> without requiring a publishable key or an AuthenticateContext. Intended for framework integrations that need to run authorization logic before real Clerk keys are available (e.g. the Next.js keyless bootstrap window). Accepts optional signInUrl, signUpUrl, isSatellite, domain, and proxyUrl so that createRedirect-driven flows (including cross-origin satellite sign-in with the __clerk_status=needs-sync handshake marker) behave correctly during bootstrap. (#8368) by @jacekradkoA clock skew of 0 will not fall back to the default value anymore. (#8359) by @dominic-clerk
Updated dependencies [d52b311]:
joinPaths (#8331) by @dominic-clerkIntroduce samlConnection and oauthConfig into the EnterpriseConnection resource. (#8326) by @LauraBeatris
The JWT claims are verified after the signature to avoid leaking information through error messages on forged tokens. (#8332) by @dominic-clerk
Updated dependencies [c7b0f47, 34762e8]:
b0b6675]:
dc2de16]:
sec-fetch-dest: document incorrectly triggering handshake redirects, resulting in 405 errors from FAPI. Non-GET requests (e.g. native form submissions) are now excluded from handshake and multi-domain sync eligibility. (#8045) by @jacekradkoExport OrganizationInvitationAcceptedWebhookEvent type. (#8235) by @wobsoriano
Updated dependencies [2c06a5f]: