Selling digital products using Laravel part 2: Logging in using GitHub
Let's take a look at how people can log in to spatie.be in via GitHub. For this functionality, we use a wonderful Laravel first-party package called Socialite.
- Part 2: Logging in using GitHub (you are here)
This is how the login page at spatie.be looks like.
Socialite makes it easy to authenticate with OAuth providers. It supports GitHub, Google, Twitter, Facebook, and many others. We don't only use Socialite when logging in, but also to connect an existing spatie.be account to a GitHub account. Connecting your spatie.be account to GitHub can be done on the profile page.
This is what it looks like when an account is connected. When connecting an account to GitHub, we also check if the user is a sponsor.
Both the "Log in with GitHub" and "Connect To GitHub accounts" go to the same controller, GitHubSocialteController.
Let's take a look at the code of that GitHubSocialteController
. The redirect
method will redirect to the user to an authentication page on GitHub.
The callback
method will be called when a user has successfully authenticated at GitHub.
namespace App\Http\Controllers;
use App\Actions\RestoreRepositoryAccessAction;
use App\Models\User;
use App\Services\GitHub\GitHubGraphApi;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;
class GitHubSocialiteController
{
public function redirect()
{
session()->reflash();
if (auth()->check()) {
/*
* If somebody is already logged in, the user wants to
* connect their GitHub profile. Remember who's logged in.
*/
session()->put('auth-user-email', auth()->user()->email);
}
return Socialite::driver('github')->redirect();
}
public function callback()
{
$gitHubUser = Socialite::driver('github')->stateless()->user();
$isSponsor = (new GitHubGraphApi())->isSponsor($gitHubUser->nickname);
$user = $this->retrieveUser($gitHubUser);
$user->update([
'github_id' => $gitHubUser->id,
'github_username' => $gitHubUser->nickname,
'avatar' => $gitHubUser->avatar,
'is_sponsor' => $isSponsor,
]);
/*
* Make sure the user has access to all repositories that
* belong to past purchases.
*/
app(RestoreRepositoryAccessAction::class)->execute($user);
if (! $user->is_sponsor && ! $user->isSpatieMember()) {
/*
* Display a flash warning on the profile page that the user
* is not yet a sponsor.
*/
session()->flash('not-a-sponsor');
}
auth()->login($user, true);
flash()->success('You have been logged in');
return redirect()->to(session('next', route('products.index')));
}
protected function retrieveUser($gitHubUser): User
{
if (session('auth-user-email')) {
/*
* If there already was a local user created for the email used
* on GitHub, then let's use that local user
*/
return User::where('email', session('auth-user-email'))->first();
}
if ($user = User::where('github_id', $gitHubUser->id)->orWhere('email', $gitHubUser->email)->first()) {
/*
* Somebody tries to login via GitHub that already
* has been logged in, in the past.
*/
return $user;
}
/*
* Somebody tries to login via GitHub that doesn't have a local user
* yet. Let's create a new local user.
*/
return User::create([
'password' => bcrypt(Str::random()),
'email' => $gitHubUser->email,
'name' => $gitHubUser->name ?? $gitHubUser->nickname,
]);
}
}
After a user has successfully logged in (or connected their GitHub account), we have a local User
model in our database that we can log in.
You probably noticed that in the code we use the email address from the GitHub profile to retreive the local user. You might think than anybody can take over accounts by creating dummy GitHub accounts that have the same address as Spatie accounts. That isn't possible because GitHub doesn't allow authenticating when an email address isn't verified at their end.
This series is continued in part 3: Giving customers access to private repositories on Github.
What are your thoughts on "Selling digital products using Laravel part 2: Logging in using GitHub"?