I’m developing a webpage with Laravel 8 and I have issues with fetching a patron details by id from Patreon API. Here is my use case.
I’ve added “Login with Patreon” option to my webpage, and it works well. When someone login with Patreon successfully, I store her/his Patreon id and set remember token to login the member automatically when she/he visits my page next time.
The first login process is fine. The problem occurs when my Patron visits my page next time. Because I want to check whether I received any payment before I let she/he see all content. That’s why I need to get my patron details from a middleware. To do that I tried:
- fetch_user() returns my account details instead of logged-in user.
- fetch_user() with the access token that returns from Patreon when someone login, returns unauthorized.
- fetch_member_details() doesn’t work with the id I passed, which is an integer like 5484646 because it requires a very long string like 55153fds-f45fd5sfs-fds42ds, I don’t know what it’s.
- fetch_page_of_members_from_campaign() and fetch_member_details() together to get the proper ID, but it takes ages to get data, which is unacceptable.
So, how can it be done?
Advertisement
Answer
I like to answer my question for those who need some help.
First of all, I use the official PHP package by Patreon
I’ve created a middleware to check if the user should be authorized again. In order to prevent the same process every single time, I set timeout to users table and check if it still has time to expire. If it does, no need to do anything. Of course, this is my use case, but without that explanation, some parts of the code can be nonsense to you.
// AppHttpMiddlewareAuthenticateMember.php public function handle(Request $request, Closure $next) { if (!Auth::check()) { return $next($request); } if (Carbon::parse(Auth::user()->timeout)->isFuture()) { return $next($request); } $this->refreshCredentials(); return $next($request); }
If “timeout” isn’t in the future, refreshCredentials
method will be called. This is a method, which will trigger binding AuthGatewayContract to the service container.
// AppTraitUsers.php public function refreshCredentials() { $gateway = App::make('AppServicesAuthGatewaysContract'); $gateway->ensureUserStillAuthenticated(); } public function handleUserRecord($user) { return User::updateOrCreate([ 'email' => $user['email'] ], $user); } public function attemptToLogin($user, $remember = true) { Auth::login($user, $remember); event(new Registered($user)); }
This is how the binding works:
// AppProvidersAppServiceProvider.php public function register() { $this->app->singleton(AuthGatewaysContract::class, function () { $routeParts = explode('/', url()->current()); $gateway = array_pop($routeParts); // this is how I know which "Login with ..." button is clicked. $isGateway = Gateway::where('name', $gateway)->first(); $gateway = $isGateway ? ucfirst($gateway) : ucfirst(Auth::user()->gateway->name); $class = "AppServicesAuthGateways\$gateway"; return new $class(); }); }
So Patreon.php is active gateway now, and ensureUserStillAuthenticated
can be called:
// AppServicesAuthGatewaysPatreon.php public function ensureUserStillAuthenticated() { $this->authenticate([ 'access_token' => Auth::user()->access_token, 'refresh_token' => Auth::user()->refresh_token, ]); } private function authenticate($tokens) { $patron = $this->fetchUserFromGateway($tokens); $user = $this->handleResponseData($patron, $tokens); $user = $this->handleUserRecord($user); return $this->attemptToLogin($user); } private function fetchUserFromGateway($tokens) { // This is the only function that communicate with Patreon-php package. $api_client = new API($tokens['access_token']); return $api_client->fetch_user(); } private function handleResponseData($data, $tokens) { return [ 'name' => $data['data']['attributes']['full_name'], 'email' => $data['data']['attributes']['email'], 'password' => Hash::make(Str::random(24)), 'role_id' => $this->assignRoleId($data), 'payment_id' => Payment::where('name', 'patreon')->first()->id, 'gateway_id' => Gateway::where('name', 'patreon')->first()->id, 'access_token' => $tokens['access_token'], 'refresh_token' => $tokens['refresh_token'], 'timeout' => Carbon::today()->addMonth()->toDateString() ]; }