Этот пост был вдохновлен этим потрясающим постом на Medium и может рассматриваться как дополнительный пост, в котором вход в Google реализован в среде с отдельным приложением React и Laravel API.
1. Создание проекта
Google Создайте новый проект Google здесь: https://console.developers.google.com/projectcreate.
После создания проекта перейдите к созданию нового идентификатора клиента OAuth 2.0.
Во-первых, убедитесь, что выбран правильный проект, а затем перейдите к настройке экрана согласия, нажав кнопку
«НАСТРОИТЬ ЭКРАН СОГЛАСИЯ».В этом примере мы настроим User Type как внешний.
После того, как вы заполните оставшуюся часть формы (вы можете просто заполнить обязательные поля, а остальные оставить по умолчанию), давайте наконец создадим OAuth-клиент.
Выберите "Веб-приложение" в качестве типа приложения и присвойте ему понятное имя.
Затем добавьте "Authorized JavasScript origins" и "Authorized redirect URIs". Поскольку мы будем создавать приложение Reactnpx create-react-app
, и оно запускает приложение http://localhost:3000
по умолчанию, мы добавим его в качестве авторизованного источника. Наш URI перенаправления будет маршрутом, поэтому мы добавляем абсолютный URL-адрес этого маршрута /auth/google
в «Авторизованные URI перенаправления».
После заполнения данных нажмите «Создать», и вы получите учетные данные в модальном окне. Запишите их или загрузите JSON, потому что они нам понадобятся в настройках приложения Laravel.
Ну вот. Приступим к созданию 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
.
Поскольку 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
вы должны увидеть что-то подобное.
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 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.
Внешние ресурсы
- Laravel Installation docs – https://laravel.com/docs/9.x/installation#choosing-your-sail-services
- Laravel Socialite – https://laravel.com/docs/9.x/socialite
- Laravel Sanctum, issuing API tokens – https://laravel.com/docs/9.x/sanctum#issuing-api-tokens
- React, installation – https://create-react-app.dev/docs/getting-started
- React, adding router – https://create-react-app.dev/docs/adding-a-router
Вам нужна помощь с настройкой входа в Google с помощью React и Laravel API?
По любым вопросам, связанным с разработкой на Laravel, присылайте нам свой запрос на [email protected]!