new·Earn with mozg — 20% of every monthSend somebody here and take a fifth of every plan payment they make, for as long as they keep paying — not a bounty on the first invoice. Your handle is the link, the window is thirty days, and the commission lands on your balance the second they pay. Free to join: if you have signed in, you already have the link. mozg.sh/earnall news →
mozg.beta
Sign in

Better Auth · Authentication · all subjects

out of scope

49 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Zod dependency upgraded to 4.5

Better Auth 1.7.3 upgrades the packaged Zod dependency to 4.5. Generated OpenAPI schemas now mark required request fields consistently with runtime validation, including passkey registration responses.

Same-origin form submissions with Referrer-Policy: no-referrer

Better Auth 1.7.2 allows same-origin form submissions from pages using Referrer-Policy: no-referrer while continuing to reject untrusted request origins.

Relative callback URLs with standard path, query, and fragment syntax

Better Auth 1.7.2 allows relative callback and redirect URLs to use standard path, query, and fragment syntax while preserving open-redirect protections.

Tilde character allowed in relative callback URLs

Better Auth 1.7.2 allows ~ in relative callback URLs validated by trusted origin checks.

Captcha plugin requires full auth path matching

Better Auth 1.7.0 requires the captcha plugin endpoint entries to match full auth paths unless they use wildcard patterns. This prevents requests like /sign-in//email from bypassing captcha while preserving trailing-slash matches like /sign-in/email/. To protect multiple routes, use explicit wildcards such as /sign-in/* or /sign-in/**.

OAuth device grants use oauthDeviceAuthorization

Better Auth 1.7.0 changes OAuth device grants to use oauthDeviceAuthorization() alongside oauthProvider() or mcp(). This single integration replaces both the standalone deviceCodeGrant() plugin and the shared-grant configuration. Standalone Device Authorization no longer accepts or stores RFC 8707 resources, and onDeviceAuthRequest receives only clientId and scope.

consumePhoneNumberOTP server-only API

Better Auth 1.7.0 adds auth.api.consumePhoneNumberOTP, a server-only API for custom phone OTP flows that need to verify and consume a code without creating or updating users or sessions.

Username plugin displayUsername field can be omitted

Better Auth 1.7.0 allows the username plugin's separate displayUsername field to be omitted by setting displayUsername: false on both the server and client plugins.

Device Authorization unique indexes for deviceCode and userCode

Better Auth 1.7.0 creates unique database indexes for deviceCode and userCode in Device Authorization. Each generated code must be unique in its column. Existing installations on every adapter must resolve duplicate values before applying the migration. MySQL and SQL Server installations must also convert both columns to bounded strings and clean up values longer than 191 characters. Generated codes are limited to 191 characters.

Custom-scheme trustedOrigins match by scheme and authority

Better Auth 1.7.0 changes custom-scheme entries in trustedOrigins to match by scheme and authority instead of string prefix. A host-less entry such as myapp:// or exp:// still trusts every host of that scheme, but a host-bearing entry such as myapp://callback matches that host exactly, preventing satisfaction by myapp://callback.attacker.tld.

Auth instance made fetchable

Better Auth 1.7.0 makes the Auth instance fetchable.

MCP scope wall with RFC 6750 insufficient_scope challenge

Better Auth 1.7.0 enables MCP clients that hit a scope wall to learn exactly which scopes to ask for. Missing protected scopes produce a 403 with an RFC 6750 insufficient_scope WWW-Authenticate challenge that names every missing scope. Configure protected scopes with requiredScopes through RequireMcpAuthOptions or the matching createMcpProtectedRequestHandler verifier option.

MCP plugin moved to @better-auth/mcp package

Better Auth 1.7.0 moves the MCP plugin out of better-auth into its own package, @better-auth/mcp, built on @better-auth/oauth-provider. The in-core MCP client (createMcpAuthClient and its adapters) is removed. OAuth endpoints move from /mcp/* to /oauth2/*, with discovery at /.well-known/oauth-authorization-server. The shared-auth route helper is renamed from withMcpAuth to requireMcpAuth.

DPoP-bound access tokens RFC 9449 support

Better Auth 1.7.0 adds DPoP-bound access token support (RFC 9449) for OAuth provider integrations. Clients request them with dpop_bound_access_tokens at registration, dpop_jkt on the authorization request, or by targeting a resource configured with dpopBoundAccessTokensRequired. Issued tokens carry cnf.jkt, return token_type: DPoP, and stay bound through refresh-token rotation. Resource servers verify DPoP requests with verifyAccessTokenRequest, which checks Authorization: DPoP scheme, proof, request target, access-token hash, and proof replay.

OAuth provider at_hash claim in ID tokens

Better Auth 1.7.0 computes at_hash in ID tokens per OIDC Core §3.1.3.6. ID tokens issued alongside an access token now include the at_hash claim, which cryptographically binds the two tokens to prevent token substitution attacks. The hash algorithm is selected based on the actual signing key's algorithm.

RFC 8628 device flow for OAuth access tokens

Better Auth 1.7.0 enables registered OAuth clients to use the RFC 8628 device flow to obtain OAuth access tokens. Request a code at /device/code and exchange it at /oauth2/token after the user approves it. Device authorization requests can bind RFC 8707 resource indicators. Existing first-party device clients continue to receive Better Auth session tokens from /device/token.

OAuth provider explicit resource configuration

Better Auth 1.7.0 models protected resources explicitly in OAuth provider. Configure them with resources or create them through the oauthResource admin API. Each resource can define token TTLs, allowed scopes, custom JWT claims, and JWT signing pins. validAudiences is removed and each existing resource identifier moves into resources.

organization.getOrganization API

Better Auth 1.7.0 adds organization.getOrganization() to fetch organization metadata without members or invitations.

listUserTeams accepts userId and organizationId parameters

Better Auth 1.7.0 enables listUserTeams API to accept userId and organizationId parameters. userId lets callers list teams for another member of an organization (gated behind member:update permission). organizationId scopes the result to a specific organization without needing to switch the session's active organization.

Hardened private_key_jwt and token endpoint authentication

Better Auth 1.7.0 hardens private_key_jwt and token endpoint client authentication. @better-auth/core/oauth2 exposes encodeBasicCredentials and decodeBasicCredentials following RFC 6749 §2.3.1. createPrivateKeyJwtClientAssertionGetter validates options eagerly; unsupported algorithms, JWKs with no key material, and disagreement between explicit algorithm and JWK-embedded alg throw at construction. @better-auth/oauth-provider accepts client jwks metadata only as RFC 7517 JWK Set object with non-empty keys array.

OAuth2 async request builders for authorization code and refresh

Better Auth 1.7.0 removes synchronous OAuth2 request builders createAuthorizationCodeRequest, createRefreshAccessTokenRequest, and createClientCredentialsTokenRequest. Use the async authorizationCodeRequest, refreshAccessTokenRequest, and clientCredentialsTokenRequest helpers instead.

baseURL dynamic config ignores forwarded headers by default

Better Auth 1.7.0 makes the dynamic baseURL config ignore x-forwarded-host and x-forwarded-proto unless advanced.trustedProxyHeaders: true is set. Requests using baseURL: { allowedHosts } resolve the auth origin from Host by default. If proxy exposes the public hostname only through x-forwarded-host, set advanced.trustedProxyHeaders: true. Deployments where proxy rewrites Host to public hostname are unaffected.

Username immutable option after initial set

Better Auth 1.7.0 adds immutable username option via username plugin, allowing users to set their username during sign-up or first update but preventing changing it to a different value afterwards. Users can still update other profile fields.

oidcProvider plugin removed from better-auth/plugins

Better Auth 1.7.0 removes the deprecated oidcProvider plugin from better-auth/plugins. Migrate OIDC authorization-server integrations to @better-auth/oauth-provider.

Transactional OIDC user resolution

Better Auth 1.7.0 adds transactional OIDC user resolution so applications can link verified issuer and subject pairs to exact existing users while preserving or updating the local profile.

getHttpTestInstance for HTTP listener testing

Better Auth 1.7.0 adds getHttpTestInstance as a counterpart to getTestInstance in better-auth/test. It binds a real HTTP listener on an OS-assigned port and constructs the auth instance against the discovered URL, removing the temp-server-then-rebind race.

resolveSigningKey export for JWKS key resolution

Better Auth 1.7.0 exports resolveSigningKey from better-auth/plugins to resolve the current JWKS signing key including its algorithm.

Cloudflare Workers subpath imports now supported

Better Auth 1.7.0 fixes Cloudflare Workers apps to start when importing Better Auth subpaths such as better-auth/db.

Rate limiting enforcement with determined client IP

Rate limiting is now enforced even when a client IP cannot be determined, instead of being skipped. When baseURL is not configured, password-reset and verification links use the current request's host rather than the host of the first request the server handled.

SIWE message binding to server state

Bind the SIWE signed message to server state before creating a session. The plugin now parses the ERC-4361 message itself and requires its nonce, domain, address, and chain ID to match the server-issued nonce and configured domain, and enforces the message's Expiration Time / Not Before bounds, before verifying the signature. message must now be a valid ERC-4361 message; non-conforming or mismatched messages are rejected with 401 (UNAUTHORIZED_SIWE_MESSAGE_MISMATCH, UNAUTHORIZED_SIWE_MESSAGE_EXPIRED, or UNAUTHORIZED_SIWE_MESSAGE_NOT_YET_VALID).

Rate limit enforcement with concurrent requests

Concurrent requests can no longer slip past the configured rate limit. The in-memory rate-limit store no longer grows without bound, and the database backend removes expired entries on its own. A custom rate-limit storage may implement a new optional consume method for strict enforcement; without it, the previous behavior is kept and a one-time warning is logged.

Rate limit client requests before plugin handlers

Rate limit client requests before plugin request handlers run.

X-Forwarded-For trust and proxy IP handling

Rate limiting no longer trusts multi-hop X-Forwarded-For chains, preventing a client behind an appending proxy from spoofing the leftmost hop to bypass the per-IP rate limit. Single-value IP headers continue to work. To key the real client behind a proxy chain, set advanced.ipAddress.trustedProxies to your reverse-proxy IPs or CIDR ranges (the chain is walked right to left, skipping trusted hops), or point advanced.ipAddress.ipAddressHeaders at a single trusted client-IP header.

Device authorization code user pre-binding

The device authorization plugin now accepts an optional user_id when issuing a device code via /device/code, pre-binding the code to that user. Only the bound user can approve or deny the code, so a publicly visible user code can no longer be claimed by someone else.

Device code redemption single-use enforcement

Polling for a device-authorization token can no longer redeem the same approved device code more than once when several polls arrive together.

Phone number OTP concurrent submission protection

Submitting the same phone-number OTP from several requests at once can no longer sign in more than once or gain extra tries beyond the attempt limit.

SIWE nonce single-use enforcement

A Sign-In with Ethereum nonce can no longer be used to sign in more than once when submitted from several requests at the same time.

Plugin schema table disableMigration flag

Honor disableMigration on plugin schema tables. Tables flagged with disableMigration: true are now skipped by better-auth generate (Drizzle and Prisma output) and by the runtime migrator, instead of being emitted and created anyway.

Captcha provider verification timeout

Captcha provider verification requests now time out after 10 seconds and fail closed, so a slow or unreachable captcha provider can no longer tie up a request indefinitely.

Google reCAPTCHA and Cloudflare Turnstile optional parameters

Google reCAPTCHA and Cloudflare Turnstile accept optional expectedAction and allowedHostnames to reject tokens minted for a different action or hostname.

SIWE email binding constraint

The SIWE plugin no longer binds a provided email that already belongs to another account. With anonymous set to false, /siwe/verify previously created the new account using that email even when it was already in use; it now keeps the wallet-derived address in that case, so one email cannot be attached to two accounts.

OpenAPI user field inclusions for sign-up and update

OpenAPI now includes user.additionalFields and plugin user schema fields (e.g. username plugin username / displayUsername) on /sign-up/email and /update-user request bodies.

Drizzle adapter requires drizzle-orm ^0.45.2

The drizzle adapter peer dependency narrows to `drizzle-orm` ^0.45.2. This tracks the minor line carrying the vulnerability fix and nothing newer, so the adapter only advertises support for versions actually tested against.

Kysely adapter requires kysely ^0.28.14

The kysely adapter peer dependency narrows to `kysely` ^0.28.14. This tracks the minor line carrying the vulnerability fix and nothing newer, so the adapter only advertises support for versions actually tested against.

Restore Kysely 0.28 and 0.29 compatibility

Kysely 0.28 and 0.29 compatibility was restored for SQLite dialect introspection. The dialects now mirror Kysely's stable migration table names locally, avoiding strict ESM build failures in Turbopack without forcing consumers onto Kysely 0.29.

authClient.siwe.getNonce() compatibility alias

`authClient.siwe.getNonce()` is exposed as a compatibility alias for the SIWE nonce endpoint.

Fixed UUID user IDs from create hooks on PostgreSQL adapters

Fixed forced UUID user IDs from create hooks being ignored on PostgreSQL adapters when `advanced.database.generateId` is set to `"uuid"`.

OpenAPI schema for POST /sign-in/social field requirements

The OpenAPI schema for POST /sign-in/social now correctly declares required fields.

OIDC-provider plugin deprecated in favor of oauth-provider

The `oidc-provider` plugin is deprecated in favor of `@better-auth/oauth-provider`. The `oidc-provider` plugin now emits a one-time runtime deprecation warning when instantiated and is marked as `@deprecated` in TypeScript. It will be removed in the next major version.

Give your agent this brain