fix(auth): stop warning when account identity scope is omitted

This commit is contained in:
Taesu
2026-09-04 10:05:59 +09:00
parent 1b47398d40
commit 27de1f669c
8 changed files with 12 additions and 42 deletions
@@ -5,4 +5,4 @@
"better-auth": patch
---
The CLI now generates `account: { identityScope: "provider" }` for new projects. Existing configurations continue to use issuer-scoped identity and log a warning during auth initialization when this option is omitted.
The CLI now generates `account: { identityScope: "provider" }` for new projects. Existing configurations continue to use issuer-scoped identity when this option is omitted.
+1 -1
View File
@@ -133,7 +133,7 @@ npx auth@latest init
* `--database` - The database you want to use. Currently, the only supported database is `SQLite`.
* `--package-manager` - The package manager you want to use. Currently, the only supported package managers are `npm`, `pnpm`, `yarn`, `bun` (defaults to the manager you used to initialize the CLI).
The generated `auth.ts` includes `account: { identityScope: "provider" }`. Existing configurations that omit this option continue to use issuer scope and log a warning during auth initialization.
The generated `auth.ts` includes `account: { identityScope: "provider" }`. Existing configurations that omit this option continue to use issuer scope. `auth migrate plan` reports this compatibility choice.
## Upgrade
@@ -421,7 +421,7 @@ The `"issuer"` scope stores the verified authority. Provider configurations that
The `"provider"` scope stores a deterministic namespace for each configured provider. Provider aliases therefore remain separate accounts. Protocol verification is unchanged by this storage choice.
The `auth init` command selects `"provider"` for new projects. Existing configurations that omit `account.identityScope` continue to use issuer scope and log a warning during auth initialization. Keep the scope consistent across every application instance. Switching scopes on a populated v1.7 database requires an account re-key migration. See the [1.7 upgrade guide](/docs/guides/1-7-upgrade-guide#choose-account-identity-scope) before changing it.
The `auth init` command selects `"provider"` for new projects. Existing configurations that omit `account.identityScope` continue to use issuer scope. `auth migrate plan` reports this compatibility choice. Keep the scope consistent across every application instance. Switching scopes on a populated v1.7 database requires an account re-key migration. See the [1.7 upgrade guide](/docs/guides/1-7-upgrade-guide#choose-account-identity-scope) before changing it.
### List User Accounts
@@ -114,7 +114,7 @@ This covers email and password, social login, the generic OAuth plugin, One Tap,
### Choose account identity scope
Every 1.7 account uses the unique key `(issuer, accountId)`. `account.identityScope` controls the value stored in `issuer`. The `"issuer"` scope stores the verified authority, while `"provider"` stores a deterministic namespace for each configured provider. Configurations generated by `auth init` use `"provider"`. Omitting the option continues to use issuer scope and logs a warning during auth initialization.
Every 1.7 account uses the unique key `(issuer, accountId)`. `account.identityScope` controls the value stored in `issuer`. The `"issuer"` scope stores the verified authority, while `"provider"` stores a deterministic namespace for each configured provider. Configurations generated by `auth init` use `"provider"`. Omitting the option continues to use issuer scope. `auth migrate plan` reports this compatibility choice.
#### Populated 1.6: preserve provider-scoped account identity
@@ -141,7 +141,7 @@ export const auth = betterAuth({
#### Already migrated v1.7: keep issuer identity
Set `account.identityScope` to `"issuer"`. If omitted, the runtime keeps the same identity behavior but logs a warning during auth initialization. In both cases, `auth migrate plan` detects the existing issuer data and `auth migrate apply` makes no account identity changes.
Set `account.identityScope` to `"issuer"`. If omitted, the runtime keeps the same identity behavior. In both cases, `auth migrate plan` detects the existing issuer data and `auth migrate apply` makes no account identity changes.
Use this one-line configuration to make the existing behavior explicit:
+1 -1
View File
@@ -95,7 +95,7 @@ A redirect URL will be automatically generated using the provider ID. For instan
Better Auth verifies the OIDC issuer and `sub` claim before resolving an account. The `mapping` option can map profile fields, but it cannot replace the account subject.
`account.identityScope` only controls the namespace stored after verification. The `"issuer"` scope stores the verified issuer, while `"provider"` stores a deterministic namespace for each provider configuration. Omitting the option continues to use issuer scope and logs a warning during auth initialization. Configurations generated by `auth init` use `"provider"`.
`account.identityScope` only controls the namespace stored after verification. The `"issuer"` scope stores the verified issuer, while `"provider"` stores a deterministic namespace for each provider configuration. Omitting the option continues to use issuer scope. Configurations generated by `auth init` use `"provider"`.
Under issuer scope, provider aliases that return the same issuer and subject resolve to one external account.
+1 -1
View File
@@ -498,7 +498,7 @@ Controls the namespace stored in `issuer` and paired with `accountId` to identif
The `"issuer"` scope stores the verified authority. When a provider has no trusted issuer, Better Auth uses a deterministic provider namespace. The `"provider"` scope always stores a deterministic namespace such as `local:oauth:google`.
The `auth init` command generates `account: { identityScope: "provider" }`. Omitting this option continues to use issuer scope and logs a warning during auth initialization.
The `auth init` command generates `account: { identityScope: "provider" }`. Omitting this option continues to use issuer scope. Auth initialization remains silent, while `auth migrate plan` reports this compatibility choice.
Use `"provider"` when migrating populated 1.6 data to preserve `(providerId, accountId)` identity. Both scopes use the same Account schema and unique `(issuer, accountId)` index.
-6
View File
@@ -1,6 +1,5 @@
import type { AuthContext, BetterAuthOptions } from "@better-auth/core";
import { runWithAdapter } from "@better-auth/core/context";
import { createLogger } from "@better-auth/core/env";
import { BASE_ERROR_CODES, BetterAuthError } from "@better-auth/core/error";
import { getEndpoints, router } from "../api";
import {
@@ -16,11 +15,6 @@ export const createBetterAuth = <Options extends BetterAuthOptions>(
options: Options,
initFn: (options: Options) => Promise<AuthContext>,
): Auth<Options> => {
if (options.account?.identityScope === undefined) {
createLogger(options.logger).warn(
'account.identityScope is omitted; Better Auth v1.7 compatibility mode is using issuer identity. Add account: { identityScope: "issuer" } to make this behavior explicit. For a new database, use account: { identityScope: "provider" } instead. Run auth migrate plan before changing populated account data.',
);
}
const authContext = initFn(options);
const { api } = getEndpoints(authContext, options);
const errorCodes = options.plugins?.reduce((acc, plugin) => {
@@ -10,9 +10,6 @@ import { createAuthContext } from "./create-context";
import { getAwaitableValue } from "./helpers";
describe("base context creation", () => {
const omittedIdentityScopeWarning =
'account.identityScope is omitted; Better Auth v1.7 compatibility mode is using issuer identity. Add account: { identityScope: "issuer" } to make this behavior explicit. For a new database, use account: { identityScope: "provider" } instead. Run auth migrate plan before changing populated account data.';
const initBase = async (options: Partial<BetterAuthOptions> = {}) => {
const opts: BetterAuthOptions = {
baseURL: "http://localhost:3000",
@@ -36,7 +33,7 @@ describe("base context creation", () => {
vi.unstubAllEnvs();
});
it("warns once when account.identityScope is omitted", async () => {
it("does not warn when account.identityScope is omitted", async () => {
const log = vi.fn();
const baseOptions = {
baseURL: "http://localhost:3000",
@@ -49,37 +46,16 @@ describe("base context creation", () => {
...baseOptions,
database: () => adapter,
});
expect(log).toHaveBeenCalledTimes(1);
await auth.$context;
await auth.$context;
expect(log).toHaveBeenCalledTimes(1);
expect(log).toHaveBeenCalledWith("warn", omittedIdentityScopeWarning);
expect(log).not.toHaveBeenCalledWith(
"warn",
expect.stringContaining("account.identityScope"),
);
expect(findOne).not.toHaveBeenCalled();
expect(findMany).not.toHaveBeenCalled();
});
it.each([
"issuer",
"provider",
] as const)("does not warn when account.identityScope is explicitly %s", async (identityScope) => {
const log = vi.fn();
const options = {
baseURL: "http://localhost:3000",
logger: { level: "warn", log },
account: { identityScope },
} satisfies BetterAuthOptions;
const adapter = await getAdapter(options);
const auth = betterAuth({
...options,
database: () => adapter,
});
await auth.$context;
expect(log).not.toHaveBeenCalledWith("warn", omittedIdentityScopeWarning);
});
it("should respect base path", async () => {
const res = await initBase({
basePath: "/custom-path",