From 43dc4cd53251c2c12d03a30cbd75082cda3afed8 Mon Sep 17 00:00:00 2001 From: memleakd <121398829+memleakd@users.noreply.github.com> Date: Sat, 30 May 2026 09:19:18 +0200 Subject: [PATCH] docs(auth): explain gateway action pattern Signed-off-by: memleakd <121398829+memleakd@users.noreply.github.com> --- .../references/authentication/auth_actions.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/references/authentication/auth_actions.md b/docs/references/authentication/auth_actions.md index 817b65297..7876cebb3 100644 --- a/docs/references/authentication/auth_actions.md +++ b/docs/references/authentication/auth_actions.md @@ -135,3 +135,55 @@ the condition cannot be determined. It is not a replacement for authorization. Once an action is already pending in the session, Shield continues that pending action instead of rechecking the condition. + +## Gateway Actions + +Shield allows one configured action for each authentication event, such as +`login` or `register`. If your application needs to choose between multiple +ways to complete that action, register one custom action as a gateway. +Use this when users may have different verification methods enabled, but Shield +should still treat them as one login action. + +A gateway action is a normal custom action. It can also be conditional, so it +only runs for users who have at least one supported method available. For +example, a login action can check whether the user has any two-factor methods +enabled: + +```php +public function appliesTo(User $user): bool +{ + return $user->getIdentity('mfa_email') !== null + || $user->getIdentity('mfa_sms') !== null; +} +``` + +Then register the gateway action as the login action: + +```php +public array $actions = [ + 'register' => null, + 'login' => \App\Authentication\Actions\TwoFactorGateway::class, +]; +``` + +The gateway action owns the choice between methods: + +1. `show()` displays the available methods for the pending user. +2. `handle()` validates the selected method, sends the challenge, and remembers + the selected method. +3. `verify()` verifies the challenge and completes the action. + +Use one action identity type for the gateway, returned by `getType()`. For +example, the gateway might return `mfa_gateway`. Do not create separate action +identities for each method, such as `email_2fa` and `sms_2fa`, because Shield +discovers the pending action through the configured action's type. + +Keep the action identity's `extra` value as the pending action message. If the +gateway needs to remember internal state, such as the selected method, store it +somewhere else, like `secret2` or an application-owned table. If the stored +value is sensitive, prefer application-owned protected storage. + +This pattern is useful for application-owned flows, especially when the choices +are code-delivery methods such as email or SMS. It does not make Shield provide +built-in MFA. Your application is still responsible for enrollment, delivery, +recovery, and method-specific security rules.