-
Notifications
You must be signed in to change notification settings - Fork 314
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implemented webhooks * oAuth wip * Implement the whole auth flow * Implement file upload limit depending on appsumo license
- Loading branch information
Showing
19 changed files
with
612 additions
and
103 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.'); | ||
} | ||
} | ||
} |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
|
||
namespace App\Models; | ||
|
||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
class License extends Model | ||
{ | ||
const STATUS_ACTIVE = 'active'; | ||
const STATUS_INACTIVE = 'inactive'; | ||
|
||
use HasFactory; | ||
|
||
protected $fillable = [ | ||
'license_key', | ||
'user_id', | ||
'license_provider', | ||
'status', | ||
'meta' | ||
]; | ||
|
||
protected $casts = [ | ||
'meta' => 'array', | ||
]; | ||
|
||
public function user() | ||
{ | ||
return $this->belongsTo(User::class); | ||
} | ||
|
||
public function scopeActive($query) | ||
{ | ||
return $query->where('status', self::STATUS_ACTIVE); | ||
} | ||
|
||
public function getMaxFileSizeAttribute() | ||
{ | ||
return [ | ||
1 => 25000000, // 25 MB, | ||
2 => 50000000, // 50 MB, | ||
3 => 75000000, // 75 MB, | ||
][$this->meta['tier']]; | ||
} | ||
} |
Oops, something went wrong.