[GH-ISSUE #2402] Make email field optional #17812

Closed
opened 2026-04-15 16:08:20 -05:00 by GiteaMirror · 19 comments
Owner

Originally created by @dsonet on GitHub (Apr 22, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/2402

Is this suited for github?

  • Yes, this is suited for github

Right now, email is required for a user, I want that user to be able to just signup/login with a mobile number.

Describe the solution you'd like

Can we make the email optional, so a user can just sign up with mobile or username?

Describe alternatives you've considered

This should be more robust, it would be good if we could support user signup with username/email/mobile, even second email/mobile, we could introduce a login table with columns id, identifier, verified to store all the user/email/mobile. the user table can reference this table One->Many. This way we can eliminate the username, email, emailVerified, phoneNumber, phoneVerified from the user table, since we have unified the login identities.

This would also simplify the username, phone-number, email-otps underlying logic, and can support more scenarios.

Additional context

N/A

Originally created by @dsonet on GitHub (Apr 22, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/2402 ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. Right now, email is required for a user, I want that user to be able to just signup/login with a mobile number. ### Describe the solution you'd like Can we make the email optional, so a user can just sign up with mobile or username? ### Describe alternatives you've considered This should be more robust, it would be good if we could support user signup with username/email/mobile, even second email/mobile, we could introduce a `login` table with columns `id`, `identifier`, `verified` to store all the user/email/mobile. the user table can reference this table `One->Many`. This way we can eliminate the `username`, `email`, `emailVerified`, `phoneNumber`, `phoneVerified` from the `user` table, since we have unified the login identities. This would also simplify the `username`, `phone-number`, `email-otp`s underlying logic, and can support more scenarios. ### Additional context N/A
GiteaMirror added the breakingenhancementlocked labels 2026-04-15 16:08:20 -05:00
Author
Owner

@ping-maxwell commented on GitHub (Apr 23, 2025):

We will make fields more dynamic/configurable in the future.

<!-- gh-comment-id:2824147883 --> @ping-maxwell commented on GitHub (Apr 23, 2025): We will make fields more dynamic/configurable in the future.
Author
Owner

@ChetanReddyC commented on GitHub (Apr 23, 2025):

Can I work on this I've already set upped this project locally!

<!-- gh-comment-id:2824361570 --> @ChetanReddyC commented on GitHub (Apr 23, 2025): Can I work on this I've already set upped this project locally!
Author
Owner

@HimanshuKumarDutt094 commented on GitHub (Apr 30, 2025):

Is there any update on this, we are working on a project where email has 0 use case. We tried to use just phone number plugin but our entire turborepo crashes with 500 error. We are following create t3 turbo with better auth.

<!-- gh-comment-id:2842066937 --> @HimanshuKumarDutt094 commented on GitHub (Apr 30, 2025): Is there any update on this, we are working on a project where email has 0 use case. We tried to use just phone number plugin but our entire turborepo crashes with 500 error. We are following create t3 turbo with better auth.
Author
Owner

@SARAsBooks commented on GitHub (May 3, 2025):

I'm also interested in this feature. It's complex because to do it right you need to get to the data structure, which means breaking changes or added abstraction layer. I might try to tackle it, but I've never done something this complex before. Open to anyone weighing in on the specification before getting started.

Proposal: Flexible User Identity with Identifier Table Architecture

Branch: feat/identifier-table

feat/identifier-table/CONTRIBUTING.md

Summary

This branch will implement a flexible identifier system using a dedicated Identifier table, allowing users to authenticate with email, username, phone number, or other identity types without requiring email as a mandatory field.

Problem Statement

Currently, the Better Auth system requires email for all users. This limits flexibility for applications that want to support alternative login methods like phone numbers or usernames without requiring email.

Proposed Solution

Implement the Identifier Table architecture using the Abstraction Layer approach to maintain backward compatibility while enabling new flexible authentication patterns.

Implementation Strategy

  1. Abstraction Layer Approach

    • Create a code-level abstraction that maps between existing API interface and new database structure
    • Maintain backward compatibility through virtual fields and query transformation
    • Avoid dual-schema synchronization in favor of a cleaner implementation
    ┌────────────────────┐      ┌─────────────────────┐      ┌─────────────────┐
    │                    │      │                     │      │                 │
    │   Legacy API       │─────▶│  Abstraction Layer  │─────▶│  New Database   │
    │   Interface        │      │  (Virtual Fields)   │      │     Schema      │
    │                    │◀─────│                     │◀─────│                 │
    └────────────────────┘      └─────────────────────┘      └─────────────────┘
    
  2. Schema Changes

    • Add new Identifier table with fields for type, value, verification status, and authentication data
    • Keep existing fields on User table initially but make them virtual/computed
    • Implement appropriate indices for efficient lookup
  3. API Evolution

    • Maintain backwards compatibility with existing API endpoints
    • Add new endpoints that embrace flexible identifier concepts
    • Document clear migration paths for developers
  4. Recovery Strategy

    • Implement a recovery classification system (Full, Partial, Minimal, Pseudonymous)
    • Provide clear UX patterns for proactive recovery setup
    • Allow developers to configure minimum recovery requirements
  5. Performance Configuration

    • Provide configuration options to control the data structure mode
    • Allow developers to optimize for performance or compatibility based on their needs

Technical Implementation

1. Database Schema

// New Identifier table
model Identifier {
  id              String    @id @default(uuid())
  userId          String
  type            String    // "email", "phone", "username", "passkey", "oauth"
  value           String
  verified        Boolean   @default(false)
  passwordHash    String?   // For credential-based identifiers
  createdAt       DateTime  @default(now())
  updatedAt       DateTime  @updatedAt
  metadata        Json?     // For provider-specific details
  
  user            User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  
  @@unique([type, value])  // Ensure identifiers are unique by type+value
  @@index([userId])         // For efficient lookup of all user identifiers
}

// Update Invitation model to reference identifiers
model Invitation {
  // Existing fields...
  identifierId    String?   // New field, replaces email
  identifierType  String?   // New field
  email           String?   // Keep for backward compatibility
  // Other fields...
}

2. Abstraction Layer

// Virtual fields in User model
{
  // Real fields in database
  id: string,
  name: string,
  // ...
  
  // Virtual fields (computed from identifiers)
  get email() {
    return this._identifiers?.find(i => i.type === 'email')?.value;
  },
  
  get emailVerified() {
    return this._identifiers?.find(i => i.type === 'email')?.verified || false;
  },
  
  // Methods to modify virtual fields
  async setEmail(newEmail: string) {
    // Implementation that updates or creates email identifier
  }
}

// Query transformation for backward compatibility
function transformQuery(model: string, query: any) {
  if (model !== "user") return query;
  
  const newQuery = { ...query };
  
  // Transform email queries to identifier queries
  if (newQuery.where?.email) {
    const emailValue = newQuery.where.email;
    delete newQuery.where.email;
    
    newQuery.where.identifiers = {
      some: {
        type: "email",
        value: emailValue
      }
    };
  }
  
  return newQuery;
}

3. Recovery Classification

enum RecoveryLevel {
  FULL = "full",         // Has verified email/phone
  PARTIAL = "partial",   // Has social logins
  PSEUDONYMOUS = "pseudonymous", // Password/Passkey/credential only
  ANONYMOUS = "anonymous" // Anonymous account
}

// Virtual property on user model
get recoveryStatus(): RecoveryLevel {
  const identifiers = this._identifiers || [];
  
  // Check for verified email or phone
  const hasVerifiedContactPoint = identifiers.some(
    id => (id.type === "email" || id.type === "phone") && id.verified
  );
  if (hasVerifiedContactPoint) return RecoveryLevel.FULL;
  
  // Additional classification logic...
}

4. Performance Configuration

// In auth config
{
  identifierTable: {
    mode: "virtual" | "direct" | "legacy",
    // virtual = abstraction layer (default)
    // direct = new structure only (best performance)
    // legacy = old structure only (backward compatibility)
    
    migrateExistingData: boolean, // whether to migrate data on mode change
    warnOnLegacyUsage: boolean // emit warnings when using deprecated patterns
  }
}

This configuration gives developers full control over performance tradeoffs:

  • Applications that prioritize backward compatibility can use "virtual" mode
  • Applications that prioritize performance can use "direct" mode
  • Legacy applications can stay on "legacy" mode temporarily

Test Strategy

  1. Extend existing tests to verify both traditional and new patterns
  2. Add new tests specifically for flexible identifier scenarios
  3. Create migration tests to verify data integrity during transitions

Key test files to update:

  • /packages/better-auth/src/api/routes/sign-up.test.ts
  • /packages/better-auth/src/api/routes/sign-in.test.ts
  • /packages/better-auth/src/api/routes/email-verification.test.ts
  • /packages/better-auth/src/plugins/magic-link/magic-link.test.ts
  • /packages/better-auth/src/test-utils/test-instance.ts

Benefits

  1. Greater Authentication Flexibility

    • Support for email-less registration
    • Multiple authentication methods per user
    • Better support for international users who prefer phone-based authentication
  2. Improved User Experience

    • Allow users to choose their preferred authentication method
    • Support progressive identity building
    • Provide clear recovery options based on available identifiers
  3. Future-Proof Architecture

    • Easily add new identifier types
    • Better support for passwordless authentication
    • Simplified plugin architecture for authentication methods
  4. Performance and Compatibility Control

    • Configuration options to balance performance and compatibility
    • Documented benchmarks for making informed decisions
    • Smooth migration path for existing applications

Risks and Mitigations

  1. Risk: Breaking changes for existing applications

    Mitigation: Abstraction layer maintains backward compatibility

  2. Risk: Performance impact from abstraction layer

    Mitigation:

    • Optimize queries and use appropriate indices
    • Provide configuration options to control data structure mode
    • Allow direct mode for best performance when compatibility isn't needed
  3. Risk: Account recovery challenges

    Mitigation: Implement recovery classification and clear UX recommendations

Next Steps

  1. Create feature branch
  2. Create test suite
  3. Implement Identifier table schema
  4. Build abstraction layer
  5. Update core authentication flows
  6. Document migration path
<!-- gh-comment-id:2848345810 --> @SARAsBooks commented on GitHub (May 3, 2025): I'm also interested in this feature. It's complex because to do it right you need to get to the data structure, which means breaking changes or added abstraction layer. I might try to tackle it, but I've never done something this complex before. Open to anyone weighing in on the specification before getting started. # Proposal: Flexible User Identity with Identifier Table Architecture ## Branch: `feat/identifier-table` [feat/identifier-table/CONTRIBUTING.md](https://github.com/SARAsBooks/better-auth/blob/feat/identifier-table/feat/identifier-table/CONTRIBUTING.md) ## Summary This branch will implement a flexible identifier system using a dedicated Identifier table, allowing users to authenticate with email, username, phone number, or other identity types without requiring email as a mandatory field. ## Problem Statement Currently, the Better Auth system requires email for all users. This limits flexibility for applications that want to support alternative login methods like phone numbers or usernames without requiring email. ## Proposed Solution Implement the Identifier Table architecture using the Abstraction Layer approach to maintain backward compatibility while enabling new flexible authentication patterns. ## Implementation Strategy 1. **Abstraction Layer Approach** - Create a code-level abstraction that maps between existing API interface and new database structure - Maintain backward compatibility through virtual fields and query transformation - Avoid dual-schema synchronization in favor of a cleaner implementation ``` ┌────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │ │ │ │ │ │ │ Legacy API │─────▶│ Abstraction Layer │─────▶│ New Database │ │ Interface │ │ (Virtual Fields) │ │ Schema │ │ │◀─────│ │◀─────│ │ └────────────────────┘ └─────────────────────┘ └─────────────────┘ ``` 1. **Schema Changes** - Add new Identifier table with fields for type, value, verification status, and authentication data - Keep existing fields on User table initially but make them virtual/computed - Implement appropriate indices for efficient lookup 2. **API Evolution** - Maintain backwards compatibility with existing API endpoints - Add new endpoints that embrace flexible identifier concepts - Document clear migration paths for developers 3. **Recovery Strategy** - Implement a recovery classification system (Full, Partial, Minimal, Pseudonymous) - Provide clear UX patterns for proactive recovery setup - Allow developers to configure minimum recovery requirements 4. **Performance Configuration** - Provide configuration options to control the data structure mode - Allow developers to optimize for performance or compatibility based on their needs ## Technical Implementation ### 1. Database Schema ```typescript // New Identifier table model Identifier { id String @id @default(uuid()) userId String type String // "email", "phone", "username", "passkey", "oauth" value String verified Boolean @default(false) passwordHash String? // For credential-based identifiers createdAt DateTime @default(now()) updatedAt DateTime @updatedAt metadata Json? // For provider-specific details user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([type, value]) // Ensure identifiers are unique by type+value @@index([userId]) // For efficient lookup of all user identifiers } // Update Invitation model to reference identifiers model Invitation { // Existing fields... identifierId String? // New field, replaces email identifierType String? // New field email String? // Keep for backward compatibility // Other fields... } ``` ### 2. Abstraction Layer ```typescript // Virtual fields in User model { // Real fields in database id: string, name: string, // ... // Virtual fields (computed from identifiers) get email() { return this._identifiers?.find(i => i.type === 'email')?.value; }, get emailVerified() { return this._identifiers?.find(i => i.type === 'email')?.verified || false; }, // Methods to modify virtual fields async setEmail(newEmail: string) { // Implementation that updates or creates email identifier } } // Query transformation for backward compatibility function transformQuery(model: string, query: any) { if (model !== "user") return query; const newQuery = { ...query }; // Transform email queries to identifier queries if (newQuery.where?.email) { const emailValue = newQuery.where.email; delete newQuery.where.email; newQuery.where.identifiers = { some: { type: "email", value: emailValue } }; } return newQuery; } ``` ### 3. Recovery Classification ```typescript enum RecoveryLevel { FULL = "full", // Has verified email/phone PARTIAL = "partial", // Has social logins PSEUDONYMOUS = "pseudonymous", // Password/Passkey/credential only ANONYMOUS = "anonymous" // Anonymous account } // Virtual property on user model get recoveryStatus(): RecoveryLevel { const identifiers = this._identifiers || []; // Check for verified email or phone const hasVerifiedContactPoint = identifiers.some( id => (id.type === "email" || id.type === "phone") && id.verified ); if (hasVerifiedContactPoint) return RecoveryLevel.FULL; // Additional classification logic... } ``` ### 4. Performance Configuration ```typescript // In auth config { identifierTable: { mode: "virtual" | "direct" | "legacy", // virtual = abstraction layer (default) // direct = new structure only (best performance) // legacy = old structure only (backward compatibility) migrateExistingData: boolean, // whether to migrate data on mode change warnOnLegacyUsage: boolean // emit warnings when using deprecated patterns } } ``` This configuration gives developers full control over performance tradeoffs: - Applications that prioritize backward compatibility can use "virtual" mode - Applications that prioritize performance can use "direct" mode - Legacy applications can stay on "legacy" mode temporarily ## Test Strategy 1. Extend existing tests to verify both traditional and new patterns 2. Add new tests specifically for flexible identifier scenarios 3. Create migration tests to verify data integrity during transitions Key test files to update: - `/packages/better-auth/src/api/routes/sign-up.test.ts` - `/packages/better-auth/src/api/routes/sign-in.test.ts` - `/packages/better-auth/src/api/routes/email-verification.test.ts` - `/packages/better-auth/src/plugins/magic-link/magic-link.test.ts` - `/packages/better-auth/src/test-utils/test-instance.ts` ## Benefits 1. **Greater Authentication Flexibility** - Support for email-less registration - Multiple authentication methods per user - Better support for international users who prefer phone-based authentication 2. **Improved User Experience** - Allow users to choose their preferred authentication method - Support progressive identity building - Provide clear recovery options based on available identifiers 3. **Future-Proof Architecture** - Easily add new identifier types - Better support for passwordless authentication - Simplified plugin architecture for authentication methods 4. **Performance and Compatibility Control** - Configuration options to balance performance and compatibility - Documented benchmarks for making informed decisions - Smooth migration path for existing applications ## Risks and Mitigations 1. **Risk**: Breaking changes for existing applications **Mitigation**: Abstraction layer maintains backward compatibility 2. **Risk**: Performance impact from abstraction layer **Mitigation**: - Optimize queries and use appropriate indices - Provide configuration options to control data structure mode - Allow direct mode for best performance when compatibility isn't needed 3. **Risk**: Account recovery challenges **Mitigation**: Implement recovery classification and clear UX recommendations ## Next Steps 1. Create feature branch 2. Create test suite 3. Implement Identifier table schema 4. Build abstraction layer 5. Update core authentication flows 6. Document migration path
Author
Owner

@dinfer commented on GitHub (May 30, 2025):

Any progress?
I also want to know why email is required when only using username plugin.

<!-- gh-comment-id:2920967717 --> @dinfer commented on GitHub (May 30, 2025): Any progress? I also want to know why email is required when only using username plugin.
Author
Owner

@VIEWVIEWVIEW commented on GitHub (Jun 2, 2025):

Any progress? I also want to know why email is required when only using username plugin.

@ping-maxwell any chance to get this added to the 1.3.0 milestone? #2172 is included in that milestone too. This and that issue both relate to the same problem.

<!-- gh-comment-id:2930875403 --> @VIEWVIEWVIEW commented on GitHub (Jun 2, 2025): > Any progress? I also want to know why email is required when only using username plugin. @ping-maxwell any chance to get this added to the 1.3.0 milestone? #2172 is included in that milestone too. This and that issue both relate to the same problem.
Author
Owner

@SARAsBooks commented on GitHub (Jun 2, 2025):

@dsonet, @dinfer, @VIEWVIEWVIEW

I have many uncommitted changes on my fork SARAsBooks/better-auth, but here's a summary by Claude Code on my git diff. I had to change some COMPLETE to NEARLY COMPLETE or BUGGY because I'm still cleaning issues before I commit. I can commit now if other's want to assist.

Key Changes Summary

1. Core Infrastructure Implementation NEARLY COMPLETE

Database Schema & Core Types

  • packages/better-auth/src/db/schema.ts - Added comprehensive Identifier table schema with proper constraints
  • packages/better-auth/src/db/get-tables.ts - Added identifier table configuration with indexes
  • packages/better-auth/src/db/get-migration.ts - Enhanced migration system for constraint creation // TODO: property constraints does not exist on type
  • packages/better-auth/src/types/models.ts - Added Identifier type definitions
  • packages/better-auth/src/types/adapter.ts - Extended adapter interface with identifier methods // TODO: getUserByIdentifier has as return type of any | null, should be User | null
  • packages/better-auth/src/types/options.ts - Added identifier configuration options

Database Utilities

  • packages/better-auth/src/db/utils.ts - 15+ comprehensive utility functions for identifier operations including validation, normalization, and security functions // TODO: review thoroughly

2. Migration Infrastructure NEARLY COMPLETE

Migration Support Across All Adapters

  • Created migration scripts for all supported database adapters:
    • Prisma: SQL and schema migrations
    • Drizzle: SQL and TypeScript schema migrations
    • MongoDB: Collection migration script
    • Kysely: TypeScript migration script

Migration Management

  • packages/better-auth/src/db/migration-helpers.ts - Comprehensive migration utilities for email-to-identifier transformation // TODO: type errors need to be resolved
  • packages/better-auth/src/api/routes/migration.ts - API endpoints for managing migrations

3. Authentication API Implementation PARTIAL

New API Endpoints

  • packages/better-auth/src/api/routes/sign-up-identifier.ts - Identifier-based sign-up endpoint
  • packages/better-auth/src/api/routes/sign-in-identifier.ts - Identifier-based sign-in endpoint
  • packages/better-auth/src/api/routes/identifier-management.ts - CRUD operations for identifiers
  • packages/better-auth/src/api/routes/identifier-verification.ts - Identifier verification workflows

API Integration

  • packages/better-auth/src/api/index.ts - Updated to include new identifier routes
  • packages/better-auth/src/api/routes/index.ts - Route registration for identifier endpoints

4. Adapter Implementation NEARLY COMPLETE

Memory Adapter Enhancement

  • packages/better-auth/src/adapters/memory-adapter/memory-adapter.ts - Extended with full identifier support
  • packages/better-auth/src/adapters/test/identifier-table-tests.ts - Comprehensive adapter test suite
  • packages/better-auth/src/adapters/memory-adapter/adapter.memory.test.ts - Integration testing

5. Comprehensive Testing Infrastructure BUGGY

Test Utilities

  • packages/better-auth/src/test-utils/identifier-helpers.ts - Production-ready test helper functions // TODO: type errors need to be resolved
  • packages/better-auth/src/test-utils/test-data-factory.ts - Centralized test data generation with sequence-based uniqueness

Core Feature Tests

  • Sign-up and sign-in identifier tests with comprehensive coverage
  • Identifier management CRUD operation tests
  • Identifier verification security-focused testing
  • Database table abstraction and virtual fields testing

Integration & Migration Tests

  • packages/better-auth/src/integration/recovery-classification.test.ts - Recovery system testing
  • packages/better-auth/src/migration/email-to-identifier.test.ts - Data migration integrity validation
  • packages/better-auth/src/migration/backward-compatibility.test.ts - Legacy API compatibility testing

Security Testing

  • packages/better-auth/src/security/enumeration-prevention.test.ts - Enhanced enumeration attack prevention
  • packages/better-auth/src/security/timing-consistency.test.ts - Statistical timing analysis (50+ iterations)
  • packages/better-auth/src/security/input-sanitization.test.ts - Multi-vector attack prevention

Performance & Edge Case Testing

  • packages/better-auth/src/performance/benchmarks.test.ts - Concrete performance validation and memory leak detection
  • packages/better-auth/src/error/edge-cases.test.ts - Unicode, boundary conditions, and concurrent operation testing
<!-- gh-comment-id:2931187375 --> @SARAsBooks commented on GitHub (Jun 2, 2025): @dsonet, @dinfer, @VIEWVIEWVIEW I have many uncommitted changes on my fork [SARAsBooks/better-auth](https://github.com/SARAsBooks/better-auth/tree/feat/identifier-table), but here's a summary by Claude Code on my git diff. I had to change some ✅ **COMPLETE** to ✅ **NEARLY COMPLETE** or ✅ **BUGGY** because I'm still cleaning issues before I commit. I can commit now if other's want to assist. ## Key Changes Summary ### 1. Core Infrastructure Implementation ✅ **NEARLY COMPLETE** **Database Schema & Core Types** - `packages/better-auth/src/db/schema.ts` - Added comprehensive Identifier table schema with proper constraints - `packages/better-auth/src/db/get-tables.ts` - Added identifier table configuration with indexes - `packages/better-auth/src/db/get-migration.ts` - Enhanced migration system for constraint creation // TODO: property constraints does not exist on type - `packages/better-auth/src/types/models.ts` - Added Identifier type definitions - `packages/better-auth/src/types/adapter.ts` - Extended adapter interface with identifier methods // TODO: getUserByIdentifier has as return type of any | null, should be User | null - `packages/better-auth/src/types/options.ts` - Added identifier configuration options **Database Utilities** - `packages/better-auth/src/db/utils.ts` - 15+ comprehensive utility functions for identifier operations including validation, normalization, and security functions // TODO: review thoroughly ### 2. Migration Infrastructure ✅ **NEARLY COMPLETE** **Migration Support Across All Adapters** - Created migration scripts for all supported database adapters: - Prisma: SQL and schema migrations - Drizzle: SQL and TypeScript schema migrations - MongoDB: Collection migration script - Kysely: TypeScript migration script **Migration Management** - `packages/better-auth/src/db/migration-helpers.ts` - Comprehensive migration utilities for email-to-identifier transformation // TODO: type errors need to be resolved - `packages/better-auth/src/api/routes/migration.ts` - API endpoints for managing migrations ### 3. Authentication API Implementation ✅ **PARTIAL** **New API Endpoints** - `packages/better-auth/src/api/routes/sign-up-identifier.ts` - Identifier-based sign-up endpoint - `packages/better-auth/src/api/routes/sign-in-identifier.ts` - Identifier-based sign-in endpoint - `packages/better-auth/src/api/routes/identifier-management.ts` - CRUD operations for identifiers - `packages/better-auth/src/api/routes/identifier-verification.ts` - Identifier verification workflows **API Integration** - `packages/better-auth/src/api/index.ts` - Updated to include new identifier routes - `packages/better-auth/src/api/routes/index.ts` - Route registration for identifier endpoints ### 4. Adapter Implementation ✅ **NEARLY COMPLETE** **Memory Adapter Enhancement** - `packages/better-auth/src/adapters/memory-adapter/memory-adapter.ts` - Extended with full identifier support - `packages/better-auth/src/adapters/test/identifier-table-tests.ts` - Comprehensive adapter test suite - `packages/better-auth/src/adapters/memory-adapter/adapter.memory.test.ts` - Integration testing ### 5. Comprehensive Testing Infrastructure ✅ **BUGGY** **Test Utilities** - `packages/better-auth/src/test-utils/identifier-helpers.ts` - Production-ready test helper functions // TODO: type errors need to be resolved - `packages/better-auth/src/test-utils/test-data-factory.ts` - Centralized test data generation with sequence-based uniqueness **Core Feature Tests** - Sign-up and sign-in identifier tests with comprehensive coverage - Identifier management CRUD operation tests - Identifier verification security-focused testing - Database table abstraction and virtual fields testing **Integration & Migration Tests** - `packages/better-auth/src/integration/recovery-classification.test.ts` - Recovery system testing - `packages/better-auth/src/migration/email-to-identifier.test.ts` - Data migration integrity validation - `packages/better-auth/src/migration/backward-compatibility.test.ts` - Legacy API compatibility testing **Security Testing** - `packages/better-auth/src/security/enumeration-prevention.test.ts` - Enhanced enumeration attack prevention - `packages/better-auth/src/security/timing-consistency.test.ts` - Statistical timing analysis (50+ iterations) - `packages/better-auth/src/security/input-sanitization.test.ts` - Multi-vector attack prevention **Performance & Edge Case Testing** - `packages/better-auth/src/performance/benchmarks.test.ts` - Concrete performance validation and memory leak detection - `packages/better-auth/src/error/edge-cases.test.ts` - Unicode, boundary conditions, and concurrent operation testing
Author
Owner

@VIEWVIEWVIEW commented on GitHub (Jun 4, 2025):

@dsonet, @dinfer, @VIEWVIEWVIEW

I have many uncommitted changes on my fork SARAsBooks/better-auth, but here's a summary by Claude Code on my git diff. I had to change some COMPLETE to NEARLY COMPLETE or BUGGY because I'm still cleaning issues before I commit. I can commit now if other's want to assist.

I've read through your fork without the latest commit, but I think your approach is too complicated.

It would be the easiest, to simply disallow usernames to use @ and allow phone numbers only in the usual formats. Then, allow email to be nullable. When the user logins, simply perform a check whether it is an email, username or phone number login attempt, and look it up in the db. I don't think a dedicated abstraction is necessary, as email/username/phone number are the only three things I can imagine a login for (excluding all the SSO and webauthn jazz, but that's not important for this issue anyway).

<!-- gh-comment-id:2940015648 --> @VIEWVIEWVIEW commented on GitHub (Jun 4, 2025): > [@dsonet](https://github.com/dsonet), [@dinfer](https://github.com/dinfer), [@VIEWVIEWVIEW](https://github.com/VIEWVIEWVIEW) > > I have many uncommitted changes on my fork [SARAsBooks/better-auth](https://github.com/SARAsBooks/better-auth/tree/feat/identifier-table), but here's a summary by Claude Code on my git diff. I had to change some ✅ **COMPLETE** to ✅ **NEARLY COMPLETE** or ✅ **BUGGY** because I'm still cleaning issues before I commit. I can commit now if other's want to assist. I've read through your fork without the latest commit, but I think your approach is too complicated. It would be the easiest, to simply disallow usernames to use ``@`` and allow phone numbers only in the usual formats. Then, allow email to be nullable. When the user logins, simply perform a check whether it is an email, username or phone number login attempt, and look it up in the db. I don't think a dedicated abstraction is necessary, as email/username/phone number are the only three things I can imagine a login for (excluding all the SSO and webauthn jazz, but that's not important for this issue anyway).
Author
Owner

@dosubot[bot] commented on GitHub (Sep 3, 2025):

Hi, @dsonet. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You requested making email optional for signup and enabling login via username, email, or phone number using a unified identifier system.
  • Contributor SARAsBooks developed a nearly complete feature branch with an Identifier table, abstraction layer, migration scripts, API endpoints, and tests, though some parts remain buggy or uncommitted.
  • Other users suggested simpler alternatives like allowing nullable email and disallowing "@" in usernames to differentiate login types without complex abstraction.
  • Maintainers have acknowledged the request and indicated plans to make fields more configurable in the future.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of better-auth by commenting here to keep the discussion open.
  • Otherwise, I will automatically close this issue in 7 days.

Thank you for your understanding and contribution!

<!-- gh-comment-id:3249863201 --> @dosubot[bot] commented on GitHub (Sep 3, 2025): Hi, @dsonet. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You requested making email optional for signup and enabling login via username, email, or phone number using a unified identifier system. - Contributor SARAsBooks developed a nearly complete feature branch with an Identifier table, abstraction layer, migration scripts, API endpoints, and tests, though some parts remain buggy or uncommitted. - Other users suggested simpler alternatives like allowing nullable email and disallowing "@" in usernames to differentiate login types without complex abstraction. - Maintainers have acknowledged the request and indicated plans to make fields more configurable in the future. **Next Steps:** - Please let me know if this issue is still relevant to the latest version of better-auth by commenting here to keep the discussion open. - Otherwise, I will automatically close this issue in 7 days. Thank you for your understanding and contribution!
Author
Owner

@VIEWVIEWVIEW commented on GitHub (Sep 3, 2025):

Still relevant, we are waiting for the better-auth core team to make the fields more dynamic. The guy who slapped AI on the problem didn't finish their work either.

<!-- gh-comment-id:3250223341 --> @VIEWVIEWVIEW commented on GitHub (Sep 3, 2025): Still relevant, we are waiting for the better-auth core team to make the fields more dynamic. The guy who slapped AI on the problem didn't finish their work either.
Author
Owner

@dosubot[bot] commented on GitHub (Dec 3, 2025):

Hi, @dsonet. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary

  • You requested making email optional for signup and enabling login via username, email, or phone number through a unified identifier system.
  • Contributor SARAsBooks developed a nearly complete feature branch with an Identifier table, abstraction layer, migration scripts, API endpoints, and tests, though some parts remain buggy or uncommitted.
  • Other users suggested simpler approaches like allowing nullable email and disallowing "@" in usernames to differentiate login types without complex abstraction.
  • Maintainers have acknowledged the request and plan to make fields more configurable in the future.
  • The issue remains open and awaiting further core team action.

Next Steps

  • Please let me know if this issue is still relevant to the latest version of better-auth by commenting here to keep the discussion open.
  • Otherwise, I will automatically close this issue in 7 days.

Thank you for your understanding and contribution!

<!-- gh-comment-id:3607641870 --> @dosubot[bot] commented on GitHub (Dec 3, 2025): Hi, @dsonet. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary** - You requested making email optional for signup and enabling login via username, email, or phone number through a unified identifier system. - Contributor SARAsBooks developed a nearly complete feature branch with an Identifier table, abstraction layer, migration scripts, API endpoints, and tests, though some parts remain buggy or uncommitted. - Other users suggested simpler approaches like allowing nullable email and disallowing "@" in usernames to differentiate login types without complex abstraction. - Maintainers have acknowledged the request and plan to make fields more configurable in the future. - The issue remains open and awaiting further core team action. **Next Steps** - Please let me know if this issue is still relevant to the latest version of better-auth by commenting here to keep the discussion open. - Otherwise, I will automatically close this issue in 7 days. Thank you for your understanding and contribution!
Author
Owner

@SARAsBooks commented on GitHub (Dec 4, 2025):

@dsonet, @dinfer, @VIEWVIEWVIEW
I have many uncommitted changes on my fork SARAsBooks/better-auth, but here's a summary by Claude Code on my git diff. I had to change some COMPLETE to NEARLY COMPLETE or BUGGY because I'm still cleaning issues before I commit. I can commit now if other's want to assist.

I've read through your fork without the latest commit, but I think your approach is too complicated.

It would be the easiest, to simply disallow usernames to use @ and allow phone numbers only in the usual formats. Then, allow email to be nullable. When the user logins, simply perform a check whether it is an email, username or phone number login attempt, and look it up in the db. I don't think a dedicated abstraction is necessary, as email/username/phone number are the only three things I can imagine a login for (excluding all the SSO and webauthn jazz, but that's not important for this issue anyway).

I'm ok with this being closed. I was trying to create a system that allowed for both soft-identification and hard-authentication. I ended up rolling my own on the soft-identification and using WorkOS for the authentication.

<!-- gh-comment-id:3609662137 --> @SARAsBooks commented on GitHub (Dec 4, 2025): > > [@dsonet](https://github.com/dsonet), [@dinfer](https://github.com/dinfer), [@VIEWVIEWVIEW](https://github.com/VIEWVIEWVIEW) > > I have many uncommitted changes on my fork [SARAsBooks/better-auth](https://github.com/SARAsBooks/better-auth/tree/feat/identifier-table), but here's a summary by Claude Code on my git diff. I had to change some ✅ **COMPLETE** to ✅ **NEARLY COMPLETE** or ✅ **BUGGY** because I'm still cleaning issues before I commit. I can commit now if other's want to assist. > > I've read through your fork without the latest commit, but I think your approach is too complicated. > > It would be the easiest, to simply disallow usernames to use `@` and allow phone numbers only in the usual formats. Then, allow email to be nullable. When the user logins, simply perform a check whether it is an email, username or phone number login attempt, and look it up in the db. I don't think a dedicated abstraction is necessary, as email/username/phone number are the only three things I can imagine a login for (excluding all the SSO and webauthn jazz, but that's not important for this issue anyway). I'm ok with this being closed. I was trying to create a system that allowed for both soft-identification and hard-authentication. I ended up rolling my own on the soft-identification and using WorkOS for the authentication.
Author
Owner

@eiriksm commented on GitHub (Dec 4, 2025):

For anyone else reading here. I simply went ahead and did this:

-      const result = await signIn.email({
-        email,
-        password,
-      });
+      const containsAt = emailOrUsername.includes("@");
+
+      let result;
+      if (containsAt) {
+        // Contains @, try email first, then fallback to username
+        result = await signIn.email({
+          email: emailOrUsername,
+          password,
+        });
+
+        // If email failed, try username
+        if (result.error) {
+          result = await signIn.username({
+            username: emailOrUsername,
+            password,
+          });
+        }
+      } else {
+        // No @, treat as username only
+        result = await signIn.username({
+          username: emailOrUsername,
+          password,
+        });
+      }

It's of course not the best performance wise, but a nice UX improvement. And randomly overlaps with the acceptance criteria 🤓

<!-- gh-comment-id:3610943808 --> @eiriksm commented on GitHub (Dec 4, 2025): For anyone else reading here. I simply went ahead and did this: ```diff - const result = await signIn.email({ - email, - password, - }); + const containsAt = emailOrUsername.includes("@"); + + let result; + if (containsAt) { + // Contains @, try email first, then fallback to username + result = await signIn.email({ + email: emailOrUsername, + password, + }); + + // If email failed, try username + if (result.error) { + result = await signIn.username({ + username: emailOrUsername, + password, + }); + } + } else { + // No @, treat as username only + result = await signIn.username({ + username: emailOrUsername, + password, + }); + } ``` It's of course not the best performance wise, but a nice UX improvement. And randomly overlaps with the acceptance criteria 🤓
Author
Owner

@dosubot[bot] commented on GitHub (Mar 5, 2026):

Hi, @dsonet. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You requested making email optional for signup and enabling login via username, email, or phone number using a unified identifier system.
  • Contributor SARAsBooks developed a nearly complete feature branch with an Identifier table, abstraction layer, migration scripts, API endpoints, and tests, though some parts remain buggy or uncommitted.
  • Other users suggested simpler alternatives like allowing nullable email and disallowing "@" in usernames to differentiate login types without complex abstraction.
  • Maintainers have acknowledged plans to make fields more configurable in the future, but no core team action has been taken yet.
  • The issue remains open and unresolved, awaiting further input from the core team.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of better-auth by commenting here to keep the discussion open.
  • Otherwise, I will automatically close this issue in 7 days.

Thank you for your understanding and contribution!

<!-- gh-comment-id:4006189496 --> @dosubot[bot] commented on GitHub (Mar 5, 2026): Hi, @dsonet. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You requested making email optional for signup and enabling login via username, email, or phone number using a unified identifier system. - Contributor SARAsBooks developed a nearly complete feature branch with an Identifier table, abstraction layer, migration scripts, API endpoints, and tests, though some parts remain buggy or uncommitted. - Other users suggested simpler alternatives like allowing nullable email and disallowing "@" in usernames to differentiate login types without complex abstraction. - Maintainers have acknowledged plans to make fields more configurable in the future, but no core team action has been taken yet. - The issue remains open and unresolved, awaiting further input from the core team. **Next Steps:** - Please let me know if this issue is still relevant to the latest version of better-auth by commenting here to keep the discussion open. - Otherwise, I will automatically close this issue in 7 days. Thank you for your understanding and contribution!
Author
Owner

@nathan-hello commented on GitHub (Mar 7, 2026):

This issue is still relevant. We could use placeholder emails, but then that might mean that if a certain endpoint gets hit, like /send-verification-email, then an email will get sent to the placeholder, incurring potential API costs and undelivered/unread emails that reduce email reputation. Overall it's just not a great strategy. I would like to use username-only, but given the constraints I'm just going to require email until this issue is resolved.

I would like to help contribute to this issue, but it seems like a large refactor for this library because email existing seems to be a core assumption.

Wild, wild idea, but I'm just throwing it out there. Could email() be a plugin like username() is? I assume this would count as a breaking change, but it would be the most composible option I think.

<!-- gh-comment-id:4017389611 --> @nathan-hello commented on GitHub (Mar 7, 2026): This issue is still relevant. We could use placeholder emails, but then that might mean that if a certain endpoint gets hit, like `/send-verification-email`, then an email will get sent to the placeholder, incurring potential API costs and undelivered/unread emails that reduce email reputation. Overall it's just not a great strategy. I would like to use username-only, but given the constraints I'm just going to require email until this issue is resolved. I would like to help contribute to this issue, but it seems like a large refactor for this library because email existing seems to be a core assumption. Wild, wild idea, but I'm just throwing it out there. Could `email()` be a plugin like `username()` is? I assume this would count as a breaking change, but it would be the most composible option I think.
Author
Owner

@Laurin-Notemann commented on GitHub (Mar 13, 2026):

are more dynamic fields still in work or what is the status on that? @himself65 @ping-maxwell @bytaesu @Bekacru

<!-- gh-comment-id:4055003235 --> @Laurin-Notemann commented on GitHub (Mar 13, 2026): are more dynamic fields still in work or what is the status on that? @himself65 @ping-maxwell @bytaesu @Bekacru
Author
Owner

@bytaesu commented on GitHub (Mar 21, 2026):

This looks similar to #424 (name field)

We're currently avoiding changes to the core schema in v1.X.X to prevent breaking changes.

Also, much of the existing logic is built around email, which acts as a unique identifier. Making it optional would require structural changes. While we're aware of plugins that don't rely on email, properly supporting them would require redesigning the core 🧐

<!-- gh-comment-id:4102808024 --> @bytaesu commented on GitHub (Mar 21, 2026): This looks similar to #424 (`name` field) We're currently avoiding changes to the core schema in `v1.X.X` to prevent breaking changes. Also, much of the existing logic is built around `email`, which acts as a unique identifier. Making it optional would require structural changes. While we're aware of plugins that don't rely on email, properly supporting them would require redesigning the core 🧐
Author
Owner

@Laurin-Notemann commented on GitHub (Mar 22, 2026):

@bytaesu Hm, yea I guess that makes sense, is there any kind of roadmap / plan for that or is that just the logic behind the decision?

<!-- gh-comment-id:4107127474 --> @Laurin-Notemann commented on GitHub (Mar 22, 2026): @bytaesu Hm, yea I guess that makes sense, is there any kind of roadmap / plan for that or is that just the logic behind the decision?
Author
Owner

@github-actions[bot] commented on GitHub (Apr 2, 2026):

This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.

<!-- gh-comment-id:4173719182 --> @github-actions[bot] commented on GitHub (Apr 2, 2026): This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#17812