betterauth / symfony-bundle
Symfony bundle for BetterAuth - Modern authentication library
Installs: 9
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 0
Forks: 0
Type:symfony-bundle
pkg:composer/betterauth/symfony-bundle
Requires
- php: ^8.2
- api-platform/core: ^4.0
- bacon/bacon-qr-code: ^2.0|^3.0
- doctrine/doctrine-bundle: ^2.0|^3.0
- doctrine/doctrine-migrations-bundle: ^3.0
- doctrine/orm: ^3.0
- monolog/monolog: ^3.0
- nelmio/cors-bundle: ^2.0
- paragonie/paseto: ^3.1
- psr/cache: ^3.0
- ramsey/uuid: ^4.7
- symfony/asset: ^6.4|^7.0|^8.0
- symfony/config: ^6.4|^7.0|^8.0
- symfony/console: ^6.4|^7.0|^8.0
- symfony/dependency-injection: ^6.4|^7.0|^8.0
- symfony/filesystem: ^6.4|^7.0|^8.0
- symfony/framework-bundle: ^6.4|^7.0|^8.0
- symfony/http-kernel: ^6.4|^7.0|^8.0
- symfony/mailer: ^6.4|^7.0|^8.0
- symfony/mime: ^6.4|^7.0|^8.0
- symfony/property-access: ^6.4|^7.0|^8.0
- symfony/property-info: ^6.4|^7.0|^8.0
- symfony/security-bundle: ^6.4|^7.0|^8.0
- symfony/serializer: ^6.4|^7.0|^8.0
- symfony/twig-bundle: ^6.4|^7.0|^8.0
- symfony/uid: ^6.4|^7.0|^8.0
- symfony/validator: ^6.4|^7.0|^8.0
- symfony/yaml: ^6.4|^7.0|^8.0
Requires (Dev)
- dg/bypass-finals: ^1.9
- doctrine/doctrine-fixtures-bundle: ^4.3
- liip/test-fixtures-bundle: ^3.8
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
- symfony/browser-kit: ^6.4|^7.0|^8.0
- symfony/debug-bundle: ^6.4|^7.0|^8.0
- symfony/http-client: ^6.4|^7.0|^8.0
- symfony/maker-bundle: ^1.0
- symfony/monolog-bundle: ^3.0
- symfony/phpunit-bridge: ^6.4|^7.0|^8.0
- symfony/stopwatch: ^6.4|^7.0|^8.0
- symfony/web-profiler-bundle: ^6.4|^7.0|^8.0
Suggests
- ext-apcu: APCu extension for in-memory caching (development)
- ext-redis: Redis extension for better performance
- geoip2/geoip2: For IP geolocation in device tracking
- mobiledetect/mobiledetectlib: For device detection in device tracking
- symfony/redis: For Redis-based rate limiting (recommended for production)
README
Modern, secure authentication for Symfony 6.4 / 7.x / 8.0 applications with automatic setup and embedded core.
Part of the BetterAuth ecosystem — a unified authentication library for PHP. Also available: BetterAuth for Laravel
BetterAuth vs LexikJWT
LexikJWTAuthenticationBundle is the most popular JWT bundle for Symfony. Here's an honest comparison:
Quick Comparison
| Feature | BetterAuth | LexikJWT |
|---|---|---|
| Token Type | Paseto V4 (encrypted) | JWT (signed only) |
| Setup | 1 command (better-auth:install) |
Manual configuration |
| Refresh Tokens | Built-in with rotation | Requires extra bundle |
| Registration | Built-in | Manual implementation |
| OAuth 2.0 | 5 providers ready | Manual integration |
| 2FA / TOTP | Built-in | External packages |
| Magic Link | Built-in | Manual implementation |
| Email Verification | Built-in | Manual implementation |
| Password Reset | Built-in | Manual implementation |
| Session Management | Built-in | Not supported |
| Multi-tenant | Organizations & teams | Not supported |
| Rate Limiting | Built-in | Manual implementation |
| Entity Generation | Automatic | Manual |
| Controller Generation | Automatic | Manual |
| Symfony Versions | 6.4, 7.x, 8.0 | 5.4, 6.x, 7.x |
| PHP Versions | 8.2+ | 7.4+ |
Token Security: Paseto vs JWT
| Aspect | Paseto V4 (BetterAuth) | JWT (LexikJWT) |
|---|---|---|
| Encryption | XChaCha20-Poly1305 | None (plaintext payload) |
| Authentication | Ed25519 signatures | HMAC or RSA |
| Payload Visibility | Encrypted, not readable | Base64, anyone can read |
| Algorithm Confusion | Not possible | Known vulnerability class |
| Key Compromise Impact | Encrypted payload protected | All tokens readable |
JWT payloads are base64-encoded, not encrypted. Anyone with the token can read user IDs, emails, roles, etc.:
# JWT payload is readable without the secret echo "eyJzdWIiOiJ1c2VyLTEyMyIsInJvbGUiOiJhZG1pbiJ9" | base64 -d # Output: {"sub":"user-123","role":"admin"}
Paseto V4 encrypts the payload. Even with the token, no one can read the contents without the secret key.
Feature Comparison Details
What you get with BetterAuth
✅ Registration endpoint
✅ Login endpoint
✅ Token refresh with rotation
✅ Logout (single + all sessions)
✅ Password reset flow
✅ Email verification flow
✅ OAuth 2.0 (Google, GitHub, etc.)
✅ TOTP 2FA with backup codes
✅ Magic link authentication
✅ Session management
✅ Device tracking
✅ Rate limiting
✅ Multi-tenant organizations
What you need to build with LexikJWT
❌ Registration endpoint → Build yourself
❌ Token refresh → Install gesdinet/jwt-refresh-token-bundle
❌ Logout → Build yourself (JWT cannot be revoked)
❌ Password reset → Build yourself
❌ Email verification → Build yourself
❌ OAuth 2.0 → Install knpuniversity/oauth2-client-bundle + configure
❌ 2FA → Install scheb/2fa-bundle + configure
❌ Magic link → Build yourself
❌ Session management → Build yourself
❌ Device tracking → Build yourself
❌ Rate limiting → Install symfony/rate-limiter + configure
❌ Multi-tenant → Build yourself
Setup Comparison
BetterAuth (2 minutes)
composer require betterauth/symfony-bundle
php bin/console better-auth:install
# Done! All entities, controllers, config generated.
LexikJWT (30+ minutes)
composer require lexik/jwt-authentication-bundle # Generate SSH keys mkdir -p config/jwt openssl genpkey -out config/jwt/private.pem -algorithm RSA -pkeyopt rsa_keygen_bits:4096 openssl pkey -in config/jwt/private.pem -out config/jwt/public.pem -pubout # Configure security.yaml # Configure lexik_jwt_authentication.yaml # Create User entity # Create registration controller # Create login controller # Install refresh token bundle # Configure refresh tokens # Build password reset # Build email verification # ...and more
When to Choose LexikJWT
LexikJWT is a great choice if you:
- Need maximum simplicity for basic API auth
- Want JWT interoperability with other services
- Already have user management built
- Only need token generation, not a full auth system
- Prefer minimal dependencies
When to Choose BetterAuth
BetterAuth is a better choice if you:
- Want encrypted tokens (sensitive data in payload)
- Need OAuth 2.0 social login
- Need 2FA / TOTP authentication
- Want magic link passwordless login
- Need session management with device tracking
- Want multi-tenant organizations
- Prefer automated setup over manual configuration
- Need password reset and email verification flows
- Want everything in one package
Migration from LexikJWT
If you're already using LexikJWT and want to migrate:
- Install BetterAuth alongside LexikJWT
- Run
php bin/console better-auth:install(use existing User entity) - Update your frontend to use new endpoints
- Remove LexikJWT once migration is complete
Both can coexist during migration.
Features
Core Authentication
- Email/password registration & login
- Paseto V4 encrypted tokens (not JWT)
- Refresh token rotation (one-time use)
- Session management with device tracking
Passwordless
- Magic Link authentication
- Passkeys / WebAuthn (draft)
Two-Factor Authentication
- TOTP (Google Authenticator, Authy, etc.)
- Backup codes with regeneration
- 2FA status endpoints
OAuth 2.0 Providers
| Provider | Status |
|---|---|
| Stable | |
| GitHub | Draft |
| Draft | |
| Microsoft | Draft |
| Discord | Draft |
Enterprise Features
- Email verification flow
- Password reset flow
- Guest sessions (anonymous users)
- Multi-tenant organizations & teams
- Security monitoring & threat detection
- Rate limiting built-in
Developer Experience
- Automated installer with wizard
- UUID v7 or auto-increment IDs
- API Platform OpenAPI integration
- Symfony Security integration
- Comprehensive console commands
Requirements
| Requirement | Version |
|---|---|
| PHP | ^8.2 |
| Symfony | ^6.4 | ^7.0 | ^8.0 |
| Doctrine ORM | ^3.0 |
| API Platform | ^4.0 (optional) |
Tested on: PHP 8.2/8.3/8.4 × Symfony 6.4/7.0/7.1/7.2/7.3/8.0
Installation
Step 1: Install the bundle
composer require betterauth/symfony-bundle
Step 2: Run the installer
php bin/console better-auth:install
The interactive wizard will:
- Generate User, Session, and RefreshToken entities
- Create AuthController with all endpoints
- Set up configuration files
- Generate Doctrine migrations
- Create a secure secret key
Non-Interactive Installation
php bin/console better-auth:install \ --id-strategy=uuid \ --mode=api \ --no-interaction
| Option | Values | Description |
|---|---|---|
--id-strategy |
uuid, int |
UUID v7 (default) or auto-increment |
--mode |
api, session, hybrid |
Authentication mode |
--exclude-fields |
avatar, name |
Exclude optional User fields |
--minimal |
- | Skip name/avatar fields |
--skip-migrations |
- | Don't generate migrations |
--skip-controller |
- | Don't generate controller |
Quick Start
Register a user
curl -X POST http://localhost:8000/auth/register \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"SecurePassword123"}'
Login
curl -X POST http://localhost:8000/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"SecurePassword123"}' # Response: { "access_token": "v4.local.eyJ...", "refresh_token": "v4.local.eyJ...", "token_type": "Bearer", "expires_in": 3600 }
Authenticated request
curl http://localhost:8000/auth/me \
-H "Authorization: Bearer v4.local.eyJ..."
Refresh token
curl -X POST http://localhost:8000/auth/refresh \ -H "Content-Type: application/json" \ -d '{"refreshToken":"v4.local.eyJ..."}'
API Endpoints
Authentication
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Create new user |
POST |
/auth/login |
Authenticate user |
POST |
/auth/login/2fa |
Complete 2FA login |
GET |
/auth/me |
Get current user |
POST |
/auth/refresh |
Refresh access token |
POST |
/auth/logout |
Logout current session |
POST |
/auth/revoke-all |
Revoke all tokens |
Sessions
| Method | Endpoint | Description |
|---|---|---|
GET |
/auth/sessions |
List active sessions |
DELETE |
/auth/sessions/{id} |
Revoke specific session |
Two-Factor Auth
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/2fa/setup |
Initialize 2FA setup |
POST |
/auth/2fa/validate |
Validate setup code |
POST |
/auth/2fa/verify |
Verify 2FA code |
POST |
/auth/2fa/disable |
Disable 2FA |
GET |
/auth/2fa/status |
Get 2FA status |
POST |
/auth/2fa/backup-codes/regenerate |
Regenerate backup codes |
Magic Link
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/magic-link/send |
Send magic link email |
POST |
/auth/magic-link/verify |
Verify magic link token |
GET |
/auth/magic-link/verify/{token} |
Verify via GET |
Email Verification
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/email/send-verification |
Send verification email |
POST |
/auth/email/verify |
Verify email with token |
GET |
/auth/email/verification-status |
Check verification status |
Password Reset
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/password/forgot |
Request password reset |
POST |
/auth/password/reset |
Reset with token |
POST |
/auth/password/verify-token |
Validate reset token |
OAuth
| Method | Endpoint | Description |
|---|---|---|
GET |
/auth/oauth/providers |
List enabled providers |
GET |
/auth/oauth/{provider} |
Redirect to OAuth provider |
GET |
/auth/oauth/{provider}/url |
Get OAuth URL (for SPAs) |
GET |
/auth/oauth/{provider}/callback |
OAuth callback handler |
Guest Sessions
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/guest/create |
Create guest session |
GET |
/auth/guest/{token} |
Get guest session |
POST |
/auth/guest/convert |
Convert to registered user |
DELETE |
/auth/guest/{token} |
Delete guest session |
Configuration
config/packages/better_auth.yaml
better_auth: # Authentication mode: api, session, or hybrid mode: 'api' # Secret key for token encryption (auto-generated) secret: '%env(BETTER_AUTH_SECRET)%' # Token configuration token: lifetime: 3600 # Access token: 1 hour refresh_lifetime: 2592000 # Refresh token: 30 days # OAuth providers oauth: providers: google: enabled: true client_id: '%env(GOOGLE_CLIENT_ID)%' client_secret: '%env(GOOGLE_CLIENT_SECRET)%' redirect_uri: '%env(APP_URL)%/auth/oauth/google/callback' # Session settings (for session/hybrid mode) session: lifetime: 604800 cookie_name: 'better_auth_session' # Email verification email_verification: enabled: true lifetime: 3600 # Password reset password_reset: enabled: true lifetime: 3600 # Multi-tenant (organizations) multi_tenant: enabled: false default_role: 'member'
Environment Variables
# Required BETTER_AUTH_SECRET=your-64-character-secret-key-here APP_URL=http://localhost:8000 # Email MAILER_DSN=smtp://127.0.0.1:1025 # OAuth (optional) GOOGLE_CLIENT_ID=your-google-client-id GOOGLE_CLIENT_SECRET=your-google-client-secret
Authentication Modes
API Mode (Stateless)
- Paseto V4 tokens via
Authorization: Bearerheader - Perfect for SPAs, mobile apps, APIs
- No server-side session storage
Session Mode (Stateful)
- Traditional Symfony sessions with cookies
- Best for server-rendered applications
- Uses database for session storage
Hybrid Mode
- Both tokens and sessions available
- Useful for apps with mixed requirements
Console Commands
List all commands
bin/console list better-auth
Setup features after installation
# Enable specific features php bin/console better-auth:setup-features \ --enable=magic_link \ --enable=two_factor \ --with-controllers \ --migrate # Use a preset php bin/console better-auth:setup-features \ --preset=full \ --with-controllers \ --migrate # Preview changes php bin/console better-auth:setup-features \ --enable=magic_link \ --dry-run
| Preset | Features |
|---|---|
minimal |
Basic auth only |
standard |
Auth + email verification + password reset |
full |
All features enabled |
| Feature | Description |
|---|---|
magic_link |
Passwordless login via email |
two_factor |
TOTP 2FA (Google Authenticator) |
oauth |
Social login providers |
email_verification |
Email verification flow |
password_reset |
Forgot password flow |
session_management |
View/revoke sessions |
device_tracking |
Track user devices |
security_monitoring |
Threat detection |
guest_sessions |
Anonymous sessions |
multi_tenant |
Organizations & teams |
Other commands
# Generate a new secret key php bin/console better-auth:generate-secret # Add a specific controller php bin/console better-auth:add-controller magic-link # Manage User entity fields php bin/console better-auth:user-fields add name,avatar php bin/console better-auth:user-fields remove avatar # Switch authentication mode php bin/console better-auth:switch-mode api # Reconfigure bundle php bin/console better-auth:configure # Cleanup old sessions/tokens php bin/console better-auth:cleanup:sessions php bin/console better-auth:cleanup:tokens # Publish email templates php bin/console better-auth:publish-templates
Entity Customization
Extend the base User entity to add custom fields:
<?php namespace App\Entity; use BetterAuth\Symfony\Model\User as BaseUser; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class User extends BaseUser { #[ORM\Column(type: 'string', length: 20, nullable: true)] private ?string $phoneNumber = null; #[ORM\Column(type: 'json', nullable: true)] private ?array $preferences = null; public function getPhoneNumber(): ?string { return $this->phoneNumber; } public function setPhoneNumber(?string $phoneNumber): static { $this->phoneNumber = $phoneNumber; return $this; } public function getPreferences(): ?array { return $this->preferences; } public function setPreferences(?array $preferences): static { $this->preferences = $preferences; return $this; } }
Security
Token Security
- Paseto V4 — encrypted and authenticated tokens
- No algorithm confusion — unlike JWT
- XChaCha20-Poly1305 encryption
- Ed25519 signatures
Password Security
- Argon2id — memory-hard password hashing
- Configurable memory, time, and threads
Token Lifecycle
- Refresh token rotation — one-time use tokens
- Automatic cleanup — expired tokens removed
- Revocation support — revoke all tokens on demand
Additional Security
- UUID v7 — non-guessable, time-ordered IDs
- Rate limiting — built-in protection against brute force
- CSRF protection — for session-based auth
Testing
# Run tests composer test # Static analysis composer phpstan
CI Matrix
| PHP | Symfony Versions |
|---|---|
| 8.2 | 6.4, 7.0, 7.1, 7.2 |
| 8.3 | 6.4, 7.0, 7.1, 7.2, 7.3 |
| 8.4 | 6.4, 7.0, 7.1, 7.2, 7.3, 8.0 |
Documentation
Full documentation available at betterauth.dev
- Installation Guide
- Configuration Reference
- API Mode
- Session Mode
- OAuth Providers
- Two-Factor Authentication
- Magic Link
- Multi-Tenant
- Security Best Practices
Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Ensure tests pass (
composer test) - Run static analysis (
composer phpstan) - Commit with conventional commits (
feat: add amazing feature) - Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
git clone https://github.com/MakFly/betterauth-symfony.git cd betterauth-symfony composer install composer test
Security Issues
If you discover a security vulnerability, please do not open a public issue. Instead, create a private security advisory on GitHub.
License
MIT License. See LICENSE for details.
Changelog
See CHANGELOG.md for version history.
Packagist · Documentation · GitHub · Issues