Authentication & Authorization¶
This document explains how authentication and authorization work in the package and which extension points you can override.
Note
The authentication and authorization flow is documented through explicit contracts and package extension points.
Authentication Overview¶
The package uses independent HTTP Basic Auth validated through CredentialValidatorInterface.
- credentials are not checked through Laravel's
auth()guard - username and password come from the configured
webdav-server.auth.account_model - on success, the validator returns a
WebDavPrincipalValueObject - on failure, the validator throws a package auth exception such as
InvalidCredentialsException - if
webdav-server.logging.driveris configured, authentication outcomes are logged atinfo - debug logging traces credential extraction and validator flow without logging raw credentials
Default: Database-Backed Authentication¶
By default, DatabaseCredentialValidator uses EloquentAccountRepository plus the configured account model:
// config/webdav-server.php
'auth' => [
'account_model' => \App\Models\WebDavAccount::class,
'user_model' => \App\Models\User::class,
'username_column' => 'username',
'password_column' => 'password_encrypted',
'enabled_column' => 'enabled',
'user_id_column' => 'user_id',
'display_name_column' => 'username',
],
Passwords must be stored as hashes.
Managing WebDAV Accounts With Artisan¶
For the default Eloquent-backed account model, the package ships with dedicated artisan commands.
Create an account:
php artisan laravel-webdav-server:account:create testuser password --display-name="Test User" --user-id=1
Show one account:
php artisan laravel-webdav-server:account:show testuser
List all accounts:
php artisan laravel-webdav-server:account:list
Update an account:
php artisan laravel-webdav-server:account:update testuser --secret=new-password --disable
Important behavior:
- the commands use the configured
webdav-server.auth.account_model - username, password, enabled, linked-user, and display-name fields follow the configured auth column mapping
- passwords passed to the create and update commands are always stored as hashes
- the root command
php artisan laravel-webdav-servershows the package-specific command entry points
Custom Authentication¶
To implement custom authentication such as LDAP or API token lookup, implement CredentialValidatorInterface:
namespace App\Services;
use App\Models\User;
use N3XT0R\LaravelWebdavServer\Contracts\Auth\CredentialValidatorInterface;
use N3XT0R\LaravelWebdavServer\Exception\Auth\InvalidCredentialsException;
use N3XT0R\LaravelWebdavServer\ValueObjects\WebDavPrincipalValueObject;
final class LdapCredentialValidator implements CredentialValidatorInterface
{
public function validate(string $username, string $password): WebDavPrincipalValueObject
{
if (! $this->validateLdap($username, $password)) {
throw new InvalidCredentialsException('Invalid WebDAV credentials.');
}
$user = User::firstOrCreate(
['email' => $username],
['name' => $username, 'password' => bcrypt(bin2hex(random_bytes(16)))],
);
return new WebDavPrincipalValueObject(
id: (string) $user->getAuthIdentifier(),
displayName: $user->name,
user: $user,
);
}
private function validateLdap(string $username, string $password): bool
{
return true;
}
}
Register it in your application:
use App\Services\LdapCredentialValidator;
use N3XT0R\LaravelWebdavServer\Contracts\Auth\CredentialValidatorInterface;
public function register(): void
{
$this->app->bind(CredentialValidatorInterface::class, LdapCredentialValidator::class);
}
Authorization Overview¶
Authorization is handled by PathAuthorizationInterface.
The default implementation is GatePathAuthorization, which delegates to Laravel Gate / policies and throws
Sabre\DAV\Exception\Forbidden when access is denied.
If logging is enabled, GatePathAuthorization emits:
debugbefore the Gate check with ability, principal, disk, and path contextinfowhen access is denied
Default: Gate-Based Policies¶
The package registers its own reference policy for PathResourceDto.
If your application needs custom rules, register your own policy in the app:
use App\Policies\PathPolicy;
use Illuminate\Support\Facades\Gate;
use N3XT0R\LaravelWebdavServer\DTO\Auth\PathResourceDto;
public function boot(): void
{
Gate::policy(PathResourceDto::class, PathPolicy::class);
}
namespace App\Policies;
use Illuminate\Contracts\Auth\Authenticatable;
use N3XT0R\LaravelWebdavServer\DTO\Auth\PathResourceDto;
final class PathPolicy
{
public function read(Authenticatable $user, PathResourceDto $resource): bool
{
return $this->isUserPath($user, $resource);
}
public function write(Authenticatable $user, PathResourceDto $resource): bool
{
return $this->isUserPath($user, $resource);
}
public function delete(Authenticatable $user, PathResourceDto $resource): bool
{
return $this->isUserPath($user, $resource);
}
public function createDirectory(Authenticatable $user, PathResourceDto $resource): bool
{
return $this->isUserPath($user, $resource);
}
public function createFile(Authenticatable $user, PathResourceDto $resource): bool
{
return $this->isUserPath($user, $resource);
}
private function isUserPath(Authenticatable $user, PathResourceDto $resource): bool
{
return str_starts_with($resource->path, 'webdav/'.$user->getAuthIdentifier().'/')
|| $resource->path === 'webdav/'.$user->getAuthIdentifier();
}
}
Custom Authorization Adapter¶
To replace Gate-based authorization entirely, implement PathAuthorizationInterface:
namespace App\Services;
use N3XT0R\LaravelWebdavServer\Contracts\Auth\PathAuthorizationInterface;
use N3XT0R\LaravelWebdavServer\ValueObjects\WebDavPrincipalValueObject;
use Sabre\DAV\Exception\Forbidden;
final class CustomPathAuthorization implements PathAuthorizationInterface
{
public function authorizeRead(WebDavPrincipalValueObject $principal, string $disk, string $path): void
{
if (! $this->canRead($principal, $disk, $path)) {
throw new Forbidden('Read access denied.');
}
}
public function authorizeWrite(WebDavPrincipalValueObject $principal, string $disk, string $path): void
{
if (! $this->canWrite($principal, $disk, $path)) {
throw new Forbidden('Write access denied.');
}
}
public function authorizeDelete(WebDavPrincipalValueObject $principal, string $disk, string $path): void
{
if (! $this->canDelete($principal, $disk, $path)) {
throw new Forbidden('Delete access denied.');
}
}
public function authorizeCreateDirectory(WebDavPrincipalValueObject $principal, string $disk, string $path): void
{
if (! $this->canCreateDirectory($principal, $disk, $path)) {
throw new Forbidden('Directory creation denied.');
}
}
public function authorizeCreateFile(WebDavPrincipalValueObject $principal, string $disk, string $path): void
{
if (! $this->canCreateFile($principal, $disk, $path)) {
throw new Forbidden('File creation denied.');
}
}
private function canRead(WebDavPrincipalValueObject $principal, string $disk, string $path): bool
{
return true;
}
private function canWrite(WebDavPrincipalValueObject $principal, string $disk, string $path): bool
{
return true;
}
private function canDelete(WebDavPrincipalValueObject $principal, string $disk, string $path): bool
{
return true;
}
private function canCreateDirectory(WebDavPrincipalValueObject $principal, string $disk, string $path): bool
{
return true;
}
private function canCreateFile(WebDavPrincipalValueObject $principal, string $disk, string $path): bool
{
return true;
}
}
Register it in your application:
use App\Services\CustomPathAuthorization;
use N3XT0R\LaravelWebdavServer\Contracts\Auth\PathAuthorizationInterface;
public function register(): void
{
$this->app->bind(PathAuthorizationInterface::class, CustomPathAuthorization::class);
}
Linked Laravel Users¶
If your account model links to a Laravel user, the resolved principal carries that user in $principal->user.
That is the user object Gate / policies receive by default.
If your policy logic depends on linked users, make sure:
webdav-server.auth.user_modelis configured- your configured account model exposes a
user()relationship
Related Runtime Exceptions¶
The authentication and authorization pipeline uses package-specific exceptions instead of generic runtime failures.
Common examples:
MissingCredentialsExceptionwhen no Basic Auth credentials can be extractedInvalidCredentialsExceptionwhen the credential validator rejects the supplied credentialsAccountNotFoundExceptionorAccountDisabledExceptionwhen the default account repository rejects the recordUnauthenticatedPrincipalExceptionwhen a principal is requested before successful authentication
Authorization denials remain mapped to Sabre\DAV\Exception\Forbidden because that is the SabreDAV boundary contract.