* Implemented webhooks

* oAuth wip

* Implement the whole auth flow

* Implement file upload limit depending on appsumo license
This commit is contained in:
Julien Nahum
2023-11-01 16:58:10 +01:00
committed by GitHub
parent 2e52518aa7
commit e9174238e4
19 changed files with 611 additions and 102 deletions

View File

@@ -0,0 +1,117 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\License;
use App\Models\User;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
class AppSumoAuthController extends Controller
{
use AuthenticatesUsers;
public function handleCallback(Request $request)
{
$this->validate($request, [
'code' => 'required',
]);
$accessToken = $this->retrieveAccessToken($request->code);
$license = $this->fetchOrCreateLicense($accessToken);
// If user connected, attach license
if (Auth::check()) return $this->attachLicense($license);
// otherwise start login flow by passing the encrypted license key id
if (is_null($license->user_id)) {
return redirect(url('/register?appsumo_license='.encrypt($license->id)));
}
return redirect(url('/register?appsumo_error=1'));
}
private function retrieveAccessToken(string $requestCode): string
{
return Http::withHeaders([
'Content-type' => 'application/json'
])->post('https://appsumo.com/openid/token/', [
'grant_type' => 'authorization_code',
'code' => $requestCode,
'redirect_uri' => route('appsumo.callback'),
'client_id' => config('services.appsumo.client_id'),
'client_secret' => config('services.appsumo.client_secret'),
])->throw()->json('access_token');
}
private function fetchOrCreateLicense(string $accessToken): License
{
// Fetch license from API
$licenseKey = Http::get('https://appsumo.com/openid/license_key/?access_token=' . $accessToken)
->throw()
->json('license_key');
// Fetch or create license model
$license = License::where('license_provider','appsumo')->where('license_key',$licenseKey)->first();
if (!$license) {
$licenseData = Http::withHeaders([
'X-AppSumo-Licensing-Key' => config('services.appsumo.api_key'),
])->get('https://api.licensing.appsumo.com/v2/licenses/'.$licenseKey)->json();
// Create new license
$license = License::create([
'license_key' => $licenseKey,
'license_provider' => 'appsumo',
'status' => $licenseData['status'] === 'active' ? License::STATUS_ACTIVE : License::STATUS_INACTIVE,
'meta' => $licenseData,
]);
}
return $license;
}
private function attachLicense(License $license) {
if (!Auth::check()) {
throw new AuthenticationException('User not authenticated');
}
// Attach license if not already attached
if (is_null($license->user_id)) {
$license->user_id = Auth::id();
$license->save();
return redirect(url('/home?appsumo_connect=1'));
}
// Licensed already attached
return redirect(url('/home?appsumo_error=1'));
}
/**
* @param User $user
* @param string|null $licenseHash
* @return string|null
*
* Returns null if no license found
* Returns true if license was found and attached
* Returns false if there was an error (license not found or already attached)
*/
public static function registerWithLicense(User $user, ?string $licenseHash): ?bool
{
if (!$licenseHash) {
return null;
}
$licenseId = decrypt($licenseHash);
$license = License::find($licenseId);
if ($license && is_null($license->user_id)) {
$license->user_id = $user->id;
$license->save();
return true;
}
return false;
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use App\Models\Workspace;
use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
@@ -15,6 +16,8 @@ class RegisterController extends Controller
{
use RegistersUsers;
private ?bool $appsumoLicense = null;
/**
* Create a new controller instance.
*
@@ -28,8 +31,8 @@ class RegisterController extends Controller
/**
* The user has been registered.
*
* @param \Illuminate\Http\Request $request
* @param \App\User $user
* @param \Illuminate\Http\Request $request
* @param \App\User $user
* @return \Illuminate\Http\JsonResponse
*/
protected function registered(Request $request, User $user)
@@ -38,13 +41,17 @@ class RegisterController extends Controller
return response()->json(['status' => trans('verification.sent')]);
}
return response()->json($user);
return response()->json(array_merge(
(new UserResource($user))->toArray($request),
[
'appsumo_license' => $this->appsumoLicense,
]));
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
@@ -54,8 +61,9 @@ class RegisterController extends Controller
'email' => 'required|email:filter|max:255|unique:users|indisposable',
'password' => 'required|min:6|confirmed',
'hear_about_us' => 'required|string',
'agree_terms' => ['required',Rule::in([true])]
],[
'agree_terms' => ['required', Rule::in([true])],
'appsumo_license' => ['nullable'],
], [
'agree_terms' => 'Please agree with the terms and conditions.'
]);
}
@@ -63,7 +71,7 @@ class RegisterController extends Controller
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @param array $data
* @return \App\User
*/
protected function create(array $data)
@@ -87,6 +95,8 @@ class RegisterController extends Controller
]
], false);
$this->appsumoLicense = AppSumoAuthController::registerWithLicense($user, $data['appsumo_license'] ?? null);
return $user;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers\Webhook;
use App\Http\Controllers\Controller;
use App\Models\License;
use Illuminate\Http\Request;
use Illuminate\Validation\UnauthorizedException;
class AppSumoController extends Controller
{
public function handle(Request $request)
{
$this->validateSignature($request);
if ($request->test) {
return $this->success([
'message' => 'Webhook received.',
'event' => $request->event,
'success' => true,
]);
}
// Call the right function depending on the event using match()
match ($request->event) {
'activate' => $this->handleActivateEvent($request),
'upgrade', 'downgrade' => $this->handleChangeEvent($request),
'deactivate' => $this->handleDeactivateEvent($request),
default => null,
};
return $this->success([
'message' => 'Webhook received.',
'event' => $request->event,
'success' => true,
]);
}
private function handleActivateEvent($request)
{
$licence = License::firstOrNew([
'license_key' => $request->license_key,
'license_provider' => 'appsumo',
'status' => License::STATUS_ACTIVE,
]);
$licence->meta = $request->json()->all();
$licence->save();
}
private function handleChangeEvent($request)
{
// Deactivate old license
$oldLicense = License::where([
'license_key' => $request->prev_license_key,
'license_provider' => 'appsumo',
])->firstOrFail();
$oldLicense->update([
'status' => License::STATUS_INACTIVE,
]);
// Create new license
License::create([
'license_key' => $request->license_key,
'license_provider' => 'appsumo',
'status' => License::STATUS_ACTIVE,
'meta' => $request->json()->all(),
]);
}
private function handleDeactivateEvent($request)
{
// Deactivate old license
$oldLicense = License::where([
'license_key' => $request->prev_license_key,
'license_provider' => 'appsumo',
])->firstOrFail();
$oldLicense->update([
'status' => License::STATUS_INACTIVE,
]);
}
private function validateSignature(Request $request)
{
$signature = $request->header('x-appsumo-signature');
$payload = $request->getContent();
if ($signature === hash_hmac('sha256', $payload, config('services.appsumo.api_key'))) {
throw new UnauthorizedException('Invalid signature.');
}
}
}