Security Auth - v1.0.0
Custom 2FA Providers
Implement TwoFactorProvider to add a new verification method (SMS, hardware key challenge wrappers, custom OTP service, etc.):
namespace ArtisanPackUI\SecurityAuth\TwoFactor\Contracts;
use Illuminate\Contracts\Auth\Authenticatable;
interface TwoFactorProvider
{
public function getName(): string;
public function sendChallenge( Authenticatable $user ): void;
public function verify( Authenticatable $user, string $code ): bool;
}
Example: SMS via Twilio
namespace App\SecurityAuth\TwoFactor;
use ArtisanPackUI\SecurityAuth\TwoFactor\Contracts\TwoFactorProvider;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\Cache;
use Twilio\Rest\Client;
class SmsProvider implements TwoFactorProvider
{
public function __construct(
protected Client $twilio,
protected string $fromNumber,
) {}
public function getName(): string
{
return 'sms';
}
public function sendChallenge( Authenticatable $user ): void
{
$code = str_pad( (string) random_int( 0, 999_999 ), 6, '0', STR_PAD_LEFT );
Cache::put( $this->cacheKey( $user ), $code, now()->addMinutes( 5 ) );
$this->twilio->messages->create( $user->phone_number, [
'from' => $this->fromNumber,
'body' => "Your verification code: {$code}",
] );
}
public function verify( Authenticatable $user, string $code ): bool
{
$expected = Cache::pull( $this->cacheKey( $user ) );
return $expected !== null && hash_equals( $expected, trim( $code ) );
}
protected function cacheKey( Authenticatable $user ): string
{
return "2fa_sms:{$user->getAuthIdentifier()}";
}
}
Registering the provider
use ArtisanPackUI\SecurityAuth\TwoFactor\TwoFactorManager;
use App\SecurityAuth\TwoFactor\SmsProvider;
use Twilio\Rest\Client;
$this->app->afterResolving( TwoFactorManager::class, function ( TwoFactorManager $manager ): void {
$manager->extend( new SmsProvider(
twilio: new Client( config('services.twilio.sid'), config('services.twilio.token') ),
fromNumber: config('services.twilio.from'),
) );
} );
Now usable:
TwoFactor::provider('sms')->sendChallenge( $user );
TwoFactor::provider('sms')->verify( $user, $code );
To make SMS the default:
'two_factor' => ['default_provider' => 'sms'],
Conventions
- Don't store secrets in plaintext. If your provider keeps state (codes, salts, etc.), use
Cachewith a TTL, not the DB. - Hash-compare codes.
hash_equalsover===to avoid timing attacks. - Single-use codes.
Cache::pull()rather thanCache::get()so a verified code can't be replayed. - Rate limit
sendChallenge. Each call costs you money (Twilio, etc.) and a stream of requests could be abused. Wrap with Laravel'sRateLimiter.