Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

Selling digital products using Laravel part 2: Logging in using GitHub

Original – by Freek Van der Herten – 4 minute read

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.

This is how the login page at spatie.be looks like.

screenshot

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.

screenshot

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.

screenshot

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.

screenshot

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.

screenshot

This series is continued in part 3: Giving customers access to private repositories on Github.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "Selling digital products using Laravel part 2: Logging in using GitHub"?

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.