@@ -20,6 +20,8 @@ php artisan migrate
2020
2121Features
2222- ** Email** and ** SMS** one-time code challenges with pluggable channels
23+ - ** Configurable channel classes** - extend Email and SMS channels via configuration
24+ - ** Challenge generation without sending** - generate codes without automatic delivery
2325- Google Authenticator compatible ** TOTP** (RFC 6238) setup and verification
2426- Built-in QR code generation to display TOTP provisioning URI (uses bacon/bacon-qr-code)
2527- Remember device support via secure, hashed tokens stored in ` mfa_remembered_devices `
@@ -42,11 +44,17 @@ Usage
4244``` php
4345use CodingLibs\MFA\Facades\MFA;
4446
45- // Email/SMS
47+ // Email/SMS - Generate and send automatically
4648$challenge = MFA::issueChallenge(auth()->user(), 'email');
4749// then later
4850$ok = MFA::verifyChallenge(auth()->user(), 'email', '123456');
4951
52+ // Generate challenge without sending
53+ $challenge = MFA::generateChallenge(auth()->user(), 'email');
54+ // or
55+ $challenge = MFA::issueChallenge(auth()->user(), 'email', false);
56+ // Now handle sending manually
57+
5058// TOTP
5159$setup = MFA::setupTotp(auth()->user());
5260// $setup['otpauth_url'] -> QR code; then verify
@@ -129,10 +137,12 @@ Configuration
129137 - ** email** :
130138 - enabled (bool)
131139 - from_address, from_name, subject
140+ - channel: custom channel class (default: EmailChannel)
132141 - ** sms** :
133142 - enabled (bool)
134143 - driver: ` log ` (default) or custom integration
135144 - from: optional sender id/number
145+ - channel: custom channel class (default: SmsChannel)
136146 - ** totp** :
137147 - issuer: defaults to ` config('app.name') `
138148 - digits: 6 by default
@@ -155,9 +165,11 @@ Environment variables (examples)
155165MFA_EMAIL_FROM_ADDRESS="no-reply@example.com"
156166MFA_EMAIL_FROM_NAME="Example App"
157167MFA_EMAIL_SUBJECT="Your verification code"
168+ MFA_EMAIL_CHANNEL="App\Channels\CustomEmailChannel"
158169
159170MFA_SMS_DRIVER=log
160171MFA_SMS_FROM="ExampleApp"
172+ MFA_SMS_CHANNEL="App\Channels\CustomSmsChannel"
161173
162174MFA_TOTP_ISSUER="Example App"
163175MFA_TOTP_DIGITS=6
@@ -188,7 +200,8 @@ Database
188200 - ` mfa_recovery_codes ` : stores hashed recovery codes and usage timestamp
189201
190202API Overview (Facade ` MFA ` )
191- - ** issueChallenge(Authenticatable $user, string $method): ?MfaChallenge**
203+ - ** issueChallenge(Authenticatable $user, string $method, bool $send = true): ?MfaChallenge**
204+ - ** generateChallenge(Authenticatable $user, string $method): ?MfaChallenge** - Generate without sending
192205- ** verifyChallenge(Authenticatable $user, string $method, string $code): bool**
193206- ** setupTotp(Authenticatable $user, ?string $issuer = null, ?string $label = null): array** returns ` ['secret','otpauth_url'] `
194207- ** verifyTotp(Authenticatable $user, string $code): bool**
@@ -210,7 +223,73 @@ API Overview (Facade `MFA`)
210223 - ** getRemainingRecoveryCodesCount(Authenticatable $user): int**
211224 - ** clearRecoveryCodes(Authenticatable $user): int**
212225
213- Creating a Custom MFA Channel
226+ ## Custom Channel Classes
227+
228+ ### Configuration-Based Custom Channels
229+
230+ You can extend the built-in Email and SMS channels by configuring custom channel classes:
231+
232+ ``` php
233+ // config/mfa.php
234+ 'email' => [
235+ 'enabled' => true,
236+ 'channel' => \App\Channels\CustomEmailChannel::class,
237+ 'from_address' => 'noreply@example.com',
238+ // ... other config
239+ ],
240+
241+ 'sms' => [
242+ 'enabled' => true,
243+ 'channel' => \App\Channels\CustomSmsChannel::class,
244+ 'driver' => 'custom',
245+ // ... other config
246+ ],
247+ ```
248+
249+ ``` php
250+ // app/Channels/CustomEmailChannel.php
251+ use CodingLibs\MFA\Channels\EmailChannel;
252+
253+ class CustomEmailChannel extends EmailChannel
254+ {
255+ public function send(Authenticatable $user, string $code, array $options = []): void
256+ {
257+ // Custom sending logic
258+ Mail::to($user->email)->send(new CustomMfaMail($code, $this->config));
259+ }
260+ }
261+ ```
262+
263+ ### Programmatic Channel Registration
264+
265+ ``` php
266+ // In a service provider
267+ MFA::registerChannelFromConfig('custom_channel', [
268+ 'channel' => CustomChannel::class,
269+ 'channel_name' => 'custom_channel',
270+ 'custom_setting' => 'value'
271+ ]);
272+ ```
273+
274+ ## Challenge Generation Without Sending
275+
276+ Generate challenge codes without automatic delivery:
277+
278+ ``` php
279+ // Generate challenge without sending
280+ $challenge = MFA::generateChallenge(auth()->user(), 'email');
281+ echo $challenge->code; // Use the code as needed
282+
283+ // Or use issueChallenge with send=false
284+ $challenge = MFA::issueChallenge(auth()->user(), 'email', false);
285+
286+ // Manual sending
287+ $channel = MFA::getChannel('email');
288+ $channel->send(auth()->user(), $challenge->code, ['subject' => 'Custom Subject']);
289+ ```
290+
291+ ## Creating a Custom MFA Channel
292+
214293Steps
2152941 . Implement ` CodingLibs\MFA\Contracts\MfaChannel ` with a unique ` getName() ` and a ` send(...) ` method
2162952 . Register your channel during app boot (e.g., in a service provider) via ` MFA::registerChannel(...) `
0 commit comments