Search by

makaveli / laravel-accept-code

Ma1kaveli

Advanced Accept Code for Laravel

Package info

github.com/Ma1kaveli/laravel-accept-code

pkg:composer/makaveli/laravel-accept-code

Statistics

Installs: 27

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.1.18 2026-09-14 14:46 UTC

README

Packagist Version Packagist Downloads License

🌍 Languages

Table of Contents

  1. Introduction
  2. Requirements
  3. Installation
  4. Configuration
  5. Core Components
  6. Usage
  7. Extending the Package
  8. Recommendations
  9. Useful Links

Introduction

makaveli/laravel-accept-code is a package for managing one‑time verification codes (OTP) in Laravel. It implements typical scenarios: login by code (password‑less), registration with confirmation, and password reset. The package uses the makaveli ecosystem (JWT authentication, logging, CRUD layers) and can be easily integrated into any project.

Key features:

  • Generation and sending of verification codes (email/SMS).
  • Code validity checks with a 10-minute TTL, resend delay, and a five-attempt limit.
  • One active OTP record per credential, operation slug, and delivery type.
  • Ready‑to‑use DTOs and requests for typical actions.
  • Integration with laravel-jwt-auth to issue tokens after successful confirmation.
  • Integration with laravel-logger for logging actions.
  • Integration with laravel-login-history to record login history.
  • Flexible configuration via callbacks (user lookup, notification sending, permission checks).

Requirements

Installation

  1. Install the package via Composer:

    composer require makaveli/laravel-accept-code
  2. Run the migrations to create the accept_codes table:

    php artisan migrate:accept-code
  3. (Optional) Publish the configuration file to adjust settings:

    php artisan vendor:publish --tag=accept-code-config

    This will create the file config/accept-code.php.

Upgrade notes

  • Run migrations during a maintenance window. The unique-key migration keeps the newest duplicate for each credetinal + slug + type combination and removes older duplicates.
  • The default OTP TTL changed from 12 hours to 10 minutes and verification is blocked after five failed attempts.
  • Concurrent resend protection requires a cache driver that supports atomic locks, such as Redis, Memcached, DynamoDB, database, file, or array within one process.
  • Notification callbacks run synchronously after the OTP transaction commits and before the resend lock is released.
  • Configure check_user_can_authenticate to validate account state and application-area access without relying on a transient role_id attribute.

Configuration

The config/accept-code.php file contains the main settings. All callbacks are specified as arrays [class, method] or closures.

Parameter Description Default
accept_code_slugs List of allowed slugs ['login', 'registration', 'reset-password']
get_user_by_phone Callback to find a user by phone number [\App\Modules\User\Repositories\UserRepository::class, 'getByPhone']
get_user_by_email Callback to find a user by email similar
get_user_role_id Callback to get the user's role ID
check_permission_to_auth Callback to check permissions for authentication
check_user_can_authenticate Preferred callback that receives the user and isPortal; falls back to the role callbacks when unset null
verified_user Callback to set the is_verified flag
change_password Callback to change the password
logger_slugs Slugs for logging
email Callbacks for sending email
phone Callbacks for sending SMS
accept_code_delay_repeat_ttl Delay between resends (seconds) 90
accept_code_ttl Code lifetime (seconds) 600 (10 minutes)
accept_code_max_attempts Maximum failed code checks before the OTP is blocked 5
accept_code_lock_ttl Atomic resend lock lifetime (seconds) 60
accept_code_lock_wait Maximum wait for the resend lock (seconds) 5
phone_validation_rule Phone validation rule ValidPhone::create()
password_validation_rule Password validation rule ValidPassword::create()

Example configuration:

return [
    'get_user_by_phone' => [\App\Modules\User\Repositories\UserRepository::class, 'getByPhone'],
    'get_user_by_email' => [\App\Modules\User\Repositories\UserRepository::class, 'getByEmail'],
    'check_user_can_authenticate' => [\App\Modules\Auth\Actions\AuthActions::class, 'ensureUserCanAuthenticate'],
    'verified_user' => [\App\Modules\Auth\Services\AuthService::class, 'verifiedUser'],
    'change_password' => [\App\Modules\Auth\Services\AuthService::class, 'changePassword'],
    'email' => [
        'send_verification_notification' => [\App\Modules\Email\Actions\EmailSenderActions::class, 'sendVerificationNotification'],
    ],
    'phone' => [
        'send_login_code' => [\App\Modules\Phone\Actions\MTCActions::class, 'sendLoginCode'],
    ],
    // ...
];

Core Components

Actions

The package provides three groups of actions:

  • LoginAcceptCodeActions – sending and confirming a code for login.
  • RegistrationAcceptCodeActions – sending and confirming a code for registration.
  • ResetPasswordAcceptCodeActions – sending, verifying, and confirming a code for password reset.

Each class inherits from AcceptCodeActions, which contains common methods (transactions, logging, JWT generation, login history recording).

DTO

  • AcceptCodeDTO – a universal DTO for passing data between request and action. Contains fields: login, isPortal, code, type, captchaToken, password, request.
    Static methods: fromRegistrationSendRequest, fromRegistrationAcceptAccountRequest, fromLoginSendRequest, fromLoginAcceptRequest, fromPasswordResetSendRequest, fromPasswordResetVerifyRequest, fromPasswordResetSetNewRequest.

  • AcceptCodeFormDTO – a DTO for creating/updating a code record. Contains: userId, credetinal, slug, type, userVerified.
    Static methods: fromCredetinalsPhone, fromCredetinals.

Repository

AcceptCodeRepository extends BaseRepository and contains methods for working with codes:

  • getSendAcceptCodeByUserId – get the last sent code for a user.
  • getSendAcceptCode – get a code by credentials and slug.
  • checkDelayIsOver – checks whether a new code can be sent (delay not exceeded).
  • acceptCodeIsValid – checks whether the code has not expired.
  • getAcceptCode – generic method to fetch a code with existence and TTL checks.
  • canSendLoginAcceptCode, canSendPasswordResetCode, canSendRegistrationAcceptCode – checks whether sending is allowed.

Service

AcceptCodeService extends BaseService and is responsible for creating/updating code records:

  • _create(AcceptCodeFormDTO $dto) – creates a new record with a random 4‑digit code.
  • createOrUpdate(AcceptCodeFormDTO $dto) – atomically creates or rotates the unique OTP record and resets failed attempts.

Requests

The package includes ready‑made FormRequests for each endpoint:

  • LoginSendCodeRequest
  • LoginAcceptCodeRequest
  • RegistrationSendCodeRequest
  • RegistrationAcceptAccountRequest
  • PasswordResetSendRequest
  • PasswordResetVerifyRequest
  • PasswordResetSetNewRequest

All of them extend FormRequest and use validation rules from the configuration.

Model

AcceptCode – Eloquent model with fields user_id, credetinal, code, type, slug, and attempts. The database permits one active record for each credetinal + slug + type combination.

Console Commands

  • php artisan migrate:accept-code – runs the package migrations.

Usage

Login by Code

  1. Send code
use AcceptCode\Actions\LoginAcceptCodeActions;
use AcceptCode\DTO\AcceptCodeDTO;

$actions = new LoginAcceptCodeActions();
$dto = AcceptCodeDTO::fromLoginSendRequest($request);
$actions->sendLoginCode($dto); // returns true or throws an exception
  1. Confirm code and authenticate
[$accessToken, $refreshToken, $user] = $actions->acceptLoginCodeAndAuth(
    AcceptCodeDTO::fromLoginAcceptRequest($request)
);
// You can return the tokens and user data in the response

Registration with Confirmation

  1. Send registration code
use AcceptCode\Actions\RegistrationAcceptCodeActions;

$actions = new RegistrationAcceptCodeActions();
$dto = AcceptCodeDTO::fromRegistrationSendRequest($request);
$actions->sendRegistrationCode($dto);
  1. Confirm code and generate tokens
[$accessToken, $refreshToken, $user] = $actions->acceptRegistrationCode(
    AcceptCodeDTO::fromRegistrationAcceptAccountRequest($request)
);

Password Reset

  1. Send password reset code
use AcceptCode\Actions\ResetPasswordAcceptCodeActions;

$actions = new ResetPasswordAcceptCodeActions();
$dto = AcceptCodeDTO::fromPasswordResetSendRequest($request);
$actions->sendResetPasswordCode($dto);
  1. Verify code (optional step)
$actions->verifyResetPasswordCode(
    AcceptCodeDTO::fromPasswordResetVerifyRequest($request)
);
// if the code is invalid or expired, an exception will be thrown
  1. Set new password
$user = $actions->acceptResetPasswordCode(
    AcceptCodeDTO::fromPasswordResetSetNewRequest($request)
);

The final reset validates the OTP again, invokes only the configured change_password callback, and deletes that OTP in the same transaction. It does not invoke verified_user. Successful login and registration confirmation likewise consume only the OTP used for that operation; OTPs for other slugs remain available.

Extending the Package

You can replace any part of the package by overriding:

  • Model – create your own model extending AcceptCode, and change $table if needed.
  • Repository – extend AcceptCodeRepository and override methods for custom logic.
  • Service – extend AcceptCodeService and override _create or createOrUpdate.
  • Actions – create your own classes extending AcceptCodeActions and override the necessary methods.
  • DTO and Request – use them directly or create your own by extending the base classes.

All dependencies (logging, JWT, login history) are placed in traits that can be overridden in your own action.

Recommendations

  • Configure the callbacks in the config file – specify your own methods for user lookup, sending notifications, and permission checks.
  • Use asynchronous logging – actions already use successAsyncLog and errorAsyncLog via the AsyncLogger trait.
  • Set reasonable TTLs – adjust accept_code_ttl and accept_code_delay_repeat_ttl according to your security requirements.
  • Protect endpoints from spam – add a captcha (e.g., captchaToken is already present in the DTO) and configure rate limits.
  • For email confirmation – set up email notifications in the email section.
  • For SMS confirmation – specify your SMS services in the phone section.

Useful Links