mubas / laravel-auth-api
Laravel package for MUBAS One API authentication
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.2
- laravel/framework: >=11.0 <15.0
Requires (Dev)
- orchestra/testbench: ^9.0 || ^10.0
- phpunit/phpunit: ^10.0
README
Overview
A Laravel package that provides seamless integration with the MUBAS One API for LDAP-based authentication. This package replaces custom LDAP implementations with a simple, secure, and standardized approach to authenticate users against an LDAP directory via a central API service.
Features
- Simple integration with existing Laravel applications
- Secure LDAP authentication via centralized API
- Token-based session management with expiration support
- Automatic user creation on first login
- Support for multiple authentication modes (local_only, remote_first, fallback)
- Support for multiple local authentication models in sequence (up to 3 models)
- Dynamic field mapping (name, slug, expires_at)
- Schema-aware user synchronization
- Event-driven architecture for authentication lifecycle
- Token verification and global logout capabilities
- Domain restriction validation
- Comprehensive error handling with specific exception types
Requirements
- PHP >= 8.2
- Laravel >=11.0
- Access to MUBAS One API service
Installation
Install the package via Composer:
composer require mubas/laravel-auth-api
Publish the configuration file:
php artisan vendor:publish --provider="Mubas\AuthApi\AuthApiServiceProvider"--tag="config"
Configuration
After publishing, configure the package by updating config/auth_api.php:
<?php
return [
// MUBAS One API base URL
'api_base_url' => env('AUTH_API_BASE_URL', 'https://your-api-domain.com'),
// Application token for API access (provided by MUBAS One API admin)
'app_token' => env('AUTH_API_APP_TOKEN'),
// Domain restriction (optional)
// Limits authentication to specific email domains
'allowed_domain' => env('AUTH_API_ALLOWED_DOMAIN', ''),
// Authentication mode: 'local_only', 'remote_first', 'fallback'
// 'local_only' - Use local authentication only
// 'remote_first' - Try MUBAS One first, then fallback to local if remote fails
// 'fallback' - Try local first, then fallback to MUBAS One if local fails
'auth_mode' => env('AUTH_API_AUTH_MODE', 'local_only'),
// User model for authentication
'user_model' => env('AUTH_API_USER_MODEL', '\\App\\Models\\User'),
// Local authentication configuration with multiple models support (max 3 models)
'local_auth' => [
'models' => [
[
'model' => env('AUTH_API_LOCAL_MODEL_1', '\\App\\Models\\User'),
'username_field' => env('AUTH_API_USERNAME_FIELD_1', 'email'),
'password_field' => env('AUTH_API_PASSWORD_FIELD_1', 'password'),
'password_hashed' => env('AUTH_API_PASSWORD_HASHED_1', true),
],
[
'model' => env('AUTH_API_LOCAL_MODEL_2', ''),
'username_field' => env('AUTH_API_USERNAME_FIELD_2', 'username'),
'password_field' => env('AUTH_API_PASSWORD_FIELD_2', 'password'),
'password_hashed' => env('AUTH_API_PASSWORD_HASHED_2', true),
],
[
'model' => env('AUTH_API_LOCAL_MODEL_3', ''),
'username_field' => env('AUTH_API_USERNAME_FIELD_3', 'email'),
'password_field' => env('AUTH_API_PASSWORD_FIELD_3', 'password'),
'password_hashed' => env('AUTH_API_PASSWORD_HASHED_3', true),
],
],
],
];
Add these environment variables to your .env file:
AUTH_API_BASE_URL=https://your-mubas-api.com
AUTH_API_APP_TOKEN=your_application_token_here
AUTH_API_ALLOWED_DOMAIN=
AUTH_API_AUTH_MODE=local_only
AUTH_API_USER_MODEL=\App\Models\User
# Local authentication configuration
AUTH_API_LOCAL_MODEL_1=\App\Models\User
AUTH_API_USERNAME_FIELD_1=email
AUTH_API_PASSWORD_FIELD_1=password
AUTH_API_PASSWORD_HASHED_1=true
# For additional models:
# AUTH_API_LOCAL_MODEL_2=\App\Models\Admin
# AUTH_API_USERNAME_FIELD_2=username
# AUTH_API_PASSWORD_FIELD_2=password
# AUTH_API_PASSWORD_HASHED_2=true
# AUTH_API_LOCAL_MODEL_3=\App\Models\Staff
# AUTH_API_USERNAME_FIELD_3=staff_id
# AUTH_API_PASSWORD_FIELD_3=staff_password
# AUTH_API_PASSWORD_HASHED_3=true
Usage
Basic Authentication
Unified API Login
The package provides a single method for all authentication needs:
use Mubas\AuthApi\Facades\AuthApi;
try {
$result = AuthApi::apiLogin($identifier, $password);
$user = $result['user']; // Authenticated user model instance
$token = $result['token']; // Plain text API token
$expiresAt = $result['expires_at'] ?? null; // Token expiration timestamp (if supported)
// Additional data from remote API (if authenticated via MUBAS One)
$userFields = $result['user_fields_from_api'] ?? [];
} catch (\Illuminate\Validation\ValidationException $e) {
// Handle credential validation errors
$errors = $e->errors();
} catch (\Mubas\AuthApi\Exceptions\DomainRestrictionException $e) {
// Handle domain restriction errors
} catch (\Mubas\AuthApi\Exceptions\ApiConnectionException $e) {
// Handle API connection errors
}
Based on the auth_mode configuration, this method will:
local_only: Authenticate against local user database (trying models in sequence)remote_first: Try MUBAS One first, fallback to local if remote failsfallback: Try local first (trying models in sequence), fallback to MUBAS One if local fails
Advanced Usage
Multiple Local Authentication Models
Configure multiple models to try in sequence for local authentication:
// In config/auth_api.php
'local_auth' => [
'models' => [
[
'model' => '\App\Models\User', // Firstmodel to try
'username_field' => 'email', // Field to use for username
'password_field' => 'password', // Field to use for password
'password_hashed' => true, // Whether password is hashed
],
[
'model' => '\App\Models\Admin', // Second model to try
'username_field' => 'username', // Field to use for username
'password_field' => 'admin_password', // Different password field
'password_hashed' => true, // Whether password is hashed
],
],
],
The system will attempt authentication with each model in sequence until one succeeds or all fail.
Custom User ModelMapping
By default, the package creates standard Laravel users. You can customize this by implementing a custom user resolver:
// In a service provider
AuthApi::resolveUserUsing(function(array $userData) {
return User::updateOrCreate(
['email' => $userData['email']],
[
'name' => $userData['name'],
'last_login' => now(),
// ... other attributes
]
);
});
Direct API Calls
For direct access to the API endpoints:
// Verify a token
$isValid = AuthApi::verifyToken($userToken);
// Logout from all sessions
$logoutCount = AuthApi::globalLogout($email);
// Set custom user resolver for testing or custom logic
AuthApi::resolveUserUsing(function(array $userData) {
return User::updateOrCreate(
['email' => $userData['email']],
['name' => $userData['name']]
);
});
Middleware
The package provides middleware for protecting routes that require authentication against the API:
// In app/Http/Kernel.php
protected $routeMiddleware = [
// ... other middleware
'auth.api' => \Mubas\AuthApi\Http\Middleware\ApiAuthMiddleware::class,
];
Then use it in your routes:
Route::middleware(['auth.api'])->group(function () {
// Protected routes
});
Events
The package dispatches events during authentication flow:
Mubas\AuthApi\Events\UserLoggedIn- When a user successfully logs inMubas\AuthApi\Events\UserFailedLogin- When a login attempt failsMubas\AuthApi\Events\UserLoggedOut- Whena user logs out
Listen to these events by registering listeners in your EventServiceProvider:
protected $listen = [
\Mubas\AuthApi\Events\UserLoggedIn::class => [
\App\Listeners\LogSuccessfulLogin::class,
],
\Mubas\AuthApi\Events\UserFailedLogin::class => [
\App\Listeners\LogFailedLogin::class,
],
];
Error Handling
The package throws specific exceptions for different error conditions:
\Illuminate\Validation\ValidationException: For credential validation errors\Illuminate\Auth\Access\AuthorizationException: For authorization failures\Mubas\AuthApi\Exceptions\ApiConnectionException: For API connectivity issues\Mubas\AuthApi\Exceptions\DomainRestrictionException: When email domain is not allowed
Handle these exceptions appropriately in your application:
try {
$result = AuthApi::apiLogin($email, $password);
} catch (\Illuminate\Validation\ValidationException $e) {
// Handle validation errors
return response()->json(['errors' => $e->errors()], 422);
} catch (\Mubas\AuthApi\Exceptions\ApiConnectionException $e) {
// Handle API connectionissues
Log::error('Authentication API connection failed: ' . $e->getMessage());
return response()->json(['error' => 'Authentication service unavailable'], 503);
}
Testing
The package provides helpers for testing your authentication functionality:
// In your tests
use Mubas\AuthApi\Tests\MocksAuthApi;
class LoginTest extends TestCase
{
use MocksAuthApi;
public function test_user_can_login_via_api()
{
$this->mockAuthApiLogin(['email' => 'test@mubas.ac.mw', 'name' => 'Test User']);
$response = $this->post('/login', [
'email' => 'test@mubas.ac.mw',
'password' => 'password'
]);
$response->assertStatus(200);
}
}