betterauth/multimodal-php
Composer 安装命令:
composer require betterauth/multimodal-php
包简介
Framework-agnostic authentication library for PHP — works with Symfony, Laravel, or standalone
关键字:
README 文档
README
Framework-agnostic authentication library for PHP. Session-based, token-based (Paseto V4), or both — works with Symfony, Laravel, or standalone via PDO.
Features
- Three authentication modes — Monolith (session/cookie), API (stateless Paseto V4 tokens), Hybrid (both)
- OAuth social login — Google, GitHub, Facebook, Microsoft, Discord, Twitter, Apple
- OpenID Connect server — act as an SSO provider with authorization code + PKCE
- Magic links — passwordless email authentication
- TOTP 2FA — RFC 6238 with backup codes, SHA-256 default, SHA-1 migration path
- Session security — adaptive risk scoring (IP, device, country, impossible travel)
- Multi-tenancy — organizations, teams, members, invitations
- Plugin system — lifecycle hooks (
user.created,token.created,session.created, …) - PDO storage — 20 ready-to-use repositories, replaceable via interfaces
- Password security — Argon2id hashing, zxcvbn-inspired strength validation, auto-rehash on login
Requirements
- PHP 8.2+
- Extensions:
json,openssl,pdo
Installation
composer require betterauth/multimodal-php
Quick start
Session-based (monolith)
use BetterAuth\Core\AuthManager; use BetterAuth\Core\SessionAuthManager; use BetterAuth\Core\SessionService; use BetterAuth\Core\PasswordHasher; use BetterAuth\Core\Config\AuthConfig; use BetterAuth\Storage\Pdo\PdoUserRepository; use BetterAuth\Storage\Pdo\PdoSessionRepository; $pdo = new PDO('sqlite:auth.db'); $config = AuthConfig::forMonolith('your-secret-key-min-32-chars'); $users = new PdoUserRepository($pdo); $sessions = new PdoSessionRepository($pdo); $hasher = new PasswordHasher(); $service = new SessionService($sessions, $config); $sessionAuth = new SessionAuthManager($users, $service, $hasher); $auth = new AuthManager($config, sessionAuth: $sessionAuth); // Sign up $result = $auth->signUp('user@example.com', 'SecureP@ss2024!', 'Alice'); // Sign in $result = $auth->signIn('user@example.com', 'SecureP@ss2024!'); // $result = ['user' => UserDto, 'session' => Session] // Validate session $user = $auth->validateSession($result['session']->getToken());
Token-based (API)
use BetterAuth\Core\AuthManager; use BetterAuth\Core\TokenAuthManager; use BetterAuth\Core\TokenService; use BetterAuth\Core\PasswordHasher; use BetterAuth\Core\Config\AuthConfig; use BetterAuth\Storage\Pdo\PdoUserRepository; use BetterAuth\Storage\Pdo\PdoRefreshTokenRepository; $pdo = new PDO('sqlite:auth.db'); $config = AuthConfig::forApi('your-secret-key-min-32-chars'); $users = new PdoUserRepository($pdo); $refresh = new PdoRefreshTokenRepository($pdo); $hasher = new PasswordHasher(); $signer = new TokenService($config); $tokenAuth = new TokenAuthManager($users, $refresh, $signer, $hasher, $config); $auth = new AuthManager($config, tokenAuth: $tokenAuth); // Sign in $result = $auth->signIn('user@example.com', 'SecureP@ss2024!'); // $result = ['user' => UserDto, 'access_token' => '...', 'refresh_token' => '...', 'expires_in' => 3600] // Refresh tokens (atomic rotation) $newTokens = $auth->refresh($result['refresh_token']);
Authentication modes
| Mode | Method | Use case |
|---|---|---|
MONOLITH |
Session + cookie | Server-rendered apps (Symfony, Laravel) |
API |
Paseto V4 + refresh token | REST APIs, SPAs, mobile apps |
HYBRID |
Both active | Web frontend + mobile clients sharing the same backend |
$config = AuthConfig::forMonolith($secret); // Session mode $config = AuthConfig::forApi($secret); // Token mode $config = AuthConfig::forHybrid($secret); // Both
OAuth
use BetterAuth\Providers\OAuthProvider\OAuthManager; use BetterAuth\Providers\OAuthProvider\GoogleProvider; use BetterAuth\Providers\OAuthProvider\GitHubProvider; $oauth = new OAuthManager($auth, $users, $accountLinks); $oauth->addProvider(new GoogleProvider($clientId, $clientSecret, $redirectUri)); $oauth->addProvider(new GitHubProvider($clientId, $clientSecret, $redirectUri)); // Step 1 — redirect $authUrl = $oauth->getAuthorizationUrl('google', ['scope' => 'email profile']); // redirect user to $authUrl['url'], store $authUrl['state'] in session // Step 2 — callback $result = $oauth->handleCallback('google', $code, $redirectUri, $ip, $userAgent, $state, $expectedState);
Supported providers: Google, GitHub, Facebook, Microsoft, Discord, Twitter, Apple.
TOTP two-factor authentication
use BetterAuth\Providers\TotpProvider\TotpProvider; $totp = new TotpProvider($totpStorage, $users); // Enable 2FA $setup = $totp->generateSecret($userId, 'MyApp'); // $setup = ['secret' => '...', 'uri' => 'otpauth://totp/...', 'backup_codes' => [...]] // Verify setup $totp->enableTotp($userId, $setup['secret'], $codeFromApp); // Validate on login $totp->verifyTotp($userId, $codeFromApp);
- SHA-256 by default, SHA-1 fallback for legacy migration
- 10 one-time backup codes (bcrypt-hashed)
- 24h re-verification window
Magic links
use BetterAuth\Providers\MagicLinkProvider\MagicLinkProvider; $magicLink = new MagicLinkProvider($storage, $auth, $users, $config); // Send link $magicLink->sendMagicLink('user@example.com', $emailSender, 'https://app.com/auth/verify'); // Verify (from callback) $result = $magicLink->verifyMagicLink($token, $ip, $userAgent);
- 10-minute token expiry
- Rate-limited (3 requests / 5 minutes)
- Auto-creates users (optional)
- No user enumeration (always returns success)
OpenID Connect server
Act as an SSO provider for your own clients:
use BetterAuth\Providers\OidcProvider\OidcProvider; $oidc = new OidcProvider($clients, $codes, $tokenManager, $users, $config); // Authorization endpoint $result = $oidc->authorize($clientId, $redirectUri, 'openid profile email', $state, $userId, $codeChallenge, 'S256'); // Token endpoint $tokens = $oidc->token($grantType, $code, $redirectUri, $clientId, $clientSecret, $codeVerifier); // Discovery $config = $oidc->getDiscoveryConfig('https://auth.example.com');
Supports PKCE (S256 + plain), scopes (openid, profile, email, offline_access), and ID token claims.
Session security
Adaptive risk scoring on every request:
| Signal | Weight |
|---|---|
| Device type change (Desktop → Mobile) | 30 |
| OS family change | 25 |
| Country change | 40 |
| Impossible travel (< 1h + different country) | 50 |
| IP subnet change | 15 |
| Browser family change | 15 |
Thresholds: 0–24 → allow, 25–49 → log, 50–74 → require reauth, 75+ → terminate session.
Multi-tenancy
// Organizations, teams, members, invitations $org = $orgRepo->create('Acme Corp', 'acme-corp', $creatorId); $teamRepo->create($org->getId(), 'Engineering', 'engineering'); $invitationRepo->create($org->getId(), 'new@example.com', 'member', $inviterId);
Plugin system
use BetterAuth\Core\Plugin\PluginManager; use BetterAuth\Core\Plugin\PluginInterface; use BetterAuth\Core\Plugin\PluginContext; class AuditPlugin implements PluginInterface { public function getName(): string { return 'audit'; } public function getVersion(): string { return '1.0.0'; } public function getDependencies(): array { return []; } public function isEnabled(): bool { return true; } public function getConfig(): array { return []; } public function install(PluginContext $context): void { $context->registerHook('user.logged_in', function (array $data) { // log login event }); } } $plugins = new PluginManager(); $plugins->register(new AuditPlugin()); $plugins->loadAll();
Built-in hooks: user.created, user.logged_in, user.logged_out, token.created, session.created, oauth.linked.
Storage
All repositories are interface-based. The library ships with 20 PDO implementations that work with any SQL database (SQLite, MySQL, PostgreSQL, MariaDB).
Replace any repository by implementing the corresponding interface:
| Interface | Purpose |
|---|---|
UserRepositoryInterface |
User CRUD + lookup by email/provider |
SessionRepositoryInterface |
Session lifecycle |
RefreshTokenRepositoryInterface |
Token rotation with atomic consume() |
MagicLinkStorageInterface |
Passwordless tokens |
TotpStorageInterface |
TOTP secrets + backup codes |
OAuthClientRepositoryInterface |
OIDC client credentials |
OrganizationRepositoryInterface |
Multi-tenant organizations |
See src/Interfaces/ for the full list (28 interfaces).
Configuration
AuthConfig exposes all settings as constructor parameters with sensible defaults:
| Setting | Default |
|---|---|
| Session lifetime | 7 days |
| Session absolute lifetime | 30 days |
| Access token lifetime | 1 hour |
| Refresh token lifetime | 30 days |
| Rate limit max attempts | 5 |
| Rate limit decay | 300 seconds |
| Cookie HttpOnly | true |
| Cookie Secure | true |
| Cookie SameSite | Lax |
Security
- Tokens: Paseto V4 local encryption (XChaCha20-Poly1305), keys derived via HKDF
- Passwords: Argon2id only, automatic rehash on login when cost parameters change
- Rate limiting: per-key sliding window via PSR-6 cache (Redis, Memcached, APCu) with in-memory fallback
- Anti-enumeration: timing-safe dummy
password_verify()on user-not-found, silent success on unknown emails - CSRF:
hash_equalsstate validation on OAuth callbacks - Refresh tokens: atomic rotation via
consume()— prevents token reuse
Framework integration
This library provides the authentication core. Framework-specific bundles wire it into DI, routing, and middleware:
| Framework | Package |
|---|---|
| Symfony | betterauth/symfony-bundle |
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-16