Every release shipped to Better Auth, straight from GitHub.
Every release shipped to Better Auth, straight from GitHub.
better-authhd (hosted domain) enforcement to verify the hd claim on the verified ID token and callback profile, preventing accounts outside the configured Workspace domain from signing in.verifyAccessToken remote introspection to reject tokens with a missing or mismatching aud claim/admin/create-user and /admin/update-user, and prevent data from overriding protected fields. (#9974)Origin and Referer headers against trustedOrigins even when requests carry no cookies. (#9973)/update-session to reject plugin-managed fields (activeOrganizationId, activeTeamId, impersonatedBy) with a 400 error/update-session and account token routes to immediately reject deleted sessions when cookie cache is enabled alongside database or secondary storage. (#9967)/refresh-token to only trust the account cookie when its userId, providerId, and accountId match the resolved session user.sub.createInvitation and acceptInvitation to validate that all requested team IDs belong to the invitation's organization, preventing cross-organization team membership.oauth_client_id as the user email, preventing all users of the same app from sharing a single email addressdebug_token endpoint, requiring is_valid, a matching app_id, and a client secret for direct sign-in.For detailed changes, see CHANGELOG
@better-auth/oauth-providerauthorization_code from requesting client_credentials tokens./oauth2/continue to derive post-login gate completion from a server-issued session marker rather than the client-submitted postLogin flag.azp claim and a valid client on JWT access tokens, preventing session JWTs from being reported as active access tokens.For detailed changes, see CHANGELOG
@better-auth/ssoAuthnRequest handling to consume the request atomically, preventing replay attacks on concurrent requests. (#9972)For detailed changes, see CHANGELOG
@better-auth/api-key/api-key/create to verify the session against the authoritative store with disableCookieCache: true, preventing revoked sessions from being accepted within the cookie-cache window.For detailed changes, see CHANGELOG
@better-auth/electroncode_challenge_method values.For detailed changes, see CHANGELOG
@better-auth/scimlinkExistingUsers is set, changed org-scoped DELETE to deprovision the user rather than delete the global account, and added canGenerateToken to control SCIM token creation.For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.15...v1.6.16
better-authlistSessions endpoint to properly enforce fresh-age session checks (#9865)unbanUser, setRole, and adminUpdateUser to return USER_NOT_FOUND instead of a generic 500 when the target user does not exist (#9875)For detailed changes, see CHANGELOG
@better-auth/oauth-providerPOST support to the /oauth2/userinfo endpoint, allowing the access token to be passed in the Authorization header (#9937)hooks.before and hooks.after to run correctly when OAuth authorization resumes after sign-in, account selection, or consent (#9919)For detailed changes, see CHANGELOG
@better-auth/kysely-adapterFor detailed changes, see CHANGELOG
@better-auth/passkeygetAuthenticatorName(aaguid) and commonAuthenticatorNames so passkeys can display a friendly provider name like "1Password" or "Google Password Manager" (#9927)For detailed changes, see CHANGELOG
@better-auth/ssoERR_SUBJECT_UNCONFIRMED errors caused by clockSkew not being forwarded to samlify's ServiceProvider when validating SAML responses (#9748)For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.14...v1.6.15
better-authnull values being rejected for optional fields in the generated database schema (#9841)getSessionCookie to prefer the __Secure- prefixed cookie over a non-secure leftover, preventing a stale cookie from shadowing the current session (#9806)For detailed changes, see CHANGELOG
@better-auth/ssoFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.13...v1.6.14
better-authaccountInfo calls with an optional userId parameter, allowing trusted callers to read provider profiles without constructing session headers (#9813)viewBackupCodes is a server-only function not accessible via HTTP in its API documentation (#9822)storeStateStrategy defaulting to "cookie" instead of "database" when only secondaryStorage is configured, preventing oversized-cookie errors on platforms like AWS Lambda (#9591)updateUserInfoOnLink not being applied when linking accounts through the standard OAuth redirect flow (#8758)oidc-provider and mcp plugins accepting invalid redirect_uri schemes such as javascript: and data: (#9838)logo not accepting null, preventing users from clearing an existing logo on create and update (#9842)For detailed changes, see CHANGELOG
@better-auth/ssointernalAdapter helper methods that could silently match the wrong account or wipe all sessions for a user (#9818)samlify to 2.13.1 (GHSA-34r5-q4jw-r36m) (#9821)For detailed changes, see CHANGELOG
@better-auth/api-keyverifyApiKey rejecting keys created under a non-default configId when the request omitted configId (#9794)For detailed changes, see CHANGELOG
@better-auth/coreconsumeOne when an adapter's deleteMany returned a non-numeric value, now surfacing a clear error (#9831)For detailed changes, see CHANGELOG
@better-auth/expoFor detailed changes, see CHANGELOG
@better-auth/oauth-providerPOST /oauth2/register bypassing the clientPrivileges create check, allowing unauthorized dynamic client registration (#9837)For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.12...v1.6.13
better-authgetMigration migrations. (#9691)changeEmail to return an error when emailVerification.sendVerificationEmail is missing, and URL-encoded callbackURL in verify-email links. (#9614)callbackURL URL-encoding in verify-email links for OAuth account linking and username sign-in. (#9792)role.authorize to reject empty action lists and correctly evaluate OR conditions on unknown resources. (#9603)AdminClientOptions and OrganizationClientOptions. (#9642)parseJSON to properly decode escape sequences in quoted strings. (#9617)getTrustedOrigins to respect the dynamic baseURL protocol option. (#9644)sendVerificationEmail callback. (#9619)accessTokenExpiresIn config option to genericOAuth for providers that omit expires_in in their token response. (#9799)user_creation_failed. (#9723)state_mismatch when production and preview environments use different secrets. (#9385)state_not_found, state_invalid, state_mismatch) instead of the generic please_restart_the_process code. (#9788)errorCallbackURL instead of the default error page. (#9789)operationIds for endpoints that expose multiple HTTP methods. (#9721)deleteOrganization and removeMember to roll back on failure instead of leaving orphaned rows. (#9630)session_token and session_data cookies to be captured and replayed to bypass 2FA when cookie caching is enabled. (#9639)createUser endpoint. (#9464)For detailed changes, see CHANGELOG
@better-auth/oauth-providerregistration_endpoint to be hidden from .well-known metadata unless dynamic client registration is enabled. (#9448)client_secret values containing colons. (#9601)NOT_FOUND when the referenced client no longer exists. (#9600)For detailed changes, see CHANGELOG
@better-auth/coretoCamelCase, toSnakeCase, toPascalCase, and toKebabCase utilities to @better-auth/core/utils/string. (#9727) – 
verifyAccessToken to return proper unauthorized errors for invalid token verification failures. (#9655)For detailed changes, see CHANGELOG
@better-auth/ssoerrorCallbackURL instead of returning a JSON error. (#9702)For detailed changes, see CHANGELOG
@better-auth/drizzle-adapterFor detailed changes, see CHANGELOG
@better-auth/passkeytransports is undefined. (#9746)For detailed changes, see CHANGELOG
@better-auth/api-keyTS4023 declaration emit errors by adding better-call as a peer dependency. (#9759)For detailed changes, see CHANGELOG
@better-auth/electronFor detailed changes, see CHANGELOG
@better-auth/kysely-adapterBunSqliteDialect, NodeSqliteDialect) incorrectly reporting tables as views. (#9615)For detailed changes, see CHANGELOG
@better-auth/stripeFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.11...v1.6.12
better-authchange-email-disabled response to help clients identify the rejection reason (#8948)onLinkAccount when email verification triggers auto sign-in (#9548)oidc-provider and mcp plugins to require client_secret for confidential clients on refresh token grants and use constant-time secret comparison (#9576)oidc-provider and mcp plugins to follow OAuth 2.1: removed "none" from advertised signing algorithms, defaulted plain PKCE off, and rejected incomplete PKCE parameters (#9575)requireEmailVerificationOnInvitation by default and extending the verification gate to getInvitation and listUserInvitations (#9577)For detailed changes, see CHANGELOG
@better-auth/oauth-providerunique constraint on oauthRefreshToken.tokenFor detailed changes, see CHANGELOG
@better-auth/coreadvanced.ipAddress.ipv6Subnet to accept any valid IPv6 prefix length (0-128) instead of a narrow set of values (#9545)For detailed changes, see CHANGELOG
@better-auth/scimgenerateSCIMToken to reject providerId values that collide with built-in account providers, preventing tokens from authenticating against unintended accounts (#9579)For detailed changes, see CHANGELOG
@better-auth/ssoFor detailed changes, see CHANGELOG
authclaimOne adapter primitive for consuming database rows without race conditions (#9560)claimOne adapter primitive to consumeOne and added internalAdapter.consumeVerificationValue for atomically consuming verification rows (#9568)For detailed changes, see CHANGELOG
@better-auth/api-keyFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.10...v1.6.11
better-authrefreshUserSessions on the internal adapter (#7764)POST /sign-in/social mis-declaring required fields (#9268)useSession not revalidating after admin impersonation starts or stops (#9402)Set-Cookie headers being emitted on redirect responses from social sign-in and magic-link endpoints (#9497)emailAndPassword.autoSignIn is false (#8839)TypeError caused by non-ASCII characters in OAuth error descriptions on redirect (#9065)internalAdapter.deleteAccount parameter from accountId to id to reflect that it queries by primary key (#9503)undefined id (#9456)cancelPendingInvitationsOnReInvite having no effect, where re-inviting the same email always returned USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION (#9453)additionalFields in the organization plugin (#9349)useActiveMemberRole retaining a previous user's role after sign-out in SPA flows (#9440)setActiveTeam to only accept teams from the currently active organization (#9239)authClient.siwe.getNonce() as a compatibility alias for the SIWE nonce endpoint (#9461)callbackURL being ignored on signIn.username, so it now redirects correctly like signIn.email (#9475)For detailed changes, see CHANGELOG
@better-auth/oauth-providersessionId typing in refresh token types to be optional, matching the schema (#9324)prompt=login consent continuations not completing after a forced loginprompt=login not being honored after consent continuation, preventing session bypass (#9344)For detailed changes, see CHANGELOG
@better-auth/stripeonSubscriptionUpdate to receive the raw stripeSubscription object, and fixed onSubscriptionCancel to receive the post-update subscription row instead of a stale snapshot (#9354)getCheckoutSessionParams overriding internally managed Stripe Checkout Session fields such as success_url, cancel_url, customer, and line_items (#9481)onSubscriptionDeleted, onTrialEnd, and onTrialExpired receiving a stale pre-update subscription snapshot instead of the post-update row (#9356)getCheckoutSessionParams overriding free trial and internal metadata, which could hide trial periods and create duplicate subscription rows on webhook (#9474)For detailed changes, see CHANGELOG
@better-auth/api-keyapi.verifyApiKey not validating the key's configId against the request body (#9393)For detailed changes, see CHANGELOG
@better-auth/coreFor detailed changes, see CHANGELOG
@better-auth/passkeyFor detailed changes, see CHANGELOG
@better-auth/sso/sso/saml2/sp/metadata throwing NOT_FOUND for providers configured via defaultSSO (#9398)For detailed changes, see CHANGELOG
authauth init generating broken MySQL and PostgreSQL Kysely database configs (#9455)For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.9...v1.6.10
better-authFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.8...v1.6.9
better-authmapProfileToUser fallback for OAuth providers that may omit email from their profile response (#9331)id through beforeCreateTeam and beforeCreateInvitation hooks (#9253)For detailed changes, see CHANGELOG
@better-auth/oauth-providerstate parameter (#9328)For detailed changes, see CHANGELOG
@better-auth/passkeyexactOptionalPropertyTypes compiler option (#9270)For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.7...v1.6.8
better-authAPIError is thrown (#9211)./instrumentation module in those environments (#9281)callbackOnVerification not being called when updatePhoneNumber is enabled (#4894)For detailed changes, see CHANGELOG
@better-auth/oauth-providerAuthorization header from request context when using auth.api (#9244)For detailed changes, see CHANGELOG
@better-auth/passkeyFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.6...v1.6.7
better-authPartitioned attribute when forwarding Set-Cookie headers (#9235)disableRefresh query parameter in custom session validation (#9214)updateUser({ phoneNumber: null }) (#9219)For detailed changes, see CHANGELOG
@better-auth/coremapConcurrent, a bounded-concurrency async utility, at @better-auth/core/utils/async (#9227) – 
@opentelemetry/api an optional peer dependency (#9111) – 
For detailed changes, see CHANGELOG
@better-auth/api-keyFor detailed changes, see CHANGELOG
@better-auth/expoSecureStore on app startup, eliminating the login screen flash for returning users (#8953)For detailed changes, see CHANGELOG
@better-auth/oauth-providerFor detailed changes, see CHANGELOG
@better-auth/ssoFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.5...v1.6.6
better-auth/change-password and /revoke-other-sessions (#9087)For detailed changes, see CHANGELOG
@better-auth/oauth-provider@better-auth/oauth-provider where unprivileged authenticated users could create OAuth clients when deployments relied on clientPrivileges to restrict client creation.
– 
@better-auth/oauth-provider@1.6.5.
– 

1.7.0-beta.0 and 1.7.0-beta.1) remains affected until a fixed beta release is published.For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.4...v1.6.5
better-authforceAllowId UUIDs set in database hooks being ignored on PostgreSQL adapters when advanced.database.generateId is set to "uuid" (#9068)For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.3...v1.6.4
better-authoperationId for the requestPasswordResetCallback endpoint in the OpenAPI spec (#9072)baseURL resolution from request headers for direct auth.api calls (#9113)isMounted race condition that caused excessive requests per second in the client (#9078)storeBackupCodes storage strategy after verification (#7231)For detailed changes, see CHANGELOG
@better-auth/oauth-providercustomTokenResponseFields callback for injecting custom fields into token endpoint responses, and hardened authorization code validation (#9118)baseURL resolution for direct auth.api calls and plugin metadata helpers (#9131)For detailed changes, see CHANGELOG
@better-auth/ssoFor detailed changes, see CHANGELOG
@better-auth/stripeFor detailed changes, see CHANGELOG
authFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.2...v1.6.3
better-authMigration: Schema migration required.
Add the
verifiedcolumn to thetwoFactortable, then regenerate/apply your ORM migration.
- Prisma: run
npx auth@latest generate, thennpx prisma migrate dev(ornpx prisma db push) andnpx prisma generate.- Drizzle: run
npx auth@latest generate, thennpx drizzle-kit generateandnpx drizzle-kit migrate.
Existing rows do not need a backfill because the column defaults to
true.
nextCookies() by replacing cookie probe with header-based RSC detection (#9059)RelayState in signed SAML AuthnRequests (#9058)For detailed changes, see CHANGELOG
@better-auth/oauth-providerskip_consent at schema level in dynamic client registration (#8998)For detailed changes, see CHANGELOG
@better-auth/ssoFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.1...v1.6.2
better-authINVALID_PASSWORD for all checkPassword failures (#8902)getSession accessibility in generic Auth<O> context (#9017)For detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.6.0...v1.6.1
Blog post: Better Auth 1.6
better-authfreshAge calculation with session creation time instead of update time (#8762)Migration:
session.freshAgenow calculates fromcreatedAt. Setsession: { freshAge: 0 }to disable the check entirely.
resendStrategy option to reuse existing OTP in email-otp plugin (#8560)enable option for HaveIBeenPwned plugin (#8728)sendMagicLink callback (#8571)secret option to OAuth proxy to reduce shared key exposure (#8699)organizationId parameter in team endpoints (#5062)twoFactorPage config option for custom 2FA page routing (#5329)oidc-provider plugin in favor of @better-auth/oauth-provider (#8985) – 
accountId instead of internal id (#8786)skipOriginCheck array handling (#8582)user field through idToken sign-in body for Apple name support (#8417)operationId in admin plugin endpoints (#8570)sendOTP failures instead of silently swallowing them (#8842)cookieCache maxAge to match session.expiresIn (#8648)autoSignIn: false without requireEmailVerification (#8521)accountInfo endpoint to use accountId instead of internal id (#8346)createAdapter and type exports for backwards compatibility (#8461)Response return for HTTP request contexts (#7521)throw: true handling in client session refresh (#8610)generateId: "uuid" over adapter customIdGenerator (#8679)safeJSONParse for pre-parsed objects (#8248)CREATE INDEX (#8538)sessionSignal after requesting email change in email-otp (#8816)/magic-link/verify (#7223)form_post (#8895)storeIdentifier is hashed (#8980)redirect_uri validation for prompt=none in oidc-provider (#8398)listUserInvitations (#8694)twoFactorTable option to schema modelName (#8443)any from collapsing auth.$Infer and client inference types (#8981)updateUser to not overwrite unrelated username fields (#7570)updateUser (#8731)For detailed changes, see CHANGELOG
@better-auth/ssoMigration: Set
sso({ saml: { enableInResponseToValidation: false } })to restore the previous behavior.
node-forge vulnerability via samlify pin (#8838)sub claim correctly (#8276)provisionUser inconsistency and added provisionUserOnEveryLogin option (#8818)internalAdapter (#8353)For detailed changes, see CHANGELOG
@better-auth/mongo-adapterMigration: New documents use native BSON UUIDs. Existing string UUIDs continue to work. No data migration required.
For detailed changes, see CHANGELOG
@better-auth/oauth-providerisLocalhost function (#8286)customIdTokenClaims to override standard claims (#7865)baseURL config handling in init (#8649)oauth_query in client plugin (#8320)customIdTokenClaims to override acr and auth_time (#8633)auth_time timestamps across adapter shapes (#8761)skip_consent (#8632)prompt=none support (#8554)For detailed changes, see CHANGELOG
@better-auth/stripeprorationBehavior per plan (#8525)customerType check (#8609){CHECKOUT_SESSION_ID} placeholder in success callbackURL (#8568)priceId for annual subscriptions in list (#8810)For detailed changes, see CHANGELOG
@better-auth/drizzle-adaptermode: "insensitive") (#8556)IS NULL / IS NOT NULL for null value comparisons (#8660)For detailed changes, see CHANGELOG
@better-auth/exporequire issue (#8253)For detailed changes, see CHANGELOG
@better-auth/prisma-adapterupdateMany fallback for non-unique updates (#8524)deleteMany when deleting by non-unique field (#8314)For detailed changes, see CHANGELOG
authmcp.better-auth.com (#8747)required as true in Drizzle and Prisma generators (#8614)For detailed changes, see CHANGELOG
@better-auth/electronsafeStorage encryption failures gracefully (#8530)For detailed changes, see CHANGELOG
@better-auth/passkeyFor detailed changes, see CHANGELOG
@better-auth/test-utils@better-auth/test-utils/adapter (#8564) – 
using keyword for runtime compatibility (#8756)For detailed changes, see CHANGELOG
@better-auth/api-keyFor detailed changes, see CHANGELOG
@better-auth/coreAPIErrors as span errors in OpenTelemetry traces (#8850)For detailed changes, see CHANGELOG
@better-auth/kysely-adapternumUpdatedOrDeletedRows from D1 dialect (#8798)For detailed changes, see CHANGELOG
@better-auth/telemetryFor detailed changes, see CHANGELOG
Thanks to everyone who contributed to this release:
Full changelog: v1.5.6...v1.6.0



resendStrategy option to reuse existing OTP – 



organizationId in team endpoints – 



prorationBehavior per plan – 
@better-auth/test-utils/adapter – 
twoFactorPage in config – 
skipOriginCheck array – 

user field through idToken sign-in body for Apple name support – 






autoSignIn: false without requireEmailVerification – 





CREATE INDEX for postgres migration – 
















