• Время чтения ~7 мин
  • 15.01.2024

Этот пост был вдохновлен этим потрясающим постом на Medium и может рассматриваться как дополнительный пост, в котором вход в Google реализован в среде с отдельным приложением React и Laravel API.

1. Создание проекта

Google Создайте новый проект Google здесь: https://console.developers.google.com/projectcreate.

Creating new project - React Laravel API
Creating new project “React Laravel API”

После создания проекта перейдите к созданию нового идентификатора клиента OAuth 2.0.

Во-первых, убедитесь, что выбран правильный проект, а затем перейдите к настройке экрана согласия, нажав кнопку

Correct project selected and configure consent button
Correct project selected and configure consent button

«НАСТРОИТЬ ЭКРАН СОГЛАСИЯ».В этом примере мы настроим User Type как внешний.

Google OAuth consent screen configuration
OAuth consent screen configuration

После того, как вы заполните оставшуюся часть формы (вы можете просто заполнить обязательные поля, а остальные оставить по умолчанию), давайте наконец создадим OAuth-клиент.

Creating new OAuth client ID
Creating new OAuth client ID

Выберите "Веб-приложение" в качестве типа приложения и присвойте ему понятное имя.

Application type and Name
Application type and Name

Затем добавьте "Authorized JavasScript origins" и "Authorized redirect URIs". Поскольку мы будем создавать приложение Reactnpx create-react-app, и оно запускает приложение http://localhost:3000 по умолчанию, мы добавим его в качестве авторизованного источника. Наш URI перенаправления будет маршрутом, поэтому мы добавляем абсолютный URL-адрес этого маршрута /auth/google в «Авторизованные URI перенаправления».

JavasScript origin and Authorized redirect URI
JavasScript origin and Authorized redirect URI

После заполнения данных нажмите «Создать», и вы получите учетные данные в модальном окне. Запишите их или загрузите JSON, потому что они нам понадобятся в настройках приложения Laravel.

Client details
Client details

Ну вот. Приступим к созданию Laravel API.

2. Создание Laravel API

2.1. Установка и настройка Laravel и необходимых пакетов

Во-первых, давайте создадим новое приложение Laravel. На MacOS вы можете выполнить следующую команду, чтобы установить новое приложение Laravel в папку "laravel" с помощью Laravel Sail (mysql).

curl -s "https://laravel.build/laravel?with=mysql" | bash

Как указано в конце команды, выполните

cd laravel && ./vendor/bin/sail up

команду Если все прошло хорошо, ваше приложение должно быть запущено и запущено на http://localhost.

http://localhost - Fresh Laravel installation
http://localhost

Поскольку Laravel теперь поставляется с Laravel Sanctum по умолчанию, мы будем использовать его для аутентификации запросов API. Однако, прежде чем мы сможем выпустить токен API для пользователя, мы должны «Войти в систему» с помощью Google. Для этого мы будем использовать Laravel Socialite, поэтому давайте добавим его.

composer require laravel/socialite

Чтобы закончить настройку, нам нужно настроить config/services.php файл и добавить переменные env.

// config/services.php
return [
    ...
    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('GOOGLE_REDIRECT_URI'),
    ],
];

GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET you got in the last step of “Creating Google project” part. GOOGLE_REDIRECT_URI is the same as the one you configured in “Authorized Redirect URI” in Google project. This points to React App!!

// .env
GOOGLE_CLIENT_ID=783523056435-2901krcqpbe6a08q0gls6ifvha8lrd10.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=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
GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google

2.2. Настройка миграций и моделей

Теперь давайте настроим миграции по умолчанию. Обычно мы добавляем новую миграцию, но поскольку мы до сих пор не перенесли БД, мы можем просто изменить БД по умолчанию. В этом примере мы добавим 2 поля. Первое - обязательное полеgoogle_id, в котором будет храниться google id пользователя, а второе - необязательноеavatar. Вы можете проверить и сохранить любое другое поле, которое Google возвращает в соответствии с вашими предпочтениями.

// database/migrations/2014_10_12_000000_create_users_table.php
public function up()
{
    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(); // Changed to nullable()
        $table->string('google_id'); // Added
        $table->string('avatar')->nullable(); // Added
        $table->rememberToken();
        $table->timestamps();
    });
}

И теперь мы готовы перенести БД, чтобы вы могли выполнить следующую команду для запуска миграций из Sail.

 ./vendor/bin/sail artisan migrate

Кроме того, не забудьте отразить изменения в модели User.

// app/Models/User.php
protected $fillable = [
    'name',
    'email',
    'email_verified_at', // Fillable since we will manually add it on Sign on
    'password',
    'google_id', // Added
    'avatar', // Added
];

2.3. Добавление маршрутов и логики контроллера.

Во-первых, нам понадобятся 2 новых маршрута в routes/api.php.

// routes/api.php
use App\Http\Controllers\AuthController;
Route::get('auth', [AuthController::class, 'redirectToAuth']);
Route::get('auth/callback', [AuthController::class, 'handleAuthCallback']);

redirectToAuth method is straight forward. It just generates Google redirect url and returns it. Make sure you use stateless() since we are using this Laravel app as API and we are not keeping state at any time.

// app/Http/Controllers/AuthController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\JsonResponse;
use Laravel\Socialite\Contracts\User as SocialiteUser;
use Laravel\Socialite\Facades\Socialite;
class AuthController extends Controller
{
    public function redirectToAuth(): JsonResponse
    {
        return response()->json([
            'url' => Socialite::driver('google')
                         ->stateless()
                         ->redirect()
                         ->getTargetUrl(),
        ]);
    }
}

handleAuthCallback contains the logic to handle the callback. To keep it simple, we will just check if user is correctly authenticated, firstOrCreate the User and respond with the User and newly generated bearer token. Tokens are generated with Laravel Sanctum $user->createToken('google-token')->plainTextToken.

В обычном варианте использования, вероятно, будет либо повторно использоваться маркер аутентификации, либо аннулировать предыдущие, но это зависит от вас, а не в рамках этого примера.

// app/Http/Controllers/AuthController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\JsonResponse;
use Laravel\Socialite\Contracts\User as SocialiteUser;
use Laravel\Socialite\Facades\Socialite;
class AuthController extends Controller
{
    public function handleAuthCallback(): JsonResponse
    {
        try {
            /** @var SocialiteUser $socialiteUser */
            $socialiteUser = Socialite::driver('google')->stateless()->user();
        } catch (ClientException $e) {
            return response()->json(['error' => 'Invalid credentials provided.'], 422);
        }
        /** @var User $user */
        $user = User::query()
            ->firstOrCreate(
                [
                    'email' => $socialiteUser->getEmail(),
                ],
                [
                    'email_verified_at' => now(),
                    'name' => $socialiteUser->getName(),
                    'google_id' => $socialiteUser->getId(),
                    'avatar' => $socialiteUser->getAvatar(),
                ]
            );
        return response()->json([
            'user' => $user,
            'access_token' => $user->createToken('google-token')->plainTextToken,
            'token_type' => 'Bearer',
        ]);
    }
}

Ну вот. Вы можете найти весь этот пример с тестами в этом репозитории GitHub.

3. Создание приложения React 3.1 Установка приложения

React Давайте сначала создадим новое приложение React с помощью .

npx create-react-app react-app
cd react-app
npm start

npxЕсли все прошло хорошо, то при посещении http://localhost:3000вы должны увидеть что-то подобное.

http://localhost:3000 - Fresh React installation
http://localhost:3000

3.2. Добавление react-router-dom и настройка маршрутов

Для простоты также добавим react-router-dom.

npm install --save react-router-dom

Теперь мы можем настроить наши маршруты в App.js файле.

// src/App.js
import './App.css';
import {Route, BrowserRouter, Routes} from "react-router-dom";
import SignIn from "./SignIn";
import GoogleCallback from "./GoogleCallback";
function App() {
  return (
      <BrowserRouter>
          <Routes>
              <Route path="/" element={<SignIn />}></Route>
              <Route path="/auth/google" element={<GoogleCallback />}></Route>
          </Routes>
      </BrowserRouter>
  );
}
export default App;

3.3. Получение URL-адреса редиректа Google и редиректа на форму

SignIn component is simple. On load, we will fetch Google redirect url from Laravel API and set it as href for our link.

// src/SignIn.js
import React, {useState, useEffect} from 'react';
function SignIn() {
    const [loginUrl, setLoginUrl] = useState(null);
    useEffect(() => {
        fetch('http://localhost:80/api/auth', {
            headers : {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        })
            .then((response) => {
                if (response.ok) {
                    return response.json();
                }
                throw new Error('Something went wrong!');
            })
            .then((data) => setLoginUrl( data.url ))
            .catch((error) => console.error(error));
    }, []);
    return (
        <div>
            {loginUrl != null && (
                <a href={loginUrl}>Google Sign In</a>
            )}
        </div>
    );
}
export default SignIn;

регистрации Когда пользователь нажимает на ссылку "Google Sign In", страница будет перенаправлена на форму аутентификации Google.

Google Sign In form
Google Sign In form

После успешной аутентификации с помощью учетной записи Google Google перенаправит обратно на URL-адрес, который мы настроили в приложении Laravel .env GOOGLE_REDIRECT_URI с некоторыми дополнительными данными в search параметрах.

3.4. Обработка обратного вызова и аутентификация пользовательских запросов

Во втором компоненте GoogleCallback, мы возьмем эти search параметры и "проксируем" их в Laravel API. Если все пойдет хорошо, Laravel ответит только что созданным/полученным User и новым токеном авторизации Bearer, который мы можем использовать для выполнения аутентифицированных вызовов защищенных маршрутов Laravel API.

// src/GoogleCallback.js
import React, {useState, useEffect} from 'react';
import {useLocation} from "react-router-dom";
function GoogleCallback() {
    const [loading, setLoading] = useState(true);
    const [data, setData] = useState({});
    const [user, setUser] = useState(null);
    const location = useLocation();
    // On page load, we take "search" parameters 
    // and proxy them to /api/auth/callback on our Laravel API
    useEffect(() => {
        fetch(`http://localhost:80/api/auth/callback${location.search}`, {
            headers : {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        })
            .then((response) => {
                return response.json();
            })
            .then((data) => {
                setLoading(false);
                setData(data);
            });
    }, []);
    // Helper method to fetch User data for authenticated user
    // Watch out for "Authorization" header that is added to this call
    function fetchUserData() {
        fetch(`http://localhost:80/api/user`, {
            headers : {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'Authorization': 'Bearer ' + data.access_token,
            }
        })
            .then((response) => {
                return response.json();
            })
            .then((data) => {
                setUser(data);
            });
    }
    if (loading) {
        return <DisplayLoading/>
    } else {
        if (user != null) {
            return <DisplayData data={user}/>
        } else {
            return (
                <div>
                    <DisplayData data={data}/>
                    <div style={{marginTop:10}}>
                        <button onClick={fetchUserData}>Fetch User</button>
                    </div>
                </div>
            );
        }
    }
}
function DisplayLoading() {
    return <div>Loading....</div>;
}
function DisplayData(data) {
    return (
        <div>
            <samp>{JSON.stringify(data, null, 2)}</samp>
        </div>
    );
}
export default GoogleCallback;

Ну вот! Теперь у вас есть полностью функционирующий вход в Google с помощью React и Laravel API.

Вы можете найти весь проект в этом репозитории GitHub.

Внешние ресурсы

  1. Laravel Installation docs – https://laravel.com/docs/9.x/installation#choosing-your-sail-services
  2. Laravel Socialite – https://laravel.com/docs/9.x/socialite
  3. Laravel Sanctum, issuing API tokens – https://laravel.com/docs/9.x/sanctum#issuing-api-tokens
  4. React, installation – https://create-react-app.dev/docs/getting-started
  5. React, adding router – https://create-react-app.dev/docs/adding-a-router

Вам нужна помощь с настройкой входа в Google с помощью React и Laravel API?
По любым вопросам, связанным с разработкой на Laravel, присылайте нам свой запрос на [email protected]!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297