blog bg

October 01, 2023

Implementing OAuth2.0 with React and Laravel Socialite

Share what you learn in this blog to prepare for your interview, create your forever-free profile now, and explore how to monetize your valuable knowledge.

Introduction

This project takes inspiration from a YouTube tutorial by The Codeholic but expands upon it significantly, introducing numerous enhancements and innovative features. In this article, we will explore my implementation of OAuth authentication using React and PHP Laravel, a notable addition to this project. The original repository can be found here

OAuth is a widely adopted method for user authentication, commonly found in applications offering Google, Facebook, GitHub, and similar login or sign-up features. As developers, understanding how to seamlessly implement such a feature into your application is becoming increasingly essential.

By the end of this article, you will not only have learned how to set up Google and GitHub account integration for this feature but also gained a comprehensive understanding of the user control flow required to make it function effectively.

Background

OAuth serves as an authorization protocol that empowers applications to request data from another application on behalf of a user. This standard allows third-party applications to access resources from other applications without exposing sensitive information such as passwords. For instance, OAuth 2.0 enables you to log into services like your own application using your Google account credentials, eliminating the need to share your password.

In today's applications, OAuth plays a vital role by providing users with the ability to access services and applications without the necessity of sharing their passwords. Additionally, it simplifies the process for users who struggle to manage multiple passwords for various applications due to security concerns.

Prerequisites

To effectively follow this article, a basic understanding of the following technologies is required, as this article is not intended for beginners:

- React
- PHP Laravel
- MySQL

Familiarity with these technologies will greatly aid in comprehending the OAuth implementation discussed in this article.

Project Setup

Before diving into the OAuth implementation, make sure you have Node.js and Composer installed on your machine.

 

Backend (Laravel)

To set up the backend, you'll start by creating a Laravel project. Open your terminal and navigate to the desired directory for your project.

Run the following command to create a new Laravel project:

composer create-project laravel/laravel Oauth-implementation

Replace Oauth-implementation with your preferred project name.

This command will scaffold the Laravel project. The next step is to set up your user table and run the migration. In your Laravel project, you can find the user migration file (usually located at `database/migrations`) and modify it as follows:

// Add this code to your user migration file
public function up(): void
{
   Schema::create('users', function (Blueprint $table) {
       $table->id();
       $table->string('name');
       $table->string('email')->unique();
       $table->timestamp('email_verified_at')->nullable();
       $table->string('password')->nullable();
       $table->rememberToken();
       $table->timestamps();
   });
}

Next, run the migration to create the user table:

php artisan migrate

Now, it's time to set up your OAuth provider accounts. Your OAuth provider could be Google, GitHub, Facebook, or others, depending on your choice. Below are links to set up OAuth 2.0 for Google and GitHub:

Google: Google OAuth Setup Guide

GitHub: GitHub OAuth Setup Guide

Make sure to keep track of your redirect URL, client ID, and client secret. Ideally, these should be stored in your environment configuration file (env file). The redirect or callback URL is where the provider will redirect the user after authentication.

Next, let's set up Laravel Socialite, a package for OAuth authentication:

composer require laravel/socialite

Now, update your config/services.php file with the details from your chosen OAuth provider. For example, for Google and GitHub:

'google' => [
   'client_id' => env('GOOGLE_CLIENT_ID'),
   'client_secret' => env('GOOGLE_CLIENT_SECRET'),
   'redirect' => env('GOOGLE_REDIRECT_URI'),
],
'github' => [
   'client_id' => env('GITHUB_CLIENT_ID'),
   'client_secret' => env('GITHUB_CLIENT_SECRET'),
   'redirect' => env('GITHUB_REDIRECT_URI'),
]

With this basic setup completed, let's configure our routes to handle OAuth requests. You'll need two routes: the redirect route and the callback route.

Redirect Route:

Create a route like this in your Laravel application to dynamically handle multiple OAuth providers:

use Laravel\Socialite\Facades\Socialite;
use Illuminate\Http\Request;

Route::get('/auth/redirect', function (Request $request) {
   $provider = $request->query('provider');
   
   $redirect_url = Socialite::driver($provider)->stateless()->redirect()->getTargetUrl();
   
   return response(['provider' => $provider, 'redirect_url' => $redirect_url], 200)
           ->header('Content-Type', 'application/json')
           ->header('Access-Control-Allow-Origin', '*');
});

This route generates the redirect URL based on the provider passed in the query parameter and responds with this URL. It's designed to be flexible for use with multiple providers.

Callback Route:

Set up the callback route to handle user authentication and token generation:

Route::get('/auth/callback', function (Request $request) {

   $validated = $request->only([
       'provider', 'code'
   ]);
   
   $oauthUser = null;
   
   try {
       $oauthUser = Socialite::driver($validated['provider'])->stateless()->user();
   } catch (\Throwable $th) {
       throw new BadRequestException($th->getMessage());
   }
   
   $user = User::firstWhere('email', $oauthUser->email);
   
   if (!$user) {
       $credentials = [
           'name' => $oauthUser->name,
           'email' => $oauthUser->email,
       ];
       // Sign up user
       $user = $this->userRepository->createUser($credentials);
   } else {
       // Log in user
       Auth::login($user);
       $user = Auth::user();
   }
   
   $token = $user->createToken('access-token')->plainTextToken;
   
   $result = ['user' => new UserResource($user), 'token' => $token];
   
   return new JsonResponse($result);
});
```

With this setup, after Socialite authenticates the user, they will be signed up if their email doesn't exist in the database or logged in if it does exist. An access token is generated for the user, which can be used for subsequent API requests.

Now, let's move on to setting up the React application.

Frontend (React)

We will start by creating a new React app using Vite. Assuming you are using TypeScript, follow these steps:

npm create vite

This command will run the Vite executable from the remote npm repository. It will configure the necessary tools to scaffold a React local development environment. During this process, a command-line menu will open for project settings and language type. Complete your app setup as per your preferences.

Additionally, install react-router-dom:

npm install react-router-dom

In your src directory, create a router.tsx file and define your routes.

Set up the main.tsx file as well:

import { createBrowserRouter } from "react-router-dom";
import App from "./App";
import Redirect from "./Redirect";
const router = createBrowserRouter([
  {
     path: "/",
     element: <App />,
  },
  {
     path: "/auth/redirect",
     element: <Redirect />,
  },
  {
     path: "/home",
     element: <Home />,
  },
]);
export default router;
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";

import router from "./router";
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
 <React.StrictMode>
   <RouterProvider router={router} />
 </React.StrictMode>,
)

Ensure that the redirect route in your React app matches the route provided to the auth provider.

Install Axios for making API requests:

npm install axios

Now, set up the login/sign-up page, which is the starting point for OAuth requests:

import axios from "axios";
axios.defaults.baseURL = "http://localhost:8000";

function App() {
 async function OAuth(
   e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
   provider: string
 ) {
   e.preventDefault();
    try {
        const res = await axiosClient.get(
           `/auth/redirect?provider=${provider}`
        );
        window.location.href = res.data;
     } catch (error) {
        console.log(error);
     }
 }
 return (
     <>
     <button type="button"  onClick={(e) => OAuth(e, "google")}>
       OAuth Google Login
     </button>
     
     <button type="button"  onClick={(e) => OAuth(e, "github")}>
       OAuth Github Login
     </button>
   </>
 );
}
export default App;

This code makes a request to your implemented redirect API endpoint, which returns an authorization URL from the provider. Upon receiving this URL, the user is redirected to the provider's authentication page.

Lastly, set up the redirect page, which handles the callback after successful authentication:

import { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";

import Spinner from "../../assets/spinner.gif";

axios.defaults.baseURL = "http://localhost:8000";

const Redirect = () => {
  const [loading, setLoading] = useState(false);
  const { setToken, setUser } = useAppHook();
  const { provider } = useParams();
  const navigate = useNavigate();
  
  const searchParams = new URLSearchParams(document.location.search);
  
  useEffect(() => {
     let source = axios.CancelToken.source();
     
      /**
       * Code: Email verification token.
       * Provider: From Provider redirect link. It is hardcoded to token on the server side.
       */
     const data = { code: searchParams.get("code"), provider };
     
     if (data.code) {
        setLoading(true);
        const login = async () => {
           try {
              const res = await axios.post(
                 "/auth/callback",
                 data,
                 {
                    cancelToken: source.token,
                 }
              );
              setUser(res.data.user);
              setToken(res.data.token);
              navigate('/home');
           } catch (error) {
              if (axios.isCancel(error)) {
                 console.log("Request canceled");
              } else {
                 console.log(error);
                 navigate("/");
              }
           }
           setLoading(false);
        };
        login();
     }
     return () => {
        source.cancel();
     };
  }, []);
  const isLoading = (
     <div className="flex items-center justify-center">
        <img className="w-72 h-72" src={Spinner} alt="spinner" />
     </div>
  );
  
  const content = (
     <div className="flex items-center justify-center">
        <img className="w-72 h-72" src={Spinner} alt="spinner" />
     </div>
  );
  return loading ? isLoading : content;
};
export default Redirect;
```

With this setup, after Socialite authenticates the user, they will be signed up if their email doesn't exist in the database or logged in if it does exist. An access token is generated for the user, which can be used for subsequent API requests.

Now you have a comprehensive guide for implementing OAuth authentication using React and PHP Laravel. This project enables users to seamlessly log in using their Google or GitHub accounts without sharing their passwords, enhancing the security and user experience of your application.

290 views

Please Login to create a Question