A Laravel filesystem driver for SharePoint and OneDrive using Microsoft Graph API with client credentials authentication. No manual OAuth flows required - perfect for server-side applications and automated backups.
- β Zero-configuration OAuth - Automatic token management with client credentials flow
- β SharePoint Document Libraries - Direct access to your SharePoint sites
- β OneDrive for Business - Full OneDrive integration
- β Automatic Token Refresh - Handles token expiry seamlessly with smart caching
- β Laravel 10, 11, 12 - Compatible with modern Laravel versions
- β Flysystem v3 - Built on the latest Flysystem architecture
- β Large File Support - Optimized for files up to 250MB
- β Production Ready - Battle-tested in real-world applications
- β Spatie Backup Compatible - Perfect for automated backups to SharePoint
- PHP 8.1 or higher
- Laravel 10.x, 11.x, or 12.x
- Microsoft Azure app registration with appropriate permissions
Install via Composer:
composer require sahablibya/laravel-sharepoint-filesystemThe service provider will be automatically registered via Laravel's package discovery.
- Go to Azure Portal
- Navigate to Azure Active Directory β App registrations
- Click New registration
- Enter a name (e.g., "Laravel SharePoint Integration")
- Click Register
- Note your Application (client) ID and Directory (tenant) ID
- In your app registration, go to Certificates & secrets
- Click New client secret
- Add a description and set expiration
- Click Add
β οΈ Copy the secret value immediately (you won't see it again!)
- Go to API permissions
- Click Add a permission β Microsoft Graph β Application permissions
- Add these permissions:
Files.ReadWrite.All- Read and write files in all site collectionsSites.ReadWrite.All- Read and write items in all site collections
- Click Grant admin consent (requires admin privileges)
- Wait 2-5 minutes for permissions to propagate
To use a specific SharePoint document library, you need the drive ID:
- Go to Graph Explorer
- Sign in with your account
- Find your site:
GET https://graph.microsoft.com/v1.0/sites?search=YourSiteName - Get drives for that site:
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives - Copy the
idof your desired document library
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/yoursite"
Get-PnPList | Where-Object {$_.BaseTemplate -eq 101}Add these variables to your .env file:
GRAPH_CLIENT_ID=your-application-client-id
GRAPH_CLIENT_SECRET=your-client-secret-value
GRAPH_TENANT_ID=your-tenant-id
# Optional: Specify a SharePoint document library
SHAREPOINT_DRIVE_ID=your-drive-id
# Optional: Prefix path within the drive
SHAREPOINT_PREFIX=backupsAdd the SharePoint disk to your config/filesystems.php:
'disks' => [
// ... other disks
'sharepoint' => [
'driver' => 'sharepoint',
'client_id' => env('GRAPH_CLIENT_ID'),
'client_secret' => env('GRAPH_CLIENT_SECRET'),
'tenant_id' => env('GRAPH_TENANT_ID', 'common'),
'drive_id' => env('SHAREPOINT_DRIVE_ID'), // Optional
'prefix' => env('SHAREPOINT_PREFIX', ''), // Optional
'throw' => false,
],
],use Illuminate\Support\Facades\Storage;
// Write a file
Storage::disk('sharepoint')->put('documents/report.pdf', $contents);
// Write from a stream (memory efficient for large files)
$stream = fopen('/path/to/large-file.zip', 'r');
Storage::disk('sharepoint')->writeStream('backups/large-file.zip', $stream);
// Read a file
$contents = Storage::disk('sharepoint')->get('documents/report.pdf');
// Read as stream
$stream = Storage::disk('sharepoint')->readStream('documents/report.pdf');
// Check if file exists
if (Storage::disk('sharepoint')->exists('documents/report.pdf')) {
// File exists
}
// Delete a file
Storage::disk('sharepoint')->delete('documents/report.pdf');
// Delete multiple files
Storage::disk('sharepoint')->delete(['file1.pdf', 'file2.pdf']);
// Copy a file
Storage::disk('sharepoint')->copy('old.pdf', 'new.pdf');
// Move a file
Storage::disk('sharepoint')->move('old-location.pdf', 'new-location.pdf');// Create a directory
Storage::disk('sharepoint')->makeDirectory('documents/2024');
// List files in a directory
$files = Storage::disk('sharepoint')->files('documents');
// List all files recursively
$files = Storage::disk('sharepoint')->allFiles('documents');
// List directories
$directories = Storage::disk('sharepoint')->directories('documents');
// List all directories recursively
$directories = Storage::disk('sharepoint')->allDirectories('documents');
// Delete a directory
Storage::disk('sharepoint')->deleteDirectory('old-documents');// Get file size
$size = Storage::disk('sharepoint')->size('documents/report.pdf');
// Get last modified time
$timestamp = Storage::disk('sharepoint')->lastModified('documents/report.pdf');
// Get MIME type
$mimeType = Storage::disk('sharepoint')->mimeType('documents/report.pdf');// Store an uploaded file
$path = $request->file('document')->store('uploads', 'sharepoint');
// Download a file
return Storage::disk('sharepoint')->download('documents/report.pdf');
// Download with custom name
return Storage::disk('sharepoint')->download('documents/report.pdf', 'custom-name.pdf');Perfect integration with Spatie Laravel Backup:
// config/backup.php
'destination' => [
'disks' => [
'local',
'sharepoint', // Add SharePoint as backup destination
],
],Run backups:
# Full backup (database + files)
php artisan backup:run
# Database only
php artisan backup:run --only-db
# List backups
php artisan backup:list
# Clean old backups
php artisan backup:clean'disks' => [
'sharepoint-hr' => [
'driver' => 'sharepoint',
'client_id' => env('GRAPH_CLIENT_ID'),
'client_secret' => env('GRAPH_CLIENT_SECRET'),
'tenant_id' => env('GRAPH_TENANT_ID'),
'drive_id' => 'hr-drive-id',
'prefix' => 'employee-files',
],
'sharepoint-finance' => [
'driver' => 'sharepoint',
'client_id' => env('GRAPH_CLIENT_ID'),
'client_secret' => env('GRAPH_CLIENT_SECRET'),
'tenant_id' => env('GRAPH_TENANT_ID'),
'drive_id' => 'finance-drive-id',
'prefix' => 'reports',
],
],The package also supports OneDrive for Business. Use the onedrive driver:
'onedrive' => [
'driver' => 'onedrive',
'client_id' => env('GRAPH_CLIENT_ID'),
'client_secret' => env('GRAPH_CLIENT_SECRET'),
'tenant_id' => env('GRAPH_TENANT_ID'),
'prefix' => env('ONEDRIVE_PREFIX', ''),
],Access tokens are automatically cached for 58 minutes (tokens typically expire in 60 minutes). The cache key is unique per disk configuration, allowing multiple SharePoint/OneDrive connections with independent token management.
Error: "Access denied" or "403 Forbidden"
Solutions:
- Verify
Files.ReadWrite.AllandSites.ReadWrite.Allpermissions are added - Ensure admin consent is granted (look for green checkmarks in Azure Portal)
- Wait 2-5 minutes after granting consent for changes to propagate
- Clear Laravel cache:
php artisan cache:clear
Error: "Failed to obtain access token" or "invalid_client"
Solutions:
- Verify
GRAPH_CLIENT_IDmatches your app registration's Application ID - Verify
GRAPH_CLIENT_SECRETis correct (they expire!) - Check
GRAPH_TENANT_IDmatches your Directory (tenant) ID - Ensure no extra spaces in your
.envfile
Error: "itemNotFound" or "Resource not found"
Solutions:
- Verify
SHAREPOINT_DRIVE_IDis correct - Try removing
drive_idto use the default OneDrive - Ensure the app has access to the specified drive
- Check the drive exists and hasn't been deleted
Error: Timeouts when uploading large files
Solutions:
- The package automatically sets a 5-minute timeout for file operations
- For files > 250MB, consider using Microsoft's resumable upload API
- Check your PHP
max_execution_timeandmemory_limitsettings
If you're experiencing authentication issues:
php artisan cache:clearOr clear specific SharePoint tokens:
php artisan cache:forget sharepoint_access_token_*Test your SharePoint connection:
use Illuminate\Support\Facades\Storage;
Route::get('/test-sharepoint', function () {
try {
// Create a test file
$testContent = 'Test file created at ' . now();
Storage::disk('sharepoint')->put('test.txt', $testContent);
// Verify it exists
if (!Storage::disk('sharepoint')->exists('test.txt')) {
return 'File creation failed!';
}
// Read it back
$content = Storage::disk('sharepoint')->get('test.txt');
// Clean up
Storage::disk('sharepoint')->delete('test.txt');
return 'SharePoint connection successful! Content: ' . $content;
} catch (\Exception $e) {
return 'Connection failed: ' . $e->getMessage();
}
});- Never commit credentials - Keep
.envin.gitignore - Use environment-specific apps - Separate Azure apps for dev/staging/production
- Rotate secrets regularly - Set expiration dates on client secrets in Azure
- Monitor access logs - Review app activity in Azure Portal regularly
- Principle of least privilege - Only grant necessary permissions
- Secure your
.env- Restrict file permissions:chmod 600 .env
| Method | Supported | Notes |
|---|---|---|
write() |
β | Write file contents |
writeStream() |
β | Write from stream (memory efficient) |
read() |
β | Read file contents |
readStream() |
β | Read as stream |
delete() |
β | Delete file |
deleteDirectory() |
β | Delete directory and contents |
createDirectory() |
β | Create directory |
fileExists() |
β | Check if file exists |
directoryExists() |
β | Check if directory exists |
listContents() |
β | List directory contents |
move() |
β | Move/rename file |
copy() |
β | Copy file |
lastModified() |
β | Get last modified timestamp |
fileSize() |
β | Get file size |
mimeType() |
β | Get MIME type |
visibility() |
β | Not supported by SharePoint/OneDrive |
setVisibility() |
β | Not supported by SharePoint/OneDrive |
Contributions are welcome! Please see CONTRIBUTING.md for details.
# Clone the repository
git clone https://github.com/sahablibya/laravel-sharepoint-filesystem.git
cd laravel-sharepoint-filesystem
# Install dependencies
composer install
# Run tests
composer testPlease see CHANGELOG.md for recent changes.
This package is open-sourced software licensed under the MIT license.
- Developed by SahabLibya Development Team
- Built on Flysystem by Frank de Jonge
- Powered by Microsoft Graph API
Special thanks to:
- The Laravel community for inspiration and best practices
- Microsoft for the comprehensive Graph API
- All contributors who help improve this package
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: dev@sahablibya.ly
Made with β€οΈ by SahabLibya Development Team