raid / authentication
Raid Authentication Package
Requires
- php: ^8.2
- laravel/sanctum: ^4.0
Requires (Dev)
- laravel/pint: ^1.10
This package is auto-updated.
Last update: 2024-04-26 13:29:53 UTC
README
This package is a wrapper for Laravel Sanctum
package.
it offers a new concepts to control authentication such as:
Installation
composer require raid/authentication
Configuration
Copy the config file to your own project by running the following command
php artisan vendor:publish --provider="Raid\Core\Authentication\Providers\AuthenticationServiceProvider"
Usage
Let's see basic usage for authentication with this package.
class LoginController extends Controller { public function __invoke(Request $request, UserAuthenticator $authenticator) { $channel = $authenticator->attempt($request->only([ 'email', 'password', ])); return response()->json([ 'channel' => $channel->getName(), 'token' => $channel->getStringToken(), 'resource' => $channel->getAuthenticatable(), 'errors' => $channel->errors()->toArray(), ]); } }
The Authenticator
will handle the authentication process and return a
Channel
instance that will contain the authentication information.
The Authenticator
class defines the Authenticates
class that will be
used to find the user, also it defines the Channels
that can be used to
authenticate the user.
The Channel
class depends on Workers
to find the authenticated user,
Then it can run some Rules
and Steps
to fulfill the authentication process.
Let's start digging into the Authenticates
, Authenticators
and Channels
classes.
Authenticates
The Authenticates
class will be used to find the user,
and return Illuminate\Contracts\Auth\Authenticatable
instance if found.
<?php namespace App\Models; use Illuminate\Contracts\Auth\Authenticatable; use Laravel\Sanctum\HasApiTokens; use Illuminate\Foundation\Auth\User as IlluminateUser; use Raid\Core\Authentication\Authenticates\Contracts\Authenticates; class User extends IlluminateUser implements Authenticates { use HasApiTokens; public function findForAuthentication(string $attribute, mixed $value): ?Authenticatable { return $this->where($attribute, $value)->first(); } }
The Authenticates
class must implement Authenticates
interface.
The Authenticates
class must define the findForAuthentication
method.
The findForAuthentication
method accepts two parameters: $attribute
and $value
passed from the given credentials.
The findForAuthentication
method must return Illuminate\Contracts\Auth\Authenticatable
instance if found.
Authenticator
The Authenticator
class will be used to define the Authenticates
class and Channels
to process authentication with different channels.
You can use this command to create a new authenticator class
php artisan raid:make-authenticator UserAuthenticator
This will output the following code
<?php namespace App\Http\Authentication\Authenticators; use Raid\Core\Authentication\Authenticators\Authenticator; use Raid\Core\Authentication\Authenticators\Contracts\AuthenticatorInterface; class UserAuthenticator extends Authenticator implements AuthenticatorInterface { public const NAME = ''; protected string $authenticates = ''; protected array $channels = []; }
Let's configure the Authenticator
class.
<?php namespace App\Http\Authentication\Authenticators; use App\Models\User; use App\Http\Authentication\Channels\SystemChannel; use Raid\Core\Authentication\Authenticators\Authenticator; use Raid\Core\Authentication\Authenticators\Contracts\AuthenticatorInterface; class UserAuthenticator extends Authenticator implements AuthenticatorInterface { public const NAME = 'user'; protected string $authenticates = User::class; protected array $channels = [ SystemChannel::class, ]; }
The Authenticator
class must implement AuthenticatorInterface
.
The Authenticator
class must extend the Authenticator
class.
The Authenticator
class should define the name
constant.
The Authenticator
class must define the authenticates
property.
The Authenticator
class should define the channels
property.
The Authenticator
class can handle authentication with any of its defined Channels
.
You can define the channels
with two ways:
channels
propertyconfig\authentication.php
file
<?php use App\Http\Authentication\Authenticators\UserAuthenticator; use App\Http\Authentication\Channels\SystemChannel; return [ 'authenticator_channels' => [ UserAuthenticator::class => [ SystemChannel::class, ], ], ];
This definition allows you to authenticate users with different Channels
using channel name.
If you didn't pass any channel, the default channel will be used.
<?php class LoginController extends Controller { public function __invoke(Request $request, UserAuthenticator $authenticator) { $credentials = $request->only([ 'email', 'password', ]); $channel = $authenticator->attempt($credentials, 'system'); } }
Channel
The Channel
class will be used to handle authentication process using the passed Authenticates
class and Credentials
.
You can use this command to create a new channel class
php artisan raid:make-channel SystemChannel
This will output the following code
<?php namespace App\Http\Authentication\Channels; use Raid\Core\Authentication\Channels\Channel; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; class SystemChannel extends Channel implements ChannelInterface { public const NAME = ''; }
Let's configure the Channel
class.
<?php namespace App\Http\Authentication\Channels; use Raid\Core\Authentication\Channels\Channel; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; class SystemChannel extends Channel implements ChannelInterface { public const NAME = 'system'; }
The Channel
class must implement ChannelInterface
.
The Channel
class must extend the Channel
class.
The Channel
class should define the name
constant.
The Channel
works through Workers
to find the authenticated user,
It matches the defined Workers
attribute with the given credentials.
<?php namespace App\Http\Authentication\Channels; use App\Http\Authentication\Workers\EmailWorker; use App\Http\Authentication\Workers\PhoneWorker; use Raid\Core\Authentication\Channels\Channel; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; class SystemChannel extends Channel implements ChannelInterface { public const NAME = 'system'; protected array $workers = [ EmailWorker::class, PhoneWorker::class, ]; }
You can define the workers
with two ways:
workers
propertyconfig\authentication.php
file
<?php use App\Http\Authentication\Channels\SystemChannel; use App\Http\Authentication\Workers\EmailWorker; use App\Http\Authentication\Workers\PhoneWorker; return [ 'channel_workers' => [ SystemChannel::class => [ EmailWorker::class, PhoneWorker::class, ], ], ];
This definition allows you to authenticate users with different Workers
using worker defined attribute.
Worker
The Worker
class will be used to find the authenticated user based on the given credentials.
You can use this command to create a new worker class
php artisan raid:make-worker PhoneWorker
This will output the following code
<?php namespace App\Http\Authentication\Workers; use Raid\Core\Authentication\Workers\Worker; use Raid\Core\Authentication\Workers\Contracts\WorkerInterface; class PhoneWorker extends Worker implements WorkerInterface { public const ATTRIBUTE = ''; }
Let's configure the Worker
class.
<?php namespace App\Http\Authentication\Workers; use Raid\Core\Authentication\Workers\Worker; use Raid\Core\Authentication\Workers\Contracts\WorkerInterface; class PhoneWorker extends Worker implements WorkerInterface { public const ATTRIBUTE = 'phone'; }
The Worker
class must implement WorkerInterface
.
The Worker
class must extend the Worker
class.
The Worker
class must define the attribute
constant.
The Worker
can also define a QUERY_ATTRIBUTE
constant to find the user.
The Attribute
is used to match the Worker
with the given credentials.
The Query Attribute
is passed to the findForAuthentication
method to find the user,
if not defined, it will use the Attribute
constant instead.
<?php namespace App\Http\Authentication\Workers; use Raid\Core\Authentication\Workers\Worker; use Raid\Core\Authentication\Workers\Contracts\WorkerInterface; class PhoneWorker extends Worker implements WorkerInterface { public const ATTRIBUTE = 'phone'; public const QUERY_ATTRIBUTE = 'phone_number'; }
Rule
The Rule
class will be used to validate the authentication.
To apply Rules
you need to implement ShouldRunRules
interface to the Channel
,
Then you can define your Rules
.
<?php namespace App\Http\Authentication\Channels; use App\Http\Authentication\Rules\VerifiedRule; use Raid\Core\Authentication\Channels\Channel; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Channels\Contracts\ShouldRunRules; class SystemChannel extends Channel implements ChannelInterface, ShouldRunRules { public const NAME = 'system'; protected array $rules = [ VerifiedRule::class, ]; }
You can define the rules
with two ways:
rules
propertyconfig\authentication.php
file
use App\Http\Authentication\Channels\SystemChannel; use App\Http\Authentication\Rules\VerifiedRule; return [ 'channel_rules' => [ SystemChannel::class => [ VerifiedRule::class, ], ];
The Rules
will be applied to the Channel
to validate the authentication.
You can use this command to create a new rule class
php artisan raid:make-rule VerifiedRule
This will output the following code
<?php namespace App\Http\Authentication\Rules; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Rules\Contracts\RuleInterface; class VerifiedRule implements RuleInterface { public function handle(ChannelInterface $channel): bool { } public function fail(ChannelInterface $channel): void { } }
Let's configure the Rule
class.
<?php namespace App\Http\Authentication\Rules; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Rules\Contracts\RuleInterface; class VerifiedRule implements RuleInterface { public function handle(ChannelInterface $channel): bool { return $channel->getAuthenticatable()->isVerified(); } public function fail(ChannelInterface $channel): void { $channel->fail(message: __('auth.unverified')); } }
The Rule
class must implement RuleInterface
.
The Rule
class must define the handle
method.
The handle
method must return a boolean value.
The handle
method will be called by the Channel
to validate the authentication.
Step
The Step
class will be used to add additional steps to the authentication process.
To apply Steps
you need to implement ShouldRunSteps
interface to the Channel
,
Then you can define your Steps
.
<?php namespace App\Http\Authentication\Channels; use App\Http\Authentication\Steps\TwoFactorEmailStep; use Raid\Core\Authentication\Channels\Channel; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Channels\Contracts\ShouldRunSteps; class SystemChannel extends Channel implements ChannelInterface, ShouldRunSteps { public const NAME = 'system'; protected array $steps = [ TwoFactorEmailStep::class, ], }
You can define the steps
with two ways:
steps
propertyconfig\authentication.php
file
<?php use App\Http\Authentication\Channels\SystemChannel; use App\Http\Authentication\Steps\TwoFactorEmailStep; return [ 'channel_steps' => [ SystemChannel::class => [ TwoFactorEmailStep::class, ], ];
The Steps
will be applied to the Channel
to add additional steps to the authentication process.
You can use this command to create a new step class
php artisan raid:make-step TwoFactorEmailStep
This will output the following code
<?php namespace App\Http\Authentication\Steps; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Steps\Contracts\StepInterface; class TwoFactorEmailStep implements StepInterface { public function handle(ChannelInterface $channel): void { } }
Let's configure the Step
class.
<?php namespace App\Http\Authentication\Steps; use App\Core\Integrations\Mail\MailService; use App\Mail\TwoFactorMail; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Steps\Contracts\StepInterface; class TwoFactorEmailStep implements StepInterface { public function __construct( private readonly MailService $mailService, ) { } public function handle(ChannelInterface $channel): void { $code = generate_code(); $authenticatable = $channel->getAuthenticatable(); $authenticatable->update([ 'two_factor_email_code' => $code, ]); $this->send( $authenticatable->getAttribute('email'), $authenticatable->getAttribute('name'), $code, ); } private function send(string $email, string $name, int $code): void { $this->mailService->send( $email, new TwoFactorMail($name, $code), ); } }
The Step
must implement StepInterface
.
The Step
class must define the handle
method.
The handle
method will be called by the Channel
to add additional steps to the authentication process.
hint:
Running any steps means that the Channel
will stop the authentication process without issuing any tokens,
This approach can be used in Multi-Factor
authentication.
You can configure your step class to work through queues.
<?php namespace App\Http\Authentication\Steps; use App\Mail\TwoFactorMail; use App\Core\Integrations\Mail\MailService; use Raid\Core\Authentication\Channels\Contracts\ChannelInterface; use Raid\Core\Authentication\Steps\Contracts\StepInterface; use Raid\Core\Authentication\Steps\Contracts\ShouldRunQueue; class TwoFactorEmailStep implements StepInterface, ShouldRunQueue { use HasQueue; public function __construct( private readonly MailService $mailService, ) { } public function handle(ChannelInterface $channel): void { $code = generate_code(); $authenticatable = $channel->getAuthenticatable(); $authenticatable->update([ 'two_factor_email_code' => $code, ]); $this->send( $authenticatable->getAttribute('email'), $authenticatable->getAttribute('name'), $code, ); } private function send(string $email, string $name, int $code): void { $this->mailService->send( $email, new TwoFactorMail($name, $code), ); } }
The queue step must implement ShouldRunQueue
The ShouldRunQueue
class must define the queue
method.
You can use the HasQueue
trait to define the queue
method with its default configuration.
You can override the trait queue configurations by defining these methods:
protected function getJob(): string { // return your Job class; } protected function getConnection(): ?string { // return your Connection name; } protected function getQueue(): ?string { // return your Queue name; } protected function getDelay(): DateInterval|DateTimeInterface|int|null { // return your Delay interval; }
Channel Errors
You can use the Channel
class to handle authentication errors through errors
method.
You can add errors to channel using these methods:
$channel->errors()->add('key', 'message'); // or $channel->fail('key', 'message');
You can check the channel errors using these methods:
$hasErrors = $channel->failed(); //or $hasErrors = $channel->errors()->any(); $hasError = $channel->errors()->has('key'); $errorsByKey = $channel->errors()->get('key'); $firstError = $channel->errors()->first(); $lastError = $channel->errors()->last(); $errorsAsArray = $channel->errors()->toArray(); $errorsAsJson = $channel->errors()->toJson();
And that's it.
License
The MIT License (MIT). Please see License File for more information.
Credits
Security
If you discover any security-related issues, please email instead of using the issue tracker.
About Raid
Raid is a PHP framework created by Mohamed Khedr, and it is maintained by Mohamed Khedr.
Support Raid
Raid is an MIT-licensed open-source project. It's an independent project with its ongoing development made possible.