Last active
January 30, 2025 16:11
-
-
Save ManukMinasyan/962eece79d07cc7987b29b19f41b49f9 to your computer and use it in GitHub Desktop.
Composer Satis Authentication with Laravel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Providers; | |
| use App\Actions\LicenseActivate; | |
| use App\Contracts\LicenseActivates; | |
| use App\Models\License; | |
| use Illuminate\Database\Eloquent\Model; | |
| use Illuminate\Http\Request; | |
| use Illuminate\Support\Facades\Auth; | |
| use Illuminate\Support\ServiceProvider; | |
| class AppServiceProvider extends ServiceProvider | |
| { | |
| public array $singletons = [ | |
| LicenseActivates::class => LicenseActivate::class, | |
| ]; | |
| /** | |
| * Register any application services. | |
| */ | |
| public function register(): void | |
| { | |
| // | |
| } | |
| /** | |
| * Bootstrap any application services. | |
| */ | |
| public function boot(): void | |
| { | |
| Model::unguard(); | |
| Auth::viaRequest('license-key', function (Request $request) { | |
| $license = License::query() | |
| ->where('key', $request->getPassword()) | |
| ->first(); | |
| if (! $license) { | |
| abort(401, 'License key invalid'); | |
| } | |
| if ($license->isExpired()) { | |
| abort(401, 'This license is expired'); | |
| } | |
| return $license; | |
| }); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Http\Controllers; | |
| use Illuminate\Database\Eloquent\Builder; | |
| use Illuminate\Http\Request; | |
| use Illuminate\Routing\Controller; | |
| class SatisAuthenticationController extends Controller | |
| { | |
| public function __invoke(License $license, Request $request) | |
| { | |
| /* | |
| * Find the package that is being requested. | |
| */ | |
| $package = $this->getRequestedPackage($request); | |
| /* | |
| * Composer can only store one authentication method per repository. | |
| * This means the user is probably going to try to authenticate with a license | |
| * key for the wrong package. We have to check the user's other licenses | |
| * as well. | |
| */ | |
| $license = License::query() | |
| ->where('key', $request->getPassword()) | |
| ->whereHas('product', function (Builder $query) use ($package) { | |
| $query->where('composer_package', $package); | |
| }); | |
| abort_unless($license->exists(), 401); | |
| // Increase the download count for the package | |
| StatsWriter::for( | |
| subject: PackageStatsEvent::class, | |
| attributes: [ | |
| 'product_id' => $license->first()->product_id, | |
| 'ip' => $request->ip(), | |
| ] | |
| ) | |
| ->increase(); | |
| ActivateLicenseKeyJob::dispatchAfterResponse( | |
| $license->first(), | |
| $this->getRequestedDomain($request) | |
| ); | |
| return response('valid'); | |
| } | |
| protected function getRequestedPackage(Request $request): string | |
| { | |
| $originalUrl = $request->header('X-Original-URI', ''); | |
| preg_match('#^/dist/(?<package>relaticle/[^/]*)/#', $originalUrl, $matches); | |
| if (!key_exists('package', $matches)) { | |
| abort(401, 'Missing X-Original-URI header'); | |
| } | |
| return $matches['package']; | |
| } | |
| protected function getRequestedDomain(Request $request): string | |
| { | |
| $password = $request->getPassword(); | |
| $parts = explode(':', $password); | |
| if (count($parts) !== 2) { | |
| abort(402, 'Invalid password format'); | |
| } | |
| return $parts[1]; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment